PageRenderTime 56ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/demos/web-multi-process-test.php

https://github.com/stunami/amqphp
PHP | 271 lines | 190 code | 46 blank | 35 comment | 26 complexity | 277aaf8a063607f27a26ce29afeca241 MD5 | raw file
  1. <?php
  2. /**
  3. * This demo is intended to test real-life usage of persistent
  4. * connections. A single Connection / Broker config is used to
  5. * establish the same Connection / Channel setup on all worker threads
  6. * of a PHP server, the user can then send and receive messages.
  7. */
  8. use amqphp as amqp,
  9. amqphp\persistent as pconn,
  10. amqphp\protocol,
  11. amqphp\wire;
  12. // Must match webdepl.libdir property
  13. const LIBDIR = 'amqphp-libs';
  14. // Must match webdepl.viewdir property
  15. const VIEWDIR = 'amqphp-view';
  16. // Must match webdepl.confdir property
  17. const CONFDIR = 'amqphp-config';
  18. if (0) {
  19. // Use the NSPF distribution
  20. define('DEMO_LOAD_NSPF', true);
  21. define('DEMO_LOADER_DIR', sprintf("%s%s%s%s%s%s",
  22. __DIR__,
  23. DIRECTORY_SEPARATOR,
  24. LIBDIR,
  25. DIRECTORY_SEPARATOR,
  26. 'nspf',
  27. DIRECTORY_SEPARATOR));
  28. } else {
  29. // Use the CPF distribution and a class loader
  30. define('DEMO_LOADER_DIR', sprintf("%s%s%s%s%s%s",
  31. __DIR__,
  32. DIRECTORY_SEPARATOR,
  33. LIBDIR,
  34. DIRECTORY_SEPARATOR,
  35. 'cpf',
  36. DIRECTORY_SEPARATOR));
  37. }
  38. require LIBDIR . DIRECTORY_SEPARATOR . 'demo-loader.php';
  39. require LIBDIR . DIRECTORY_SEPARATOR . 'web-common.php';
  40. require LIBDIR . DIRECTORY_SEPARATOR . 'Setup.php';
  41. /**
  42. * Uses automatic Amqphp persistence and keeps a central store of open
  43. * connections so that the interface can display information on all
  44. * open connections.
  45. */
  46. class MultiProcessPCTest
  47. {
  48. // WARNING - If you restart your web server you must delete this file!!
  49. // Persistence format: array(<procid> => <created timestamp>);
  50. public $connStore = '/tmp/MultiProcessPCTest-runtime.txt';
  51. public $connections;
  52. public $allProcs = array();
  53. public $connConfig;
  54. public $view;
  55. function start ($config) {
  56. $s = new Setup;
  57. $this->connections = $s->getSetup($config);
  58. // Read in the server process tracker data and make sure this process is in it.
  59. if ($buff = @file_get_contents($this->connStore)) {
  60. $this->allProcs = unserialize($buff);
  61. }
  62. $p = getmypid();
  63. if (! array_key_exists($p, $this->allProcs)) {
  64. $this->allProcs[$p] = time();
  65. }
  66. // Parse the connections config so thisis available in the template
  67. $d = new DOMDocument;
  68. $d->load($config);
  69. $d->xinclude();
  70. if (! ($this->connConfig = simplexml_import_dom($d))) {
  71. throw new \Exception("Invalid setup format.", 92656);
  72. }
  73. }
  74. /**
  75. * Sleep the local connections (automatic persistence) and save
  76. * this processes connection meta data to the central store
  77. */
  78. function sleep () {
  79. foreach ($this->connections as $i => $c) {
  80. $c->sleep();
  81. }
  82. file_put_contents($this->connStore, serialize($this->allProcs));
  83. }
  84. function runCommands ($c) {
  85. try {
  86. foreach ($c as $cmd) {
  87. switch ($cmd['cmd_type']) {
  88. case 'consume':
  89. MultiProcessPCTest::$CC = 0;
  90. $this->doConsume($cmd);
  91. break;
  92. case 'publish':
  93. $this->doPublish($cmd);
  94. break;
  95. default:
  96. $this->view->messages[] = sprintf('Invalid command type: %s', $cmd['cmd_type']);
  97. }
  98. }
  99. } catch (Exception $e) {
  100. $this->view->messages[] = sprintf("Exception in command loop:<br/>%s", $e->getMessage());
  101. }
  102. }
  103. public static $CC = 0;
  104. private function doConsume ($params) {
  105. // Locate target connection
  106. $conn = false;
  107. foreach ($this->connections as $i => $c) {
  108. if ($i == $params['connection']) {
  109. $conn = $c;
  110. break;
  111. }
  112. }
  113. if (! $conn) {
  114. $this->view->messages[] = sprintf("Cannot consume on connection %d - no such connection", $params['connection']);
  115. return;
  116. }
  117. // Attach an output alert function to any connected consumers
  118. $nf = function (wire\Method $meth, amqp\Channel $chan, $cons) {
  119. error_log(sprintf("[multi-recv:%s]\n%s\n", $cons->name, substr($meth->getContent(), 0, 10)));
  120. MultiProcessPCTest::$CC++;
  121. };
  122. foreach ($conn->getChannels() as $chan) {
  123. foreach ($chan->getConsumerTags() as $t) {
  124. error_log(sprintf("Add callback to consumer %d-%d-%s", $params['connection'], $chan->getChanId(), $t));
  125. $chan->getConsumerByTag($t)->nf = $nf;
  126. }
  127. }
  128. // Invoke the consume
  129. switch ($params['type']) {
  130. case 'maxloops':
  131. $loops = ($params['param1'] && is_numeric($params['param1'])) ?
  132. (int) $params['param1']
  133. : 1;
  134. $conn->setSelectMode(amqp\SELECT_MAXLOOPS, $loops);
  135. $conn->select();
  136. $this->view->messages[] = sprintf("Consumed %d messages on connection %d via. maxloops with trigger %d",
  137. MultiProcessPCTest::$CC, $params['connection'], $loops);
  138. break;
  139. case 'timeout':
  140. // Sanitize the input.
  141. $secs = ($params['param1'] && is_numeric($params['param1'])) ?
  142. (int) $params['param1']
  143. : 1;
  144. $usecs = ($params['param2'] && is_numeric($params['param2'])) ?
  145. (int) $params['param2']
  146. : 0;
  147. $conn->setSelectMode(amqp\SELECT_TIMEOUT_REL, (string) $secs, (string) $usecs);
  148. $conn->select();
  149. $this->view->messages[] = sprintf("Consumed %d messages on connection %d via. timeout with trigger (%s, %s)",
  150. MultiProcessPCTest::$CC, $params['connection'], (string) $secs, (string) $usecs);
  151. break;
  152. default:
  153. $this->view->messages[] = sprintf("Unknown / not implemented consume type: %s", $params['type']);
  154. return;
  155. }
  156. }
  157. /**
  158. * Publishes messages as specified in $params
  159. */
  160. private function doPublish ($params) {
  161. // Collect the channels that will be published on
  162. $chans = array();
  163. foreach (array_keys($params['chans']) as $chanDef) {
  164. list($connId, $chanId) = explode('.', $chanDef);
  165. foreach ($this->connections as $i => $conn) {
  166. if ($i == $connId) {
  167. foreach ($conn->getChannels() as $j => $chan) {
  168. if ($j == $chanId) {
  169. $chans[] = $chan;
  170. break;
  171. }
  172. }
  173. break;
  174. }
  175. }
  176. }
  177. if (! $chans) {
  178. $this->view->messages[] = "Error: no channels were found!";
  179. }
  180. $pubParams = array();
  181. foreach ($params['params'] as $p => $v) {
  182. if ($p == 'mandatory' || $p == 'immediate') {
  183. $pubParams[$p] = true;
  184. } else {
  185. $pubParams[$p] = $v;
  186. }
  187. }
  188. $np = 0;
  189. foreach ($chans as $chan) {
  190. $bp = $chan->basic('publish', $pubParams, $params['content']);
  191. for ($j = 0; $j < (int) $params['repeat']; $j++) {
  192. $chan->invoke($bp);
  193. $np++;
  194. }
  195. }
  196. $this->view->messages[] = sprintf("Published %d messages to %d channels", $np, count($chans));
  197. }
  198. }
  199. function dump () {
  200. ob_start();
  201. foreach (func_get_args() as $arg) {
  202. var_dump($arg);
  203. }
  204. return ob_get_clean();
  205. }
  206. class Actions extends Router
  207. {
  208. function sendMessages () {
  209. $this->ch == 'MultiProcessPCTest';
  210. }
  211. }
  212. //
  213. // MVC Runtime.
  214. //
  215. $view = new View;
  216. $actions = new Actions;
  217. $chelper = new MultiProcessPCTest;
  218. $chelper->start(CONFDIR . DIRECTORY_SEPARATOR . 'web-multi.xml');
  219. $chelper->view = $view;
  220. if (array_key_exists('cmds', $_POST)) {
  221. $chelper->runCommands($_POST['cmds']);
  222. }
  223. $view->helper = $chelper;
  224. $view->render(VIEWDIR . DIRECTORY_SEPARATOR . 'web-controls-multi.phtml');
  225. $chelper->sleep();