server.php 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. <?php
  2. require __DIR__ . '/vendor/autoload.php';
  3. use Amp\Cluster\Cluster;
  4. use Amp\Coroutine;
  5. use Amp\Http\Server\Request;
  6. use Amp\Http\Server\RequestHandler;
  7. use Amp\Http\Server\Response;
  8. use Amp\Http\Server\Router;
  9. use Amp\Http\Server\Options;
  10. use Amp\Http\Server\Server;
  11. use Amp\Promise;
  12. use Amp\Success;
  13. use Monolog\Logger;
  14. define('DB_HOST', gethostbyname('tfb-database'));
  15. define('CONCURRENCY_LIMIT', 102400);
  16. Amp\Loop::run(function () {
  17. $sockets = yield [
  18. Cluster::listen('0.0.0.0:8080',
  19. (new Amp\Socket\BindContext)->withBacklog(CONCURRENCY_LIMIT)
  20. ->withReusePort()
  21. ->withTcpNoDelay()
  22. )
  23. ];
  24. $router = new Router;
  25. $config = Amp\Mysql\ConnectionConfig::fromString(
  26. 'host='.DB_HOST.' user=benchmarkdbuser password=benchmarkdbpass db=hello_world'
  27. );
  28. $connector = new Amp\Mysql\CancellableConnector(
  29. new class implements Amp\Socket\Connector {
  30. public function connect(
  31. string $uri,
  32. ?Amp\Socket\ConnectContext $context = null,
  33. ?Amp\CancellationToken $token = null
  34. ): Amp\Promise {
  35. $context = $context ?? new Amp\Socket\ConnectContext;
  36. $context = $context->withTcpNoDelay();
  37. return Amp\Socket\connector()->connect($uri, $context, $token);
  38. }
  39. }
  40. );
  41. $mysql = new Amp\Mysql\Pool($config, 512, 300, $connector);
  42. // Case 1 - JSON
  43. $router->addRoute('GET', '/json', new class implements RequestHandler {
  44. public function handleRequest(Request $request): Promise {
  45. return new Success(new Response(200, [
  46. 'Content-Type' => 'application/json',
  47. 'Server' => 'amphp',
  48. ], \json_encode([
  49. 'message' => 'Hello, World!',
  50. ])));
  51. }
  52. });
  53. // Case 2 - Single Query
  54. $router->addRoute('GET', '/db', new class ($mysql) implements RequestHandler {
  55. private $mysql;
  56. public function __construct($mysql) {
  57. $this->mysql = $mysql;
  58. }
  59. public function handleRequest(Request $request): Promise {
  60. return new Coroutine($this->doHandleRequest($request));
  61. }
  62. private function doHandleRequest($request) {
  63. $statement = yield $this->mysql->prepare('SELECT * FROM World WHERE id = ?');
  64. $result = yield $statement->execute([mt_rand(1, 10000)]);
  65. yield $result->advance();
  66. return new Response(200, [
  67. 'Content-Type' => 'application/json',
  68. 'Server' => 'amphp',
  69. ], \json_encode($result->getCurrent()));
  70. }
  71. });
  72. // Case 3 - Multiple Queries
  73. $router->addRoute('GET', '/queries', new class ($mysql) implements RequestHandler {
  74. private $mysql;
  75. public function __construct($mysql) {
  76. $this->mysql = $mysql;
  77. }
  78. public function handleRequest(Request $request): Promise {
  79. return new Coroutine($this->doHandleRequest($request));
  80. }
  81. private function doHandleRequest($request) {
  82. $query = $request->getUri()->getQuery();
  83. \parse_str($query, $queryParams);
  84. $queries = (int) ($queryParams['q'] ?? 1);
  85. if ($queries < 1) {
  86. $queries = 1;
  87. } elseif ($queries > 500) {
  88. $queries = 500;
  89. }
  90. $items = [];
  91. $statement = yield $this->mysql->prepare('SELECT * FROM World WHERE id = ?');
  92. while ($queries--) {
  93. $items[] = new Coroutine($this->execute($statement));
  94. }
  95. return new Response(200, [
  96. 'Content-Type' => 'application/json',
  97. 'Server' => 'amphp',
  98. ], \json_encode(yield $items));
  99. }
  100. private function execute($statement) {
  101. $result = yield $statement->execute([mt_rand(1, 10000)]);
  102. yield $result->advance();
  103. return $result->getCurrent();
  104. }
  105. });
  106. // Case 4 - Fortunes
  107. $router->addRoute('GET', '/fortunes', new class ($mysql) implements RequestHandler {
  108. private $mysql;
  109. public function __construct($mysql) {
  110. $this->mysql = $mysql;
  111. }
  112. public function handleRequest(Request $request): Promise {
  113. return new Coroutine($this->doHandleRequest($request));
  114. }
  115. private function doHandleRequest($request) {
  116. $result = yield $this->mysql->query('SELECT * FROM Fortune');
  117. $items = [];
  118. while (yield $result->advance()) {
  119. $item = $result->getCurrent();
  120. $items[$item['id']] = $item['message'];
  121. }
  122. $items[0] = 'Additional fortune added at request time.';
  123. \asort($items);
  124. \ob_start();
  125. require __DIR__ . '/fortunes.php';
  126. return new Response(200, [
  127. 'Content-Type' => 'text/html; charset=utf-8',
  128. 'Server' => 'amphp',
  129. ], \ob_get_clean());
  130. }
  131. });
  132. // Case 6 - Plaintext
  133. $router->addRoute('GET', '/plaintext', new class implements RequestHandler {
  134. public function handleRequest(Request $request): Promise {
  135. return new Success(new Response(200, [
  136. 'Content-Type' => 'text/plain',
  137. 'Server' => 'amphp',
  138. ], 'Hello, World!'));
  139. }
  140. });
  141. $logger = new Logger('Worker-' . Cluster::getId());
  142. $logger->pushHandler(Cluster::createLogHandler());
  143. $logger->info('Using ' . get_class(Amp\Loop::get()));
  144. $options = (new Options)
  145. ->withoutCompression()
  146. ->withConnectionLimit(CONCURRENCY_LIMIT)
  147. ->withConnectionsPerIpLimit(CONCURRENCY_LIMIT);
  148. $server = new Server($sockets, $router, $logger, $options);
  149. yield $server->start();
  150. Amp\Loop::onSignal(\SIGINT, function (string $watcherId) use ($server) {
  151. Amp\Loop::cancel($watcherId);
  152. yield $server->stop();
  153. });
  154. });