swoole-server.php 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  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' => swoole_cpu_num(),
  7. 'log_file' => '/dev/null',
  8. 'log_level' => 5,
  9. ]);
  10. $pool = new \DatabasePool('postgres');
  11. /**
  12. * On start of the PHP worker. One worker per server process is started.
  13. */
  14. $server->on('workerStart', function ($srv) use ($pool) {
  15. $pool->init(\intdiv(512, $srv->setting['worker_num']));
  16. });
  17. /**
  18. * The DB test
  19. *
  20. * @param string $database_type
  21. * @param int $queries
  22. *
  23. * @return string
  24. */
  25. $db_postgres = function (int $queries = 0) use ($pool): string {
  26. $db = $pool->get();
  27. // Read number of queries to run from URL parameter
  28. $query_count = 1;
  29. if ($queries > 1) {
  30. $query_count = $queries > 500 ? 500 : $queries;
  31. }
  32. // Create an array with the response string.
  33. $arr = [];
  34. $db->s ??= $db->prepare('s', 'SELECT id, randomnumber FROM World WHERE id = $1');
  35. // For each query, store the result set values in the response array
  36. while ($query_count--) {
  37. $id = mt_rand(1, 10000);
  38. $res = $db->execute('s', [$id]);
  39. $ret = $db->fetchAssoc($res);
  40. // Store result in array.
  41. $arr[] = ['id' => $id, 'randomnumber' => $ret['randomnumber']];
  42. }
  43. // Use the PHP standard JSON encoder.
  44. // http://www.php.net/manual/en/function.json-encode.php
  45. if ($queries === -1) {
  46. $arr = $arr[0];
  47. }
  48. $pool->put($db);
  49. return \json_encode($arr, JSON_NUMERIC_CHECK);
  50. };
  51. /**
  52. * The Fortunes test
  53. *
  54. * @param string $database_type
  55. *
  56. * @return string
  57. */
  58. $fortunes_postgres = function () use ($pool): string {
  59. $db = $pool->get();
  60. $fortune = [];
  61. $db->f ??= $db->prepare('f', 'SELECT id, message FROM Fortune');
  62. $res = $db->execute('f', []);
  63. $arr = $db->fetchAll($res);
  64. foreach ($arr as $row) {
  65. $fortune[$row['id']] = $row['message'];
  66. }
  67. $fortune[0] = 'Additional fortune added at request time.';
  68. \asort($fortune);
  69. $html = '';
  70. foreach ($fortune as $id => $message) {
  71. $message = \htmlspecialchars($message, ENT_QUOTES, 'UTF-8');
  72. $html .= "<tr><td>{$id}</td><td>{$message}</td></tr>";
  73. }
  74. $pool->put($db);
  75. return '<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>'
  76. .$html.
  77. '</table></body></html>';
  78. };
  79. /**
  80. * The Updates test
  81. *
  82. * @param string $database_type
  83. * @param int $queries
  84. *
  85. * @return string
  86. */
  87. $updates_postgres = function (int $queries = 0) use ($pool): string {
  88. $db = $pool->get();
  89. $query_count = 1;
  90. if ($queries > 1) {
  91. $query_count = $queries > 500 ? 500 : $queries;
  92. }
  93. $arr = [];
  94. $db->us ??= $db->prepare('us', 'SELECT id,randomnumber FROM World WHERE id = $1');
  95. $db->uu ??= $db->prepare('uu', 'UPDATE World SET randomnumber = $1 WHERE id = $2');
  96. while ($query_count--) {
  97. $id = \mt_rand(1, 10000);
  98. $randomNumber = \mt_rand(1, 10000);
  99. $res = $db->execute('us', [$id]);
  100. $ret = $db->fetchAssoc($res);
  101. // Store result in array.
  102. $world = ['id' => $id, 'randomnumber' => $ret['randomnumber']];
  103. $world['randomnumber'] = $randomNumber;
  104. $res = $db->execute('uu', [$randomNumber, $id]);
  105. $arr[] = $world;
  106. }
  107. $pool->put($db);
  108. return \json_encode($arr, JSON_NUMERIC_CHECK);
  109. };
  110. /**
  111. * The DB test
  112. *
  113. * @param string $database_type
  114. * @param int $queries
  115. *
  116. * @return string
  117. */
  118. $db_mysql = function (int $queries = 0) use ($pool): string {
  119. $db = $pool->get();
  120. // Read number of queries to run from URL parameter
  121. $query_count = 1;
  122. if ($queries > 1) {
  123. $query_count = $queries > 500 ? 500 : $queries;
  124. }
  125. // Create an array with the response string.
  126. $arr = [];
  127. // Define query
  128. $db->db_test ??= $db->prepare('SELECT id, randomNumber FROM World WHERE id = ?');
  129. // For each query, store the result set values in the response array
  130. while ($query_count--) {
  131. $id = \mt_rand(1, 10000);
  132. $ret = $db->db_test->execute([$id]);
  133. // Store result in array.
  134. $arr[] = ['id' => $id, 'randomNumber' => $ret[0]['randomNumber']];
  135. }
  136. // Use the PHP standard JSON encoder.
  137. // http://www.php.net/manual/en/function.json-encode.php
  138. if ($queries === -1) {
  139. $arr = $arr[0];
  140. }
  141. $pool->put($db);
  142. return \json_encode($arr, JSON_NUMERIC_CHECK);
  143. };
  144. /**
  145. * The Fortunes test
  146. *
  147. * @param string $database_type
  148. *
  149. * @return string
  150. */
  151. $fortunes_mysql = function () use ($pool): string {
  152. $db = $pool->get();
  153. $fortune = [];
  154. $db->fortune_test ??= $db->prepare('SELECT id, message FROM Fortune');
  155. $arr = $db->fortune_test->execute();
  156. foreach ($arr as $row) {
  157. $fortune[$row['id']] = $row['message'];
  158. }
  159. $fortune[0] = 'Additional fortune added at request time.';
  160. \asort($fortune);
  161. $html = '';
  162. foreach ($fortune as $id => $message) {
  163. $message = \htmlspecialchars($message, ENT_QUOTES, 'UTF-8');
  164. $html .= "<tr><td>{$id}</td><td>{$message}</td></tr>";
  165. }
  166. $pool->put($db);
  167. return '<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>'
  168. .$html.
  169. '</table></body></html>';
  170. };
  171. /**
  172. * The Updates test
  173. *
  174. * @param string $database_type
  175. * @param int $queries
  176. *
  177. * @return string
  178. */
  179. $updates_mysql = function (int $queries = 0) use ($pool): string {
  180. $db = $pool->get();
  181. $query_count = 1;
  182. if ($queries > 1) {
  183. $query_count = $queries > 500 ? 500 : $queries;
  184. }
  185. $arr = [];
  186. $db->updates_test_select ??= $db->prepare('SELECT id,randomNumber FROM World WHERE id = ?');
  187. $db->updates_test_update ??= $db->prepare('UPDATE World SET randomNumber = ? WHERE id = ?');
  188. while ($query_count--) {
  189. $id = \mt_rand(1, 10000);
  190. $randomNumber = \mt_rand(1, 10000);
  191. $ret = $db->updates_test_select->execute([$id]);
  192. // Store result in array.
  193. $world = ['id' => $id, 'randomNumber' => $ret[0]['randomNumber']];
  194. $world['randomNumber'] = $randomNumber;
  195. $db->updates_test_update->execute([$randomNumber, $id]);
  196. $arr[] = $world;
  197. }
  198. $pool->put($db);
  199. return \json_encode($arr, JSON_NUMERIC_CHECK);
  200. };
  201. /**
  202. * On every request to the (web)server, execute the following code
  203. */
  204. $server->on('request', function (Request $req, Response $res) use ($db, $fortunes, $updates) {
  205. try {
  206. switch ($req->server['request_uri']) {
  207. case '/json':
  208. $res->header('Content-Type', 'application/json');
  209. $res->end(json_encode(['message' => 'Hello, World!']));
  210. break;
  211. case '/plaintext':
  212. $res->header('Content-Type', 'text/plain; charset=utf-8');
  213. $res->end('Hello, World!');
  214. break;
  215. case '/db':
  216. $res->header('Content-Type', 'application/json');
  217. if (isset($req->get['queries'])) {
  218. $res->end($db((int)$req->get['queries']));
  219. } else {
  220. $res->end($db(-1));
  221. }
  222. break;
  223. case '/fortunes':
  224. $res->header('Content-Type', 'text/html; charset=utf-8');
  225. $res->end($fortunes());
  226. break;
  227. case '/updates':
  228. $res->header('Content-Type', 'application/json');
  229. if (isset($req->get['queries'])) {
  230. $res->end($updates((int)$req->get['queries']));
  231. } else {
  232. $res->end($updates(-1));
  233. }
  234. break;
  235. default:
  236. $res->status(404);
  237. $res->end('Error 404');
  238. }
  239. } catch (\Throwable $e) {
  240. $res->status(500);
  241. $res->end('Error 500');
  242. }
  243. });
  244. $server->start();
  245. /**
  246. * Class DatabasePool
  247. *
  248. * Deal with the fact that Swoole 2.1.3 has no build in database pooling
  249. */
  250. class DatabasePool
  251. {
  252. private $server = [
  253. 'host' => '',
  254. 'user' => 'benchmarkdbuser',
  255. 'password' => 'benchmarkdbpass',
  256. 'database' => 'hello_world'
  257. ];
  258. private $pool;
  259. private $type;
  260. public function __construct($type)
  261. {
  262. $this->server['host'] = \gethostbyname('tfb-database');
  263. $this->type = $type;
  264. }
  265. public function init($capacity)
  266. {
  267. $this->pool=new \Swoole\Coroutine\Channel($capacity);
  268. while($capacity>0){
  269. $db=$this->createDbInstance();
  270. if($db!==false){
  271. $this->pool->push($db);
  272. $capacity--;
  273. }
  274. }
  275. }
  276. private function createDbInstance()
  277. {
  278. if ($this->type === 'postgres') {
  279. $db = new Swoole\Coroutine\PostgreSql;
  280. if ($db->connect("host={$this->server['host']} port=5432 dbname={$this->server['database']} user={$this->server['user']} password={$this->server['password']}")){
  281. return $db;
  282. }
  283. } else if($this->type === 'mysql') {
  284. $db = new Swoole\Coroutine\Mysql;
  285. if ($db->connect($this->server)){
  286. return $db;
  287. }
  288. }
  289. return false;
  290. }
  291. public function put($db)
  292. {
  293. $this->pool->push($db);
  294. }
  295. public function get()
  296. {
  297. return $this->pool->pop();
  298. }
  299. }