PageRenderTime 41ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/examples/PHP/titanic.php

http://github.com/imatix/zguide
PHP | 260 lines | 173 code | 38 blank | 49 comment | 33 complexity | 8b44a30a205782ea21d1eb01170b0094 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /*
  3. * Titanic service
  4. *
  5. * Implements server side of http://rfc.zeromq.org/spec:9
  6. * @author Ian Barber <ian(dot)barber(at)gmail(dot)com>
  7. */
  8. include_once 'mdwrkapi.php';
  9. include_once 'mdcliapi.php';
  10. /* Return a new UUID as a printable character string */
  11. function s_generate_uuid()
  12. {
  13. $uuid = sprintf('%04x%04x%04x%03x4%04x%04x%04x%04x',
  14. mt_rand(0, 65535), mt_rand(0, 65535), // 32 bits for "time_low"
  15. mt_rand(0, 65535), // 16 bits for "time_mid"
  16. mt_rand(0, 4095), // 12 bits before the 0100 of (version) 4 for "time_hi_and_version"
  17. bindec(substr_replace(sprintf('%016b', mt_rand(0, 65535)), '01', 6, 2)),
  18. // 8 bits, the last two of which (positions 6 and 7) are 01, for "clk_seq_hi_res"
  19. // (hence, the 2nd hex digit after the 3rd hyphen can only be 1, 5, 9 or d)
  20. // 8 bits for "clk_seq_low"
  21. mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535) // 48 bits for "node"
  22. );
  23. return $uuid;
  24. }
  25. define("TITANIC_DIR", ".titanic");
  26. /**
  27. * Returns freshly allocated request filename for given UUID
  28. */
  29. function s_request_filename($uuid)
  30. {
  31. return TITANIC_DIR . "/" . $uuid . ".req";
  32. }
  33. /**
  34. * Returns freshly allocated reply filename for given UUID
  35. */
  36. function s_reply_filename($uuid)
  37. {
  38. return TITANIC_DIR . "/" . $uuid . ".rep";
  39. }
  40. /**
  41. * Titanic request service
  42. */
  43. function titanic_request($pipe)
  44. {
  45. $worker = new Mdwrk("tcp://localhost:5555", "titanic.request");
  46. $reply = null;
  47. while (true) {
  48. // Get next request from broker
  49. $request = $worker->recv($reply);
  50. // Ensure message directory exists
  51. if (!is_dir(TITANIC_DIR)) {
  52. mkdir(TITANIC_DIR);
  53. }
  54. // Generate UUID and save message to disk
  55. $uuid = s_generate_uuid();
  56. $filename = s_request_filename($uuid);
  57. $fh = fopen($filename, "w");
  58. $request->save($fh);
  59. fclose($fh);
  60. // Send UUID through to message queue
  61. $reply = new Zmsg($pipe);
  62. $reply->push($uuid);
  63. $reply->send();
  64. // Now send UUID back to client
  65. // - sent in the next loop iteration
  66. $reply = new Zmsg();
  67. $reply->push($uuid);
  68. $reply->push("200");
  69. }
  70. }
  71. /**
  72. * Titanic reply service
  73. */
  74. function titanic_reply()
  75. {
  76. $worker = new Mdwrk( "tcp://localhost:5555", "titanic.reply", false);
  77. $reply = null;
  78. while (true) {
  79. $request = $worker->recv($reply);
  80. $uuid = $request->pop();
  81. $req_filename = s_request_filename($uuid);
  82. $rep_filename = s_reply_filename($uuid);
  83. if (file_exists($rep_filename)) {
  84. $fh = fopen($rep_filename, "r");
  85. assert($fh);
  86. $reply = new Zmsg();
  87. $reply->load($fh);
  88. $reply->push("200");
  89. fclose($fh);
  90. } else {
  91. $reply = new Zmsg();
  92. if (file_exists($req_filename)) {
  93. $reply->push("300"); // Pending
  94. } else {
  95. $reply->push("400"); // Unknown
  96. }
  97. }
  98. }
  99. }
  100. /**
  101. * Titanic close service
  102. */
  103. function titanic_close()
  104. {
  105. $worker = new Mdwrk("tcp://localhost:5555", "titanic.close", false);
  106. $reply = null;
  107. while (true) {
  108. $request = $worker->recv($reply);
  109. $uuid = $request->pop();
  110. $req_filename = s_request_filename($uuid);
  111. $rep_filename = s_reply_filename($uuid);
  112. unlink($req_filename);
  113. unlink($rep_filename);
  114. $reply = new Zmsg();
  115. $reply->push("200");
  116. }
  117. }
  118. /**
  119. * Attempt to process a single request, return 1 if successful
  120. *
  121. * @param Mdcli $client
  122. * @param string $uuid
  123. */
  124. function s_service_success($client, $uuid)
  125. {
  126. // Load request message, service will be first frame
  127. $filename = s_request_filename($uuid);
  128. $fh = fopen($filename, "r");
  129. // If the client already closed request, treat as successful
  130. if (!$fh) {
  131. return true;
  132. }
  133. $request = new Zmsg();
  134. $request->load($fh);
  135. fclose($fh);
  136. $service = $request->pop();
  137. // Use MMI protocol to check if service is available
  138. $mmi_request = new Zmsg();
  139. $mmi_request->push($service);
  140. $mmi_reply = $client->send("mmi.service", $mmi_request);
  141. $service_ok = $mmi_reply && $mmi_reply->pop() == "200";
  142. if ($service_ok) {
  143. $reply = $client->send($service, $request);
  144. $filename = s_reply_filename($uuid);
  145. $fh = fopen($filename, "w");
  146. assert($fh);
  147. $reply->save($fh);
  148. fclose($fh);
  149. return true;
  150. }
  151. return false;
  152. }
  153. $verbose = $_SERVER['argc'] > 1 && $_SERVER['argv'][1] == '-v';
  154. $pid = pcntl_fork();
  155. if ($pid == 0) {
  156. titanic_reply();
  157. exit();
  158. }
  159. $pid = pcntl_fork();
  160. if ($pid == 0) {
  161. titanic_close();
  162. exit();
  163. }
  164. $pid = pcntl_fork();
  165. if ($pid == 0) {
  166. $pipe = new ZMQSocket(new ZMQContext(), ZMQ::SOCKET_PAIR);
  167. $pipe->connect("ipc://" . sys_get_temp_dir() . "/titanicpipe");
  168. titanic_request($pipe);
  169. exit();
  170. }
  171. // Create MDP client session with short timeout
  172. $client = new Mdcli("tcp://localhost:5555", $verbose);
  173. $client->set_timeout(1000); // 1 sec
  174. $client->set_retries(1); // only 1 retry
  175. $request_pipe = new ZMQSocket(new ZMQContext(), ZMQ::SOCKET_PAIR);
  176. $request_pipe->bind("ipc://" . sys_get_temp_dir() . "/titanicpipe");
  177. $read = $write = array();
  178. // Main dispatcher loop
  179. while (true) {
  180. // We'll dispatch once per second, if there's no activity
  181. $poll = new ZMQPoll();
  182. $poll->add($request_pipe, ZMQ::POLL_IN);
  183. $events = $poll->poll($read, $write, 1000);
  184. if ($events) {
  185. // Ensure message directory exists
  186. if (!is_dir(TITANIC_DIR)) {
  187. mkdir(TITANIC_DIR);
  188. }
  189. // Append UUID to queue, prefixed with '-' for pending
  190. $msg = new Zmsg($request_pipe);
  191. $msg->recv();
  192. $fh = fopen(TITANIC_DIR . "/queue", "a");
  193. $uuid = $msg->pop();
  194. fprintf($fh, "-%s\n", $uuid);
  195. fclose($fh);
  196. }
  197. // Brute-force dispatcher
  198. if (file_exists(TITANIC_DIR . "/queue")) {
  199. $fh = fopen(TITANIC_DIR . "/queue", "r+");
  200. while ($fh && $entry = fread($fh, 33)) {
  201. // UUID is prefixed with '-' if still waiting
  202. if ($entry[0] == "-") {
  203. if ($verbose) {
  204. printf ("I: processing request %s%s", substr($entry, 1), PHP_EOL);
  205. }
  206. if (s_service_success($client, substr($entry, 1))) {
  207. // Mark queue entry as processed
  208. fseek($fh, -33, SEEK_CUR);
  209. fwrite ($fh, "+");
  210. fseek($fh, 32, SEEK_CUR);
  211. }
  212. }
  213. // Skip end of line, LF or CRLF
  214. if (fgetc($fh) == "\r") {
  215. fgetc($fh);
  216. }
  217. }
  218. if ($fh) {
  219. fclose($fh);
  220. }
  221. }
  222. }