swoole-server.php 9.3 KB

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