server.dart 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. import "dart:core";
  2. import "dart:io";
  3. import "dart:isolate";
  4. import 'dart:async' show Future;
  5. import 'dart:math' show Random, max;
  6. import "package:redstone/server.dart" as app;
  7. import "package:redstone_mapper/mapper.dart";
  8. import "package:redstone_mapper/plugin.dart";
  9. import "package:redstone_mapper_mongo/manager.dart";
  10. import "package:redstone_mapper_pg/manager.dart";
  11. import "package:postgresql/postgresql.dart" as pg;
  12. import "package:di/di.dart";
  13. import 'package:yaml/yaml.dart' as yaml;
  14. import 'package:mustache/mustache.dart' as mustache;
  15. import 'package:system_info/system_info.dart';
  16. final _NUM_PROCESSORS = SysInfo.processors.length;
  17. const _WORLD_TABLE_SIZE = 10000;
  18. final _RANDOM = new Random();
  19. class Fortune implements Comparable<Fortune> {
  20. @Field()
  21. int id;
  22. @Field()
  23. String message;
  24. Fortune([this.id, this.message]);
  25. compareTo(Fortune other) => message.compareTo(other.message);
  26. }
  27. class World {
  28. @Field()
  29. int id;
  30. @Field(model: "randomnumber")
  31. int randomNumber;
  32. }
  33. class MongoFortune implements Comparable<MongoFortune> {
  34. int _id;
  35. @Field(model: "_id")
  36. int get id => _id;
  37. @Field(model: "_id")
  38. set id(num value) => _id = value.toInt();
  39. @Field()
  40. String message;
  41. MongoFortune([this._id, this.message]);
  42. compareTo(MongoFortune other) => message.compareTo(other.message);
  43. }
  44. class MongoWorld {
  45. int _id;
  46. int _randomNumber;
  47. @Field(model: "_id")
  48. int get id => _id;
  49. @Field(model: "_id")
  50. set id(num value) => _id = value.toInt();
  51. @Field()
  52. int get randomNumber => _randomNumber;
  53. @Field()
  54. set randomNumber(num value) => _randomNumber = value.toInt();
  55. }
  56. ///Handle PostgreSql connections
  57. @app.Interceptor(r'/pg/.+')
  58. pgSqlManager(PostgreSqlManager pgSql) {
  59. pgSql.getConnection().then((conn) {
  60. app.request.attributes["dbConn"] = conn;
  61. app.chain.next(() {
  62. pgSql.closeConnection(conn, error: app.chain.error);
  63. });
  64. });
  65. }
  66. ///Handle MongoDb connections
  67. @app.Interceptor(r'/mongo/.+')
  68. mongoDbManager(MongoDbManager mongoDb) {
  69. mongoDb.getConnection().then((conn) {
  70. app.request.attributes["dbConn"] = conn;
  71. app.chain.next(() {
  72. mongoDb.closeConnection(conn, error: app.chain.error);
  73. });
  74. });
  75. }
  76. ///JSON test
  77. @app.Route("/json")
  78. getJson() => {"message": "Hello, World!"};
  79. ///PlainText test
  80. @app.Route("/plaintext")
  81. getPlainText() => "Hello, World!";
  82. ///PostgreSql tests
  83. @app.Group("/pg")
  84. @Encode()
  85. class PgTests {
  86. static const worldQuery = 'SELECT id, randomnumber FROM world WHERE id = @id;';
  87. static const worldUpdt = 'UPDATE world SET randomnumber = @randomnumber WHERE id = @id;';
  88. static const fortuneQuery = 'SELECT id, message FROM fortune;';
  89. PostgreSql get pgSql => app.request.attributes["dbConn"];
  90. @app.Route("/db")
  91. Future<World> queryTest() {
  92. var params = { 'id': _RANDOM.nextInt(_WORLD_TABLE_SIZE) + 1 };
  93. return pgSql.query(worldQuery, World, params).then((list) => list[0]);
  94. }
  95. @app.Route("/queries")
  96. Future<List<World>> queriesTest() {
  97. var queries = _parseQueriesParam(app.request.queryParams.queries);
  98. return Future.wait(new List.generate(queries, (_) => queryTest()));
  99. }
  100. @app.Route("/updates")
  101. Future<List<World>> updateTest() {
  102. var queries = _parseQueriesParam(app.request.queryParams.queries);
  103. return Future.wait(new List.generate(queries, (_) => queryTest().then((world) {
  104. world.randomNumber = _RANDOM.nextInt(_WORLD_TABLE_SIZE) + 1;
  105. return pgSql.execute(worldUpdt, world).then((_) => world);
  106. })));
  107. }
  108. @app.Route("/fortunes", responseType: "text/html;charset=utf-8")
  109. Future<String> fortunesTest(@app.Inject() mustache.Template template) {
  110. return pgSql.query(fortuneQuery, Fortune).then((values) {
  111. values
  112. ..add(new Fortune(0, 'Additional fortune added at request time.'))
  113. ..sort();
  114. return template.renderString({
  115. "fortunes": encode(values)
  116. });
  117. });
  118. }
  119. }
  120. ///MongoDb tests
  121. @app.Group("/mongo")
  122. @Encode()
  123. class MongoTests {
  124. static const worldCollection = "world";
  125. static const fortuneCollection = "fortune";
  126. MongoDb get mongoDb => app.request.attributes["dbConn"];
  127. @app.Route("/db")
  128. Future<MongoWorld> queryTest() {
  129. return mongoDb.findOne(worldCollection, MongoWorld, {
  130. "_id": _RANDOM.nextInt(_WORLD_TABLE_SIZE) + 1
  131. });
  132. }
  133. @app.Route("/queries")
  134. Future<List<MongoWorld>> queriesTest() {
  135. var queries = _parseQueriesParam(app.request.queryParams.queries);
  136. return Future.wait(new List.generate(queries, (_) => queryTest()));
  137. }
  138. @app.Route("/updates")
  139. Future<List<MongoWorld>> updateTest() {
  140. var queries = _parseQueriesParam(app.request.queryParams.queries);
  141. return Future.wait(new List.generate(queries, (_) => queryTest().then((world) {
  142. world.randomNumber = _RANDOM.nextInt(_WORLD_TABLE_SIZE) + 1;
  143. return mongoDb.update(worldCollection, { "_id": world.id }, world)
  144. .then((_) => world);
  145. })));
  146. }
  147. @app.Route("/fortunes", responseType: "text/html;charset=utf-8")
  148. Future<String> fortunesTest(@app.Inject() mustache.Template template) {
  149. return mongoDb.find(fortuneCollection, MongoFortune).then((values) {
  150. values
  151. ..add(new MongoFortune(0, 'Additional fortune added at request time.'))
  152. ..sort();
  153. return template.renderString({
  154. "fortunes": encode(values)
  155. });
  156. });
  157. }
  158. }
  159. main(List<String> args) {
  160. ReceivePort errorPort = new ReceivePort();
  161. errorPort.listen((e) => print(e));
  162. for (int i = 0; i < _NUM_PROCESSORS; i++) {
  163. Isolate.spawn(
  164. startInIsolate,
  165. [],
  166. onError: errorPort.sendPort);
  167. }
  168. }
  169. startInIsolate(List args) {
  170. _startServer();
  171. }
  172. _startServer() {
  173. var dbConnections = max(1, (256 / _NUM_PROCESSORS).floor());
  174. var mongoDbManager = new MongoDbManager(
  175. "mongodb://tfb-database/hello_world",
  176. poolSize: dbConnections);
  177. var pgSqlManager = new PostgreSqlManager(
  178. "postgres://benchmarkdbuser:benchmarkdbpass@tfb-database:5432/hello_world",
  179. min: dbConnections,
  180. max: dbConnections);
  181. mustache.Template fortunesTemplate;
  182. Future.wait([
  183. HttpServer.bind("0.0.0.0", 8080, shared: true),
  184. new File('fortunes.mustache').readAsString().then((template) {
  185. fortunesTemplate = mustache.parse(template);
  186. })
  187. ]).then((List waitResults) {
  188. var server = waitResults[0];
  189. //app.setupConsoleLog();
  190. //install module for dependency injection
  191. app.addModule(new Module()
  192. ..bind(MongoDbManager, toValue: mongoDbManager)
  193. ..bind(PostgreSqlManager, toValue: pgSqlManager)
  194. ..bind(mustache.Template, toValue: fortunesTemplate));
  195. //initialize mapper plugin
  196. app.addPlugin(getMapperPlugin());
  197. //start the server
  198. app.serveRequests(server);
  199. });
  200. }
  201. _parseQueriesParam(param) {
  202. return param == null || param.isEmpty ? 1 :
  203. int.parse(param, radix: 10, onError: (_) => 1).clamp(1, 500);
  204. }