server.php 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  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. Amp\Loop::run(function () {
  16. $sockets = yield [
  17. Cluster::listen('0.0.0.0:8080',
  18. (new Amp\Socket\BindContext)->withBacklog(102400)
  19. ->withReusePort()
  20. ->withTcpNoDelay()
  21. )
  22. ];
  23. $router = new Router;
  24. // Case 1 - JSON
  25. $router->addRoute('GET', '/json', new class implements RequestHandler {
  26. public function handleRequest(Request $request): Promise {
  27. return new Success(new Response(200, [
  28. 'Content-Type' => 'application/json',
  29. 'Server' => 'amphp/http-server',
  30. ], \json_encode([
  31. 'message' => 'Hello, World!',
  32. ])));
  33. }
  34. });
  35. // Case 2 - Single Query
  36. $router->addRoute('GET', '/db', new class implements RequestHandler {
  37. private $mysql;
  38. public function __construct() {
  39. $config = Amp\Mysql\ConnectionConfig::fromString(
  40. 'host='.DB_HOST.' user=benchmarkdbuser password=benchmarkdbpass db=hello_world'
  41. );
  42. $this->mysql = Amp\Mysql\pool($config);
  43. }
  44. public function handleRequest(Request $request): Promise {
  45. return new Coroutine($this->doHandleRequest($request));
  46. }
  47. private function doHandleRequest($request) {
  48. $statement = yield $this->mysql->prepare('SELECT * FROM World WHERE id = ?');
  49. $result = yield $statement->execute([mt_rand(1, 10000)]);
  50. if (yield $result->advance()) {
  51. $item = $result->getCurrent();
  52. } else {
  53. $item = null;
  54. }
  55. return new Response(200, [
  56. 'Content-Type' => 'application/json',
  57. 'Server' => 'amphp/http-server',
  58. ], \json_encode($item));
  59. }
  60. });
  61. // Case 3 - Multiple Queries
  62. $router->addRoute('GET', '/queries', new class implements RequestHandler {
  63. private $mysql;
  64. public function __construct() {
  65. $config = Amp\Mysql\ConnectionConfig::fromString(
  66. 'host='.DB_HOST.' user=benchmarkdbuser password=benchmarkdbpass db=hello_world'
  67. );
  68. $this->mysql = Amp\Mysql\pool($config);
  69. }
  70. public function handleRequest(Request $request): Promise {
  71. return new Coroutine($this->doHandleRequest($request));
  72. }
  73. private function doHandleRequest($request) {
  74. $query = $request->getUri()->getQuery();
  75. \parse_str($query, $queryParams);
  76. $queries = (int) ($queryParams['queries'] ?? 1);
  77. if ($queries < 1) {
  78. $queries = 1;
  79. } elseif ($queries > 500) {
  80. $queries = 500;
  81. }
  82. $items = [];
  83. $statement = yield $this->mysql->prepare('SELECT * FROM World WHERE id = ?');
  84. while ($queries--) {
  85. $items[] = new Coroutine($this->execute($statement));
  86. }
  87. return new Response(200, [
  88. 'Content-Type' => 'application/json',
  89. 'Server' => 'amphp/http-server',
  90. ], \json_encode(yield $items));
  91. }
  92. private function execute($statement) {
  93. $result = yield $statement->execute([mt_rand(1, 10000)]);
  94. yield $result->advance();
  95. return $result->getCurrent();
  96. }
  97. });
  98. // Case 4 - Fortunes
  99. $router->addRoute('GET', '/fortunes', new class implements RequestHandler {
  100. private $mysql;
  101. public function __construct() {
  102. $config = Amp\Mysql\ConnectionConfig::fromString(
  103. 'host='.DB_HOST.' user=benchmarkdbuser password=benchmarkdbpass db=hello_world'
  104. );
  105. $this->mysql = Amp\Mysql\pool($config);
  106. }
  107. public function handleRequest(Request $request): Promise {
  108. return new Coroutine($this->doHandleRequest($request));
  109. }
  110. private function doHandleRequest($request) {
  111. $result = yield $this->mysql->query('SELECT * FROM Fortune');
  112. $items = [];
  113. while (yield $result->advance()) {
  114. $item = $result->getCurrent();
  115. $items[$item['id']] = $item['message'];
  116. }
  117. $items[0] = 'Additional fortune added at request time.';
  118. \asort($items);
  119. \ob_start();
  120. require __DIR__ . '/fortunes.php';
  121. return new Response(200, [
  122. 'Content-Type' => 'text/html; charset=utf-8',
  123. 'Server' => 'amphp/http-server',
  124. ], \ob_get_clean());
  125. }
  126. private function execute($statement) {
  127. $result = yield $statement->execute([mt_rand(1, 10000)]);
  128. yield $result->advance();
  129. return $result->getCurrent();
  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/http-server',
  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(102400)
  147. ->withConnectionsPerIpLimit(102400);
  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. });