swoole-server.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. <?php
  2. use Swoole\Http\Request;
  3. use Swoole\Http\Response;
  4. $server = new swoole_http_server('0.0.0.0', 8080, SWOOLE_BASE);
  5. $server->set([
  6. 'worker_num' => NUMCORES
  7. ]);
  8. $pool = new DatabasePool();
  9. /**
  10. * The DB test
  11. *
  12. * @param string $database_type
  13. * @param int $queries
  14. *
  15. * @return string
  16. */
  17. $db = function (string $database_type, int $queries = 0) use ($pool): string {
  18. $db = $pool->get($database_type);
  19. // Read number of queries to run from URL parameter
  20. $query_count = 1;
  21. if ($queries > 0) {
  22. $query_count = $queries > 500 ? 500 : $queries;
  23. }
  24. // Create an array with the response string.
  25. $arr = [];
  26. // Define query
  27. $db->db_test = $db->db_test ?? $db->prepare('SELECT randomNumber FROM World WHERE id = ?');
  28. // For each query, store the result set values in the response array
  29. while (0 < $query_count--) {
  30. $id = mt_rand(1, 10000);
  31. $ret = $db->db_test->execute([$id]);
  32. // Store result in array.
  33. $arr[] = ['id' => $id, 'randomNumber' => $ret[0]['randomNumber']];
  34. }
  35. // Use the PHP standard JSON encoder.
  36. // http://www.php.net/manual/en/function.json-encode.php
  37. if ($queries === -1) {
  38. $arr = $arr[0];
  39. }
  40. $pool->put($db);
  41. return json_encode($arr);
  42. };
  43. /**
  44. * The Fortunes test
  45. *
  46. * @param string $database_type
  47. *
  48. * @return string
  49. */
  50. $fortunes = function (string $database_type) use ($pool): string {
  51. $db = $pool->get($database_type);
  52. $fortune = [];
  53. $db->fortune_test = $db->fortune_test ?? $db->prepare('SELECT id, message FROM Fortune');
  54. $arr = $db->fortune_test->execute([]);
  55. foreach ($arr as $row) {
  56. $fortune[$row['id']] = $row['message'];
  57. }
  58. $fortune[0] = 'Additional fortune added at request time.';
  59. asort($fortune);
  60. $html = '<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>';
  61. foreach ($fortune as $id => $message) {
  62. $message = htmlspecialchars($message, ENT_QUOTES, 'UTF-8');
  63. $html .= "<tr><td>{$id}</td><td>{$message}</td></tr>";
  64. }
  65. $html .= '</table></body></html>';
  66. $pool->put($db);
  67. return $html;
  68. };
  69. /**
  70. * The Updates test
  71. *
  72. * @param string $database_type
  73. * @param int $queries
  74. *
  75. * @return string
  76. */
  77. $updates = function (string $database_type, int $queries = 0) use ($pool): string {
  78. $db = $pool->get($database_type);
  79. $query_count = 1;
  80. if ($queries > 0) {
  81. $query_count = $queries > 500 ? 500 : $queries;
  82. }
  83. $arr = [];
  84. $db->updates_test_select = $db->updates_test_select ?? $db->prepare('SELECT randomNumber FROM World WHERE id = ?');
  85. $db->updates_test_update = $db->updates_test_update ?? $db->prepare('UPDATE World SET randomNumber = ? WHERE id = ?');
  86. while (0 < $query_count--) {
  87. $id = mt_rand(1, 10000);
  88. $randomNumber = mt_rand(1, 10000);
  89. $ret = $db->updates_test_select->execute([$id]);
  90. // Store result in array.
  91. $world = ['id' => $id, 'randomNumber' => $ret[0]['randomNumber']];
  92. $world['randomNumber'] = $randomNumber;
  93. $db->updates_test_update->execute([$randomNumber, $id]);
  94. $arr[] = $world;
  95. }
  96. $pool->put($db);
  97. return json_encode($arr);
  98. };
  99. /**
  100. * On start of the PHP worker. One worker per server process is started.
  101. */
  102. $server->on('workerStart', function () use ($pool) {
  103. $pool->set_host_ip();
  104. });
  105. /**
  106. * On every request to the (web)server, execute the following code
  107. */
  108. $server->on('request', function (Request $req, Response $res) use ($db, $fortunes, $updates) {
  109. switch ($req->server['request_uri']) {
  110. case '/json':
  111. $res->header('Content-Type', 'application/json');
  112. $res->end(json_encode(['message' => 'Hello, World!']));
  113. break;
  114. case '/plaintext':
  115. $res->header('Content-Type', 'text/plain; charset=utf-8');
  116. $res->end('Hello, World!');
  117. break;
  118. case '/db':
  119. $res->header('Content-Type', 'application/json');
  120. if (isset($req->get['queries'])) {
  121. $res->end($db('mysql', (int)$req->get['queries']));
  122. } else {
  123. $res->end($db('mysql', -1));
  124. }
  125. break;
  126. case '/fortunes':
  127. $res->header('Content-Type', 'text/html; charset=utf-8');
  128. $res->end($fortunes('mysql'));
  129. break;
  130. case '/updates':
  131. $res->header('Content-Type', 'application/json');
  132. if (isset($req->get['queries'])) {
  133. $res->end($updates('mysql', (int)$req->get['queries']));
  134. } else {
  135. $res->end($updates('mysql', -1));
  136. }
  137. break;
  138. case '/db_postgres':
  139. $res->header('Content-Type', 'application/json');
  140. if (isset($req->get['queries'])) {
  141. $res->end($db('postgres', (int)$req->get['queries']));
  142. } else {
  143. $res->end($db('postgres', -1));
  144. }
  145. break;
  146. case '/fortunes_postgres':
  147. $res->header('Content-Type', 'text/html; charset=utf-8');
  148. $res->end($fortunes('postgres'));
  149. break;
  150. case '/updates_postgres':
  151. $res->header('Content-Type', 'application/json');
  152. if (isset($req->get['queries'])) {
  153. $res->end($updates('postgres', (int)$req->get['queries']));
  154. } else {
  155. $res->end($updates('postgres', -1));
  156. }
  157. break;
  158. }
  159. });
  160. $server->start();
  161. /**
  162. * Class DatabasePool
  163. *
  164. * Deal with the fact that Swoole 2.1.3 has no build in database pooling
  165. */
  166. class DatabasePool
  167. {
  168. private $server = [
  169. 'host' => '',
  170. 'user' => 'benchmarkdbuser',
  171. 'password' => 'benchmarkdbpass',
  172. 'database' => 'hello_world'
  173. ];
  174. private $pool;
  175. private $pool_count = 0;
  176. function __construct()
  177. {
  178. $this->pool = new \SplQueue;
  179. }
  180. function set_host_ip()
  181. {
  182. if (empty($this->server['host'])) {
  183. $tfb_database_ip = Swoole\Coroutine::gethostbyname('tfb-database');
  184. $this->server['host'] = $tfb_database_ip;
  185. }
  186. }
  187. function put($db)
  188. {
  189. $this->pool->enqueue($db);
  190. $this->pool_count++;
  191. }
  192. function get(string $server_type)
  193. {
  194. if ($this->pool_count > 0) {
  195. $this->pool_count--;
  196. return $this->pool->dequeue();
  197. }
  198. // No idle connection, time to create a new connection
  199. if ($server_type === 'mysql') {
  200. $db = new Swoole\Coroutine\Mysql;
  201. }
  202. if ($server_type === 'postgres') {
  203. $db = new Swoole\Coroutine\PostgreSql;
  204. }
  205. $db->connect($this->server);
  206. if ($db == false) {
  207. return false;
  208. }
  209. return $db;
  210. }
  211. }