PageRenderTime 57ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/examples/PHP/peering3.php

https://github.com/dcolish/zguide
PHP | 272 lines | 186 code | 36 blank | 50 comment | 33 complexity | 31e4c2789fc5b43d68fe50b9750901c9 MD5 | raw file
  1. <?php
  2. /*
  3. * Broker peering simulation (part 3)
  4. * Prototypes the full flow of status and tasks
  5. * @author Ian Barber <ian(dot)barber(at)gmail(dot)com>
  6. */
  7. include "zmsg.php";
  8. define("NBR_CLIENTS", 10);
  9. define("NBR_WORKERS", 3);
  10. /*
  11. * Request-reply client using REQ socket
  12. * To simulate load, clients issue a burst of requests and then
  13. * sleep for a random period.
  14. */
  15. function client_thread($self) {
  16. $context = new ZMQContext();
  17. $client = new ZMQSocket($context, ZMQ::SOCKET_REQ);
  18. $endpoint = sprintf("ipc://%s-localfe.ipc", $self);
  19. $client->connect($endpoint);
  20. $monitor = new ZMQSocket($context, ZMQ::SOCKET_PUSH);
  21. $endpoint = sprintf("ipc://%s-monitor.ipc", $self);
  22. $monitor->connect($endpoint);
  23. $readable = $writeable = array();
  24. while(true) {
  25. sleep(mt_rand(0, 4));
  26. $burst = mt_rand(1, 14);
  27. while($burst--) {
  28. // Send request with random hex ID
  29. $task_id = sprintf("%04X", mt_rand(0, 10000));
  30. $client->send($task_id);
  31. // Wait max ten seconds for a reply, then complain
  32. $poll = new ZMQPoll();
  33. $poll->add($client, ZMQ::POLL_IN);
  34. $events = $poll->poll($readable, $writeable, 10 * 1000000);
  35. if($events > 0) {
  36. foreach($readable as $socket) {
  37. $zmsg = new Zmsg($socket);
  38. $zmsg->recv();
  39. // Worker is supposed to answer us with our task id
  40. assert($zmsg->body() == $task_id);
  41. }
  42. } else {
  43. $monitor->send(sprintf("E: CLIENT EXIT - lost task %s", $task_id));
  44. exit();
  45. }
  46. }
  47. }
  48. }
  49. // Worker using REQ socket to do LRU routing
  50. function worker_thread ($self) {
  51. $context = new ZMQContext();
  52. $worker = $context->getSocket(ZMQ::SOCKET_REQ);
  53. $endpoint = sprintf("ipc://%s-localbe.ipc", $self);
  54. $worker->connect($endpoint);
  55. // Tell broker we're ready for work
  56. $worker->send("READY");
  57. while(true) {
  58. $zmsg = new Zmsg($worker);
  59. $zmsg->recv();
  60. sleep(mt_rand(0,2));
  61. $zmsg->send();
  62. }
  63. }
  64. // First argument is this broker's name
  65. // Other arguments are our peers' names
  66. if($_SERVER['argc'] < 2) {
  67. echo "syntax: peering2 me {you}...", PHP_EOL;
  68. exit();
  69. }
  70. $self = $_SERVER['argv'][1];
  71. for($client_nbr = 0; $client_nbr < NBR_CLIENTS; $client_nbr++) {
  72. $pid = pcntl_fork();
  73. if($pid == 0) {
  74. client_thread($self);
  75. return;
  76. }
  77. }
  78. for($worker_nbr = 0; $worker_nbr < NBR_WORKERS; $worker_nbr++) {
  79. $pid = pcntl_fork();
  80. if($pid == 0) {
  81. worker_thread($self);
  82. return;
  83. }
  84. }
  85. printf ("I: preparing broker at %s... %s", $self, PHP_EOL);
  86. // Prepare our context and sockets
  87. $context = new ZMQContext();
  88. // Bind cloud frontend to endpoint
  89. $cloudfe = $context->getSocket(ZMQ::SOCKET_ROUTER);
  90. $endpoint = sprintf("ipc://%s-cloud.ipc", $self);
  91. $cloudfe->setSockOpt(ZMQ::SOCKOPT_IDENTITY, $self);
  92. $cloudfe->bind($endpoint);
  93. // Connect cloud backend to all peers
  94. $cloudbe = $context->getSocket(ZMQ::SOCKET_ROUTER);
  95. $cloudbe->setSockOpt(ZMQ::SOCKOPT_IDENTITY, $self);
  96. for ($argn = 2; $argn < $_SERVER['argc']; $argn++) {
  97. $peer = $_SERVER['argv'][$argn];
  98. printf ("I: connecting to cloud backend at '%s'%s", $peer, PHP_EOL);
  99. $endpoint = sprintf("ipc://%s-cloud.ipc", $peer);
  100. $cloudbe->connect($endpoint);
  101. }
  102. // Bind state backend / publisher to endpoint
  103. $statebe = new ZMQSocket($context, ZMQ::SOCKET_PUB);
  104. $endpoint = sprintf("ipc://%s-state.ipc", $self);
  105. $statebe->bind($endpoint);
  106. // Connect statefe to all peers
  107. $statefe = $context->getSocket(ZMQ::SOCKET_SUB);
  108. $statefe->setSockOpt(ZMQ::SOCKOPT_SUBSCRIBE, "");
  109. for ($argn = 2; $argn < $_SERVER['argc']; $argn++) {
  110. $peer = $_SERVER['argv'][$argn];
  111. printf ("I: connecting to state backend at '%s'%s", $peer, PHP_EOL);
  112. $endpoint = sprintf("ipc://%s-state.ipc", $peer);
  113. $statefe->connect($endpoint);
  114. }
  115. // Prepare monitor socket
  116. $monitor = new ZMQSocket($context, ZMQ::SOCKET_PULL);
  117. $endpoint = sprintf("ipc://%s-monitor.ipc", $self);
  118. $monitor->bind($endpoint);
  119. // Prepare local frontend and backend
  120. $localfe = new ZMQSocket($context, ZMQ::SOCKET_ROUTER);
  121. $endpoint = sprintf("ipc://%s-localfe.ipc", $self);
  122. $localfe->bind($endpoint);
  123. $localbe = new ZMQSocket($context, ZMQ::SOCKET_ROUTER);
  124. $endpoint = sprintf("ipc://%s-localbe.ipc", $self);
  125. $localbe->bind($endpoint);
  126. // Interesting part
  127. // -------------------------------------------------------------
  128. // Publish-subscribe flow
  129. // - Poll statefe and process capacity updates
  130. // - Each time capacity changes, broadcast new value
  131. // Request-reply flow
  132. // - Poll primary and process local/cloud replies
  133. // - While worker available, route localfe to local or cloud
  134. // Queue of available workers
  135. $local_capacity = 0;
  136. $cloud_capacity = 0;
  137. $worker_queue = array();
  138. $readable = $writeable = array();
  139. while(true) {
  140. $poll = new ZMQPoll();
  141. $poll->add($localbe, ZMQ::POLL_IN);
  142. $poll->add($cloudbe, ZMQ::POLL_IN);
  143. $poll->add($statefe, ZMQ::POLL_IN);
  144. $poll->add($monitor, ZMQ::POLL_IN);
  145. $events = 0;
  146. // If we have no workers anyhow, wait indefinitely
  147. try {
  148. $events = $poll->poll($readable, $writeable, $local_capacity ? 1000000 : -1);
  149. } catch(ZMQPollException $e) {
  150. break;
  151. }
  152. // Track if capacity changes during this iteration
  153. $previous = $local_capacity;
  154. foreach($readable as $socket) {
  155. $zmsg = new Zmsg($socket);
  156. // Handle reply from local worker
  157. if($socket === $localbe) {
  158. // Use worker address for LRU routing
  159. $zmsg->recv();
  160. $worker_queue[] = $zmsg->unwrap();
  161. $local_capacity++;
  162. if($zmsg->body() == "READY") {
  163. $zmsg = null; // Don't route it
  164. }
  165. }
  166. // Or handle reply from peer broker
  167. else if($socket === $cloudbe) {
  168. // We don't use peer broker address for anything
  169. $zmsg->recv()->unwrap();
  170. }
  171. // Handle capacity updates
  172. else if($socket === $statefe) {
  173. $zmsg->recv();
  174. $cloud_capacity = $zmsg->body();
  175. $zmsg = null;
  176. }
  177. // Handle monitor message
  178. else if($socket === $monitor) {
  179. $zmsg->recv();
  180. echo $zmsg->body(), PHP_EOL;
  181. $zmsg = null;
  182. }
  183. if($zmsg) {
  184. // Route reply to cloud if it's addressed to a broker
  185. for($argn = 2; $argn < $_SERVER['argc']; $argn++) {
  186. if($zmsg->address() == $_SERVER['argv'][$argn]) {
  187. $zmsg->set_socket($cloudfe)->send();
  188. $zmsg = null;
  189. }
  190. }
  191. }
  192. // Route reply to client if we still need to
  193. if($zmsg) {
  194. $zmsg->set_socket($localfe)->send();
  195. }
  196. }
  197. // Now route as many clients requests as we can handle
  198. // - If we have local capacity we poll both localfe and cloudfe
  199. // - If we have cloud capacity only, we poll just localfe
  200. // - Route any request locally if we can, else to cloud
  201. while($local_capacity + $cloud_capacity) {
  202. $poll = new ZMQPoll();
  203. $poll->add($localfe, ZMQ::POLL_IN);
  204. if($local_capacity) {
  205. $poll->add($cloudfe, ZMQ::POLL_IN);
  206. }
  207. $reroutable = false;
  208. $events = $poll->poll($readable, $writeable, 0);
  209. if($events > 0) {
  210. foreach($readable as $socket) {
  211. $zmsg = new Zmsg($socket);
  212. $zmsg->recv();
  213. if($local_capacity) {
  214. $zmsg->wrap(array_shift($worker_queue), "");
  215. $zmsg->set_socket($localbe)->send();
  216. $local_capacity--;
  217. }
  218. else {
  219. // Route to random broker peer
  220. printf ("I: route request %s to cloud...%s", $zmsg->body(), PHP_EOL);
  221. $zmsg->wrap($_SERVER['argv'][mt_rand(2, ($_SERVER['argc']-1))]);
  222. $zmsg->set_socket($cloudbe)->send();
  223. }
  224. }
  225. } else {
  226. break; // No work, go back to backends
  227. }
  228. }
  229. if ($local_capacity != $previous) {
  230. // Broadcast new capacity
  231. $zmsg = new Zmsg($statebe);
  232. $zmsg->body_set($local_capacity);
  233. // We stick our own address onto the envelope
  234. $zmsg->wrap($self)->send();
  235. }
  236. }