server.dart 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. import 'dart:async';
  2. import 'dart:io';
  3. import 'dart:math';
  4. import 'package:aqueduct/aqueduct.dart';
  5. import 'package:mustache/mustache.dart' as mustache;
  6. export 'dart:async';
  7. export 'dart:io';
  8. export 'package:aqueduct/aqueduct.dart';
  9. final _stripped_text = new ContentType("text", "plain");
  10. final _stripped_json = new ContentType("application", "json");
  11. final _random = new Random();
  12. // World table size
  13. const int _world_table_size = 10000;
  14. // Fortune table size used only for generation of data
  15. const int _FORTUNE_TABLE_SIZE = 100;
  16. const int _min_query_count = 1;
  17. const int _max_query_count = 500;
  18. Future main() async {
  19. try {
  20. var app = new Application<DartAqueductBenchmarkSink>();
  21. var config = new ApplicationConfiguration()
  22. ..port = 8080
  23. ..configurationFilePath = "config.yaml";
  24. app.configuration = config;
  25. int optimalInstanceCount =
  26. (Platform.numberOfProcessors > 1 ? Platform.numberOfProcessors - 1 : 1);
  27. await app.start(
  28. numberOfInstances: optimalInstanceCount, consoleLogging: false);
  29. } catch (e, st) {
  30. await writeError("$e\n $st");
  31. }
  32. }
  33. Future writeError(String error) async {
  34. print("$error");
  35. }
  36. class Fortune extends ManagedObject<_Fortune>
  37. implements _Fortune, Comparable<Fortune> {
  38. compareTo(Fortune other) => message.compareTo(other.message);
  39. }
  40. class _Fortune {
  41. static String tableName() => "fortune";
  42. @ManagedColumnAttributes(primaryKey: true)
  43. int id;
  44. String message;
  45. }
  46. class World extends ManagedObject<_World> implements _World {}
  47. class _World {
  48. static String tableName() => "world";
  49. @ManagedColumnAttributes(primaryKey: true)
  50. int id;
  51. int randomNumber;
  52. }
  53. /// This type initializes an application.
  54. ///
  55. /// Override methods in this class to set up routes and initialize resources like
  56. /// database connections. See http://aqueduct.io/docs/http/request_sink.
  57. class DartAqueductBenchmarkSink extends RequestSink {
  58. mustache.Template fortunesTemplate;
  59. /// Resource initialization code goes here.
  60. ///
  61. /// Resources like [AuthServer] and [PostgreSQLPersistentStore] should be instantiated
  62. /// in this constructor. Configuration behavior - like [HTTPCodecRepository.add] - should be
  63. /// configured in this constructor.
  64. ///
  65. /// The [appConfig] contains configuration data from `aqueduct serve`, e.g.
  66. /// the port the application is running on and the path to a configuration file.
  67. DartAqueductBenchmarkSink(ApplicationConfiguration appConfig)
  68. : super(appConfig) {
  69. // Use logging for debuging only
  70. // logger.onRecord.listen(
  71. // (rec) => print("$rec ${rec.error ?? ""} ${rec.stackTrace ?? ""}"));
  72. var options =
  73. new DartAqueductBenchmarkConfiguration(appConfig.configurationFilePath);
  74. ManagedContext.defaultContext = contextWithConnectionInfo(options.database);
  75. }
  76. /// Final initialization method for this instance.
  77. ///
  78. /// This method allows any resources that require asynchronous initialization to complete their
  79. /// initialization process. This method is invoked after [setupRouter] and prior to this
  80. /// instance receiving any requests.
  81. @override
  82. Future willOpen() async {
  83. // Load the Mustache Template
  84. final template = await new File('fortunes.mustache').readAsString();
  85. fortunesTemplate = new mustache.Template(template);
  86. }
  87. /// All routes must be configured in this method.
  88. ///
  89. /// This method is invoked after the constructor and before [willOpen].
  90. /// All routes must be set up in this method and cannot be added after this method completes.
  91. @override
  92. void setupRouter(Router router) {
  93. router
  94. .route("/json")
  95. .listen((req) async => new Response.ok({"message": "Hello, World!"})
  96. ..contentType = _stripped_json
  97. ..headers["date"] = new DateTime.now());
  98. router
  99. .route("/plaintext")
  100. .listen((req) async => new Response.ok("Hello, World!")
  101. ..contentType = _stripped_text
  102. ..headers["date"] = new DateTime.now());
  103. router.route("/db").listen((req) async {
  104. World result = await getRandomWorldObject();
  105. return new Response.ok(result)
  106. ..contentType = _stripped_json
  107. ..headers["date"] = new DateTime.now();
  108. });
  109. router.route("/queries/[:queryCount]").listen((req) async {
  110. int queryCount = _min_query_count;
  111. if (req.path.variables.containsKey("queryCount")) {
  112. queryCount = int
  113. .parse(req.path.variables["queryCount"], onError: (_) => queryCount)
  114. .clamp(_min_query_count, _max_query_count);
  115. }
  116. List<Future> resultFutures =
  117. new List.generate(queryCount, (_) => getRandomWorldObject());
  118. List results = await Future.wait(resultFutures);
  119. return new Response.ok(results)
  120. ..contentType = _stripped_json
  121. ..headers["date"] = new DateTime.now();
  122. });
  123. router.route("/updates/[:queryCount]").listen((req) async {
  124. int queryCount = _min_query_count;
  125. if (req.path.variables.containsKey("queryCount")) {
  126. queryCount = int
  127. .parse(req.path.variables["queryCount"], onError: (_) => queryCount)
  128. .clamp(_min_query_count, _max_query_count);
  129. }
  130. List<Future> resultFutures = new List.generate(
  131. queryCount,
  132. (_) => getRandomWorldObject().then((World world) async {
  133. Query query = new Query<World>()
  134. ..where.id = whereEqualTo(world.id)
  135. ..values.randomNumber =
  136. (_random.nextInt(_world_table_size) + 1);
  137. return query.updateOne();
  138. }));
  139. List results = await Future.wait(resultFutures);
  140. return new Response.ok(results)
  141. ..contentType = _stripped_json
  142. ..headers["date"] = new DateTime.now();
  143. });
  144. router.route("/fortunes").listen((req) async {
  145. Query query = new Query<Fortune>();
  146. List<Fortune> results = await query.fetch();
  147. results.add(new Fortune()
  148. ..id = 0
  149. ..message = "Additional fortune added at request time.");
  150. results.sort();
  151. List resultsMapped = results
  152. .map((Fortune fortune) =>
  153. {'id': fortune.id, 'message': fortune.message})
  154. .toList();
  155. String renderedTemplate =
  156. fortunesTemplate.renderString({'fortunes': resultsMapped});
  157. return new Response.ok(renderedTemplate)
  158. ..contentType = ContentType.HTML
  159. ..headers["date"] = new DateTime.now();
  160. });
  161. }
  162. Future<World> getRandomWorldObject() async {
  163. int worldId = _random.nextInt(_world_table_size) + 1;
  164. Query query = new Query<World>()..where.id = worldId;
  165. return query.fetchOne();
  166. }
  167. /*
  168. * Helper methods
  169. */
  170. ManagedContext contextWithConnectionInfo(
  171. DatabaseConnectionConfiguration connectionInfo) {
  172. var dataModel = new ManagedDataModel.fromCurrentMirrorSystem();
  173. var psc = new PostgreSQLPersistentStore.fromConnectionInfo(
  174. connectionInfo.username,
  175. connectionInfo.password,
  176. connectionInfo.host,
  177. connectionInfo.port,
  178. connectionInfo.databaseName);
  179. return new ManagedContext(dataModel, psc);
  180. }
  181. }
  182. /// An instance of this class reads values from a configuration
  183. /// file specific to this application.
  184. ///
  185. /// Configuration files must have key-value for the properties in this class.
  186. /// For more documentation on configuration files, see https://aqueduct.io/docs/configure/ and
  187. /// https://pub.dartlang.org/packages/safe_config.
  188. class DartAqueductBenchmarkConfiguration extends ConfigurationItem {
  189. DartAqueductBenchmarkConfiguration(String fileName)
  190. : super.fromFile(fileName);
  191. DatabaseConnectionConfiguration database;
  192. }