swoole-server.php 9.4 KB

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