├── .gitignore ├── README.md ├── composer.json ├── examples ├── 1-hello-world │ ├── README.md │ ├── receive-async.php │ ├── send-async.php │ └── send.php ├── 2-work-queues │ ├── README.md │ ├── new_task-async.php │ ├── new_task.php │ └── worker-async.php ├── 3-publish-subscribe │ ├── README.md │ ├── emit_log-async.php │ ├── emit_log.php │ └── receive_logs-async.php ├── 4-routing │ ├── README.md │ ├── emit_log-async.php │ ├── emit_log.php │ └── receive_logs-async.php ├── 5-topics │ ├── README.md │ ├── emit_log_topic-async.php │ ├── emit_log_topic.php │ └── receive_logs_topic-async.php └── workerman │ ├── consumer_use_coroutine_client.php │ ├── consumer_use_eventloop_client.php │ ├── producer_in_workerman_use_coroutine_client.php │ ├── producer_in_workerman_use_eventloop_client.php │ └── producer_use_coroutine_client.php └── src ├── Client.php ├── Clients ├── CoroutineClient.php └── EventloopClient.php └── Traits ├── LoggerMethods.php ├── MechanismMethods.php └── RestartMethods.php /.gitignore: -------------------------------------------------------------------------------- 1 | logs 2 | .buildpath 3 | .project 4 | .settings 5 | .idea 6 | .DS_Store 7 | vendor 8 | composer.lock 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rabbitmq 2 | Asynchronous rabbitmq client for PHP based on [workerman](https://github.com/walkor/workerman). 3 | 4 | # Install 5 | ``` 6 | composer require workerman/rabbitmq 7 | ``` 8 | 9 | # Examples 10 | 11 | ## Consumer 12 | 13 | - receive.php 14 | ```php 15 | eventLoop = \Workerman\Events\Fiber::class; 28 | 29 | $worker->onWorkerStart = function() { 30 | // Create RabbitMQ Client 31 | $client = Client::factory([ 32 | 'host' => '127.0.0.1', 33 | 'port' => 5672, 34 | 'user' => 'guest', 35 | 'password' => 'guest', 36 | 'vhost' => '/', 37 | 'heartbeat' => 60, 38 | 'heartbeat_callback' => function () { 39 | echo " [-] coroutine-consumer-heartbeat\n"; 40 | }, 41 | 'interval' => [100, 300] 42 | ])->connect(); 43 | $channel = $client->channel(); 44 | $channel->queueDeclare('hello-coroutine'); 45 | 46 | // Consumer 47 | $channel->consume(function (Message $message, Channel $channel, \Bunny\AbstractClient $client) { 48 | echo " [>] Received ", $message->content, "\n"; 49 | }, 50 | 'hello-coroutine', 51 | '', 52 | false, 53 | true 54 | ); 55 | $client->run(); 56 | echo ' [*] Waiting for messages. To exit press CTRL+C', "\n"; 57 | 58 | // Producer 59 | \Workerman\Timer::add($interval = 5 , function () use ($channel) { 60 | $channel->publish($message = 'Hello World By Self Timer. ' . time(), [], '', 'hello-coroutine'); 61 | echo " [<] Sent $message\n"; 62 | }); 63 | echo " [!] Producer timer created, interval: $interval s.\n"; 64 | 65 | }; 66 | Worker::runAll(); 67 | ``` 68 | - Run command `php receive.php start`. 69 | 70 | ## Publish use on Workerman ENV 71 | 72 | - send.php 73 | ```php 74 | eventLoop = \Workerman\Events\Fiber::class; 85 | 86 | $worker->onWorkerStart = function() { 87 | $client = Client::factory([ 88 | 'host' => 'host.docker.internal', 89 | 'port' => 5672, 90 | 'user' => 'guest', 91 | 'password' => 'guest', 92 | 'vhost' => '/', 93 | 'heartbeat' => 60, 94 | 'heartbeat_callback' => function () { 95 | echo "coroutine-producer-heartbeat\n"; 96 | } 97 | ])->connect(); 98 | $channel = $client->channel(); 99 | $channel->queueDeclare('hello-coroutine'); 100 | 101 | // 每5秒发一个消息 102 | \Workerman\Timer::add(5, function () use ($channel) { 103 | $channel->publish($message = 'Hello World By Workerman Env Producer. ' . time(), [], '', 'hello-coroutine'); 104 | echo " [x] Sent '$message'\n"; 105 | }); 106 | }; 107 | Worker::runAll(); 108 | ``` 109 | - Run command `php send.php start`. 110 | 111 | ## Publish script or use on PHP-FPM 112 | 113 | - script.php 114 | ```php 115 | 'host.docker.internal', 125 | 'port' => 5672, 126 | 'user' => 'guest', 127 | 'password' => 'guest', 128 | 'vhost' => '/', 129 | 'heartbeat' => 60, 130 | 'heartbeat_callback' => function () { 131 | echo "coroutine-producer-heartbeat\n"; 132 | } 133 | ])->connect(); 134 | $channel = $client->channel(); 135 | $channel->queueDeclare('hello-coroutine'); 136 | $res = $channel->publish($message = 'Hello World By Normal Producer. ' . time(), [], '', 'hello-coroutine'); 137 | 138 | echo " [x] Sent '$message', success: $res\n"; 139 | 140 | ``` 141 | - Run command `php script.php`. 142 | 143 | ## Async Consumer 144 | 145 | - receive.php 146 | ```php 147 | onWorkerStart = function() { 159 | (new Client())->connect()->then(function (Client $client) { 160 | return $client->channel(); 161 | })->then(function (Channel $channel) { 162 | return $channel->queueDeclare('hello', false, false, false, false)->then(function () use ($channel) { 163 | return $channel; 164 | }); 165 | })->then(function (Channel $channel) { 166 | echo ' [*] Waiting for messages. To exit press CTRL+C', "\n"; 167 | $channel->consume( 168 | function (Message $message, Channel $channel, Client $client) { 169 | echo " [x] Received ", $message->content, "\n"; 170 | }, 171 | 'hello', 172 | '', 173 | false, 174 | true 175 | ); 176 | }); 177 | }; 178 | Worker::runAll(); 179 | ``` 180 | - Run command `php receive.php start`. 181 | 182 | ## Async Producer 183 | 184 | - send.php 185 | ```php 186 | onWorkerStart = function() { 197 | (new Client())->connect()->then(function (Client $client) { 198 | return $client->channel(); 199 | })->then(function (Channel $channel) { 200 | return $channel->queueDeclare('hello', false, false, false, false)->then(function () use ($channel) { 201 | return $channel; 202 | }); 203 | })->then(function (Channel $channel) { 204 | echo " [x] Sending 'Hello World!'\n"; 205 | return $channel->publish('Hello World!', [], '', 'hello')->then(function () use ($channel) { 206 | return $channel; 207 | }); 208 | })->then(function (Channel $channel) { 209 | echo " [x] Sent 'Hello World!'\n"; 210 | $client = $channel->getClient(); 211 | return $channel->close()->then(function () use ($client) { 212 | return $client; 213 | }); 214 | })->then(function (Client $client) { 215 | $client->disconnect(); 216 | }); 217 | }; 218 | Worker::runAll(); 219 | ``` 220 | - Run command `php send.php start`. 221 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "workerman/rabbitmq", 3 | "type": "library", 4 | "keywords": [ 5 | "amqp", 6 | "rabbitmq client" 7 | ], 8 | "homepage": "https://www.workerman.net", 9 | "license": "MIT", 10 | "description": "Asynchronous rabbitmq client for PHP based on workerman.", 11 | "authors": [ 12 | { 13 | "name": "walkor", 14 | "email": "walkor@workerman.net", 15 | "homepage": "http://www.workerman.net", 16 | "role": "Developer" 17 | }, 18 | { 19 | "name": "chaz6chez", 20 | "email": "chaz6chez1993@outlook.com", 21 | "role": "Developer" 22 | } 23 | ], 24 | "support": { 25 | "email": "walkor@workerman.net", 26 | "issues": "https://github.com/walkor/rabbitmq/issues", 27 | "forum": "http://wenda.workerman.net/", 28 | "wiki": "http://doc.workerman.net/", 29 | "source": "https://github.com/walkor/rabbitmq" 30 | }, 31 | "require": { 32 | "php": "^8.1", 33 | "workerman/webman-framework": "^2.1 || dev-master", 34 | "workerman/workerman": "^5.0", 35 | "bunny/bunny": "^0.5", 36 | "psr/log": "^1.0 || ^2.0 || ^3.0" 37 | }, 38 | "require-dev": { 39 | "revolt/event-loop": "^1.0", 40 | "symfony/var-dumper": "^6.0 | ^7.0" 41 | }, 42 | "autoload": { 43 | "psr-4": { 44 | "Workerman\\RabbitMQ\\": "src/" 45 | } 46 | }, 47 | "suggest": { 48 | "workerman/coroutine": "For supporting Coroutine. " 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /examples/1-hello-world/README.md: -------------------------------------------------------------------------------- 1 | # 1 Hello World 2 | [http://www.rabbitmq.com/tutorials/tutorial-one-php.html](http://www.rabbitmq.com/tutorials/tutorial-one-php.html) 3 | 4 | ``` 5 | php receive-async.php start 6 | ``` 7 | 8 | ``` 9 | php send-async.php start 10 | ``` 11 | -------------------------------------------------------------------------------- /examples/1-hello-world/receive-async.php: -------------------------------------------------------------------------------- 1 | onWorkerStart = function() { 13 | (new Client())->connect()->then(function (Client $client) { 14 | return $client->channel(); 15 | })->then(function (Channel $channel) { 16 | return $channel->queueDeclare('hello', false, false, false, false)->then(function () use ($channel) { 17 | return $channel; 18 | }); 19 | })->then(function (Channel $channel) { 20 | echo ' [*] Waiting for messages. To exit press CTRL+C', "\n"; 21 | $channel->consume( 22 | function (Message $message, Channel $channel, Client $client) { 23 | echo " [x] Received ", $message->content, "\n"; 24 | }, 25 | 'hello', 26 | '', 27 | false, 28 | true 29 | ); 30 | }); 31 | }; 32 | 33 | 34 | Worker::runAll(); -------------------------------------------------------------------------------- /examples/1-hello-world/send-async.php: -------------------------------------------------------------------------------- 1 | onWorkerStart = function() { 12 | (new Client())->connect()->then(function (Client $client) { 13 | return $client->channel(); 14 | })->then(function (Channel $channel) { 15 | return $channel->queueDeclare('hello', false, false, false, false)->then(function () use ($channel) { 16 | return $channel; 17 | }); 18 | })->then(function (Channel $channel) { 19 | echo " [x] Sending 'Hello World!'\n"; 20 | return $channel->publish('Hello World!', [], '', 'hello')->then(function () use ($channel) { 21 | return $channel; 22 | }); 23 | })->then(function (Channel $channel) { 24 | echo " [x] Sent 'Hello World!'\n"; 25 | $client = $channel->getClient(); 26 | return $channel->close()->then(function () use ($client) { 27 | return $client; 28 | }); 29 | })->then(function (Client $client) { 30 | $client->disconnect(); 31 | }); 32 | }; 33 | Worker::runAll(); -------------------------------------------------------------------------------- /examples/1-hello-world/send.php: -------------------------------------------------------------------------------- 1 | connect(); 8 | $channel = $client->channel(); 9 | 10 | $channel->queueDeclare('hello', false, false, false, false); 11 | 12 | $channel->publish('Hello World!', [], '', 'hello'); 13 | echo " [x] Sent 'Hello World!'\n"; 14 | 15 | $channel->close(); 16 | $client->disconnect(); 17 | -------------------------------------------------------------------------------- /examples/2-work-queues/README.md: -------------------------------------------------------------------------------- 1 | # 2 Work Queues 2 | [http://www.rabbitmq.com/tutorials/tutorial-two-php.html](http://www.rabbitmq.com/tutorials/tutorial-two-php.html) 3 | 4 | ``` 5 | php worker-async.php start 6 | ``` 7 | 8 | 9 | ``` 10 | php new_task-asnyc.php start First message. 11 | php new_task-asnyc.php start Second message.. 12 | php new_task-asnyc.php start Third message... 13 | php new_task-asnyc.php start Fourth message.... 14 | php new_task-asnyc.php start Fifth message..... 15 | 16 | ``` -------------------------------------------------------------------------------- /examples/2-work-queues/new_task-async.php: -------------------------------------------------------------------------------- 1 | onWorkerStart = function() { 13 | 14 | global $argv; 15 | unset($argv[1]); 16 | $data = implode(' ', array_slice($argv, 1)); 17 | publish('task_queue', $data, ['delivery-mode' => 2], '', 'task_queue'); 18 | return; 19 | (new Client())->connect()->then(function (Client $client) { 20 | return $client->channel(); 21 | })->then(function (Channel $channel) { 22 | return $channel->queueDeclare('task_queue', false, true, false, false)->then(function () use ($channel) { 23 | return $channel; 24 | }); 25 | })->then(function (Channel $channel) use ($data) { 26 | echo " [x] Sending '{$data}'\n"; 27 | return $channel->publish( 28 | $data, 29 | [ 30 | 'delivery-mode' => 2 31 | ], 32 | '', 33 | 'task_queue' 34 | )->then(function () use ($channel) { 35 | return $channel; 36 | }); 37 | })->then(function (Channel $channel) use ($data) { 38 | echo " [x] Sent '{$data}'\n"; 39 | $client = $channel->getClient(); 40 | return $channel->close()->then(function () use ($client) { 41 | return $client; 42 | }); 43 | })->then(function (Client $client) { 44 | $client->disconnect(); 45 | }); 46 | }; 47 | 48 | function publish($queue, $body, $header, $exchange, $routing_key) 49 | { 50 | (new Client())->connect()->then(function (Client $client) { 51 | return $client->channel(); 52 | })->then(function (Channel $channel) use ($queue) { 53 | return $channel->queueDeclare($queue, false, false, false, false)->then(function () use ($channel) { 54 | return $channel; 55 | }); 56 | })->then(function (Channel $channel) use ($body, $header, $exchange, $routing_key) { 57 | echo " [x] Sending 'Hello World!'\n"; 58 | return $channel->publish($body, $header, $exchange, $routing_key)->then(function () use ($channel) { 59 | return $channel; 60 | }); 61 | })->then(function (Channel $channel) { 62 | echo " [x] Sent 'Hello World!'\n"; 63 | $client = $channel->getClient(); 64 | return $channel->close()->then(function () use ($client) { 65 | return $client; 66 | }); 67 | })->then(function (Client $client) { 68 | $client->disconnect(); 69 | }); 70 | } 71 | 72 | Worker::runAll(); 73 | -------------------------------------------------------------------------------- /examples/2-work-queues/new_task.php: -------------------------------------------------------------------------------- 1 | connect(); 8 | $channel = $client->channel(); 9 | 10 | $channel->queueDeclare('task_queue', false, true, false, false); 11 | 12 | $data = implode(' ', array_slice($argv, 1)); 13 | $channel->publish( 14 | $data, 15 | [ 16 | 'delivery-mode' => 2 17 | ], 18 | '', 19 | 'task_queue' 20 | ); 21 | echo " [x] Sent '{$data}'\n"; 22 | 23 | $channel->close(); 24 | $client->disconnect(); 25 | -------------------------------------------------------------------------------- /examples/2-work-queues/worker-async.php: -------------------------------------------------------------------------------- 1 | onWorkerStart = function() { 13 | (new Client())->connect()->then(function (Client $client) { 14 | return $client->channel(); 15 | })->then(function (Channel $channel) { 16 | return $channel->qos(0, 1)->then(function () use ($channel) { 17 | return $channel; 18 | }); 19 | })->then(function (Channel $channel) { 20 | return $channel->queueDeclare('task_queue', false, true, false, false)->then(function () use ($channel) { 21 | return $channel; 22 | }); 23 | })->then(function (Channel $channel) { 24 | echo ' [*] Waiting for messages. To exit press CTRL+C', "\n"; 25 | $channel->consume( 26 | function (Message $message, Channel $channel, Client $client) { 27 | echo " [x] Received ", $message->content, "\n"; 28 | sleep(substr_count($message->content, '.')); 29 | echo " [x] Done", $message->content, "\n"; 30 | $channel->ack($message); 31 | }, 32 | 'task_queue' 33 | ); 34 | }); 35 | }; 36 | 37 | Worker::runAll(); 38 | -------------------------------------------------------------------------------- /examples/3-publish-subscribe/README.md: -------------------------------------------------------------------------------- 1 | # 3 Publish/Subscribe 2 | [http://www.rabbitmq.com/tutorials/tutorial-three-php.html](http://www.rabbitmq.com/tutorials/tutorial-three-php.html) 3 | 4 | 5 | 6 | ``` 7 | php receive_logs-async.php start 8 | ``` 9 | 10 | ``` 11 | php emit_log-async.php start 12 | ``` 13 | 14 | -------------------------------------------------------------------------------- /examples/3-publish-subscribe/emit_log-async.php: -------------------------------------------------------------------------------- 1 | onWorkerStart = function() { 12 | global $argv; 13 | unset($argv[1]); 14 | $data = implode(' ', array_slice($argv, 1)); 15 | if (empty($data)) { 16 | $data = "info: Hello World!"; 17 | } 18 | 19 | (new Client())->connect()->then(function (Client $client) { 20 | return $client->channel(); 21 | })->then(function (Channel $channel) { 22 | return $channel->exchangeDeclare('logs', 'fanout')->then(function () use ($channel) { 23 | return $channel; 24 | }); 25 | })->then(function (Channel $channel) use ($data) { 26 | echo " [x] Sending '{$data}'\n"; 27 | return $channel->publish($data, [], 'logs')->then(function () use ($channel) { 28 | return $channel; 29 | }); 30 | })->then(function (Channel $channel) use ($data) { 31 | echo " [x] Sent '{$data}'\n"; 32 | $client = $channel->getClient(); 33 | return $channel->close()->then(function () use ($client) { 34 | return $client; 35 | }); 36 | })->then(function (Client $client) { 37 | $client->disconnect(); 38 | }); 39 | 40 | }; 41 | Worker::runAll(); 42 | -------------------------------------------------------------------------------- /examples/3-publish-subscribe/emit_log.php: -------------------------------------------------------------------------------- 1 | connect(); 8 | $channel = $client->channel(); 9 | 10 | $channel->exchangeDeclare('logs', 'fanout'); 11 | 12 | $data = implode(' ', array_slice($argv, 1)); 13 | $channel->publish($data, [], 'logs'); 14 | echo " [x] Sent '{$data}'\n"; 15 | 16 | $channel->close(); 17 | $client->disconnect(); 18 | -------------------------------------------------------------------------------- /examples/3-publish-subscribe/receive_logs-async.php: -------------------------------------------------------------------------------- 1 | onWorkerStart = function() { 14 | (new Client())->connect()->then(function (Client $client) { 15 | return $client->channel(); 16 | })->then(function (Channel $channel) { 17 | return $channel->exchangeDeclare('logs', 'fanout')->then(function () use ($channel) { 18 | return $channel->queueDeclare('', false, false, true, false); 19 | })->then(function (MethodQueueDeclareOkFrame $frame) use ($channel) { 20 | return $channel->queueBind($frame->queue, 'logs')->then(function () use ($frame) { 21 | return $frame; 22 | }); 23 | })->then(function (MethodQueueDeclareOkFrame $frame) use ($channel) { 24 | echo ' [*] Waiting for logs. To exit press CTRL+C', "\n"; 25 | $channel->consume( 26 | function (Message $message, Channel $channel, Client $client) { 27 | echo ' [x] ', $message->content, "\n"; 28 | }, 29 | $frame->queue, 30 | '', 31 | false, 32 | true 33 | ); 34 | }); 35 | }); 36 | }; 37 | 38 | Worker::runAll(); 39 | -------------------------------------------------------------------------------- /examples/4-routing/README.md: -------------------------------------------------------------------------------- 1 | # 4 Routing 2 | [http://www.rabbitmq.com/tutorials/tutorial-four-php.html](http://www.rabbitmq.com/tutorials/tutorial-four-php.html) 3 | 4 | ``` 5 | php receive_logs-async.php start info warning error 6 | ``` 7 | 8 | ``` 9 | php emit_log-async.php start 10 | ``` -------------------------------------------------------------------------------- /examples/4-routing/emit_log-async.php: -------------------------------------------------------------------------------- 1 | onWorkerStart = function() { 12 | global $argv; 13 | unset($argv[1]); 14 | $argv = array_values($argv); 15 | $severity = isset($argv[1]) && !empty($argv[1]) ? $argv[1] : 'info'; 16 | $data = implode(' ', array_slice($argv, 2)); 17 | if (empty($data)) { 18 | $data = "Hello World!"; 19 | } 20 | 21 | (new Client())->connect()->then(function (Client $client) { 22 | return $client->channel(); 23 | })->then(function (Channel $channel) { 24 | return $channel->exchangeDeclare('direct_logs', 'direct')->then(function () use ($channel) { 25 | return $channel; 26 | }); 27 | })->then(function (Channel $channel) use ($data, $severity) { 28 | echo " [x] Sending ", $severity, ':', $data, " \n"; 29 | return $channel->publish($data, [], 'direct_logs', $severity)->then(function () use ($channel) { 30 | return $channel; 31 | }); 32 | })->then(function (Channel $channel) use ($data, $severity) { 33 | echo " [x] Sent ", $severity, ':', $data, " \n"; 34 | $client = $channel->getClient(); 35 | return $channel->close()->then(function () use ($client) { 36 | return $client; 37 | }); 38 | })->then(function (Client $client) { 39 | $client->disconnect(); 40 | }); 41 | }; 42 | 43 | Worker::runAll(); 44 | -------------------------------------------------------------------------------- /examples/4-routing/emit_log.php: -------------------------------------------------------------------------------- 1 | connect(); 8 | $channel = $client->channel(); 9 | 10 | $channel->exchangeDeclare('direct_logs', 'direct'); 11 | 12 | $severity = isset($argv[1]) && !empty($argv[1]) ? $argv[1] : 'info'; 13 | $data = implode(' ', array_slice($argv, 2)); 14 | if (empty($data)) { 15 | $data = "Hello World!"; 16 | } 17 | 18 | $channel->publish($data, [], 'direct_logs', $severity); 19 | echo " [x] Sent ",$severity,':',$data," \n"; 20 | 21 | $channel->close(); 22 | $client->disconnect(); 23 | -------------------------------------------------------------------------------- /examples/4-routing/receive_logs-async.php: -------------------------------------------------------------------------------- 1 | onWorkerStart = function() { 14 | global $argv; 15 | unset($argv[1]); 16 | $argv = array_values($argv); 17 | $severities = array_slice($argv, 1); 18 | if (empty($severities)) { 19 | file_put_contents('php://stderr', "Usage: $argv[0] [info] [warning] [error]\n"); 20 | exit(1); 21 | } 22 | (new Client())->connect()->then(function (Client $client) { 23 | return $client->channel(); 24 | })->then(function (Channel $channel) use ($severities) { 25 | return $channel->exchangeDeclare('direct_logs', 'direct')->then(function () use ($channel, $severities) { 26 | return $channel->queueDeclare('', false, false, true, false); 27 | })->then(function (MethodQueueDeclareOkFrame $frame) use ($channel, $severities) { 28 | $promises = []; 29 | 30 | foreach ($severities as $severity) { 31 | $promises[] = $channel->queueBind($frame->queue, 'direct_logs', $severity); 32 | } 33 | 34 | return \React\Promise\all($promises)->then(function () use ($frame) { 35 | return $frame; 36 | }); 37 | })->then(function (MethodQueueDeclareOkFrame $frame) use ($channel) { 38 | echo ' [*] Waiting for logs. To exit press CTRL+C', "\n"; 39 | $channel->consume( 40 | function (Message $message, Channel $channel, Client $client) { 41 | echo ' [x] ', $message->content, "\n"; 42 | }, 43 | $frame->queue, 44 | '', 45 | false, 46 | true 47 | ); 48 | }); 49 | }); 50 | }; 51 | 52 | Worker::runAll(); 53 | -------------------------------------------------------------------------------- /examples/5-topics/README.md: -------------------------------------------------------------------------------- 1 | # 5 Topics 2 | [http://www.rabbitmq.com/tutorials/tutorial-five-php.html](http://www.rabbitmq.com/tutorials/tutorial-five-php.html) 3 | 4 | 5 | ``` 6 | php receive_logs_topic-async.php start "#" 7 | php receive_logs_topic-async.php start "kern.*" 8 | php receive_logs_topic-async.php start "*.critical" 9 | php receive_logs_topic-async.php start "kern.*" "*.critical" 10 | ``` 11 | 12 | ``` 13 | php emit_log_topic-async.php start 14 | ``` -------------------------------------------------------------------------------- /examples/5-topics/emit_log_topic-async.php: -------------------------------------------------------------------------------- 1 | onWorkerStart = function() { 11 | global $argv; 12 | unset($argv[1]); 13 | $argv = array_values($argv); 14 | $routing_key = isset($argv[1]) && !empty($argv[1]) ? $argv[1] : 'info'; 15 | $data = implode(' ', array_slice($argv, 2)); 16 | if (empty($data)) { 17 | $data = "Hello World!"; 18 | } 19 | 20 | (new Client())->connect()->then(function (Client $client) { 21 | return $client->channel(); 22 | })->then(function (Channel $channel) { 23 | return $channel->exchangeDeclare('topic_logs', 'topic')->then(function () use ($channel) { 24 | return $channel; 25 | }); 26 | })->then(function (Channel $channel) use ($data, $routing_key) { 27 | echo " [x] Sending ", $routing_key, ':', $data, " \n"; 28 | return $channel->publish($data, [], 'topic_logs', $routing_key)->then(function () use ($channel) { 29 | return $channel; 30 | }); 31 | })->then(function (Channel $channel) use ($data, $routing_key) { 32 | echo " [x] Sent ", $routing_key, ':', $data, " \n"; 33 | $client = $channel->getClient(); 34 | return $channel->close()->then(function () use ($client) { 35 | return $client; 36 | }); 37 | })->then(function (Client $client) { 38 | $client->disconnect(); 39 | }); 40 | 41 | }; 42 | 43 | Worker::runAll(); 44 | -------------------------------------------------------------------------------- /examples/5-topics/emit_log_topic.php: -------------------------------------------------------------------------------- 1 | connect(); 8 | $channel = $client->channel(); 9 | 10 | $channel->exchangeDeclare('topic_logs', 'topic'); 11 | 12 | $routing_key = isset($argv[1]) && !empty($argv[1]) ? $argv[1] : 'info'; 13 | $data = implode(' ', array_slice($argv, 2)); 14 | if (empty($data)) { 15 | $data = "Hello World!"; 16 | } 17 | 18 | $channel->publish($data, [], 'topic_logs', $routing_key); 19 | echo " [x] Sent ",$routing_key,':',$data," \n"; 20 | 21 | $channel->close(); 22 | $client->disconnect(); 23 | -------------------------------------------------------------------------------- /examples/5-topics/receive_logs_topic-async.php: -------------------------------------------------------------------------------- 1 | onWorkerStart = function() { 14 | global $argv; 15 | unset($argv[1]); 16 | $argv = array_values($argv); 17 | $binding_keys = array_slice($argv, 1); 18 | if (empty($binding_keys)) { 19 | file_put_contents('php://stderr', "Usage: $argv[0] [binding_key]\n"); 20 | exit(1); 21 | } 22 | 23 | (new Client())->connect()->then(function (Client $client) { 24 | return $client->channel(); 25 | })->then(function (Channel $channel) use ($binding_keys) { 26 | return $channel->exchangeDeclare('topic_logs', 'topic')->then(function () use ($channel, $binding_keys) { 27 | return $channel->queueDeclare('', false, false, true, false); 28 | })->then(function (MethodQueueDeclareOkFrame $frame) use ($channel, $binding_keys) { 29 | $promises = []; 30 | 31 | foreach ($binding_keys as $binding_key) { 32 | $promises[] = $channel->queueBind($frame->queue, 'topic_logs', $binding_key); 33 | } 34 | 35 | return \React\Promise\all($promises)->then(function () use ($frame) { 36 | return $frame; 37 | }); 38 | })->then(function (MethodQueueDeclareOkFrame $frame) use ($channel) { 39 | echo ' [*] Waiting for logs. To exit press CTRL+C', "\n"; 40 | $channel->consume( 41 | function (Message $message, Channel $channel, Client $client) { 42 | echo ' [x] ', $message->content, "\n"; 43 | }, 44 | $frame->queue, 45 | '', 46 | false, 47 | true 48 | ); 49 | }); 50 | }); 51 | }; 52 | 53 | Worker::runAll(); 54 | -------------------------------------------------------------------------------- /examples/workerman/consumer_use_coroutine_client.php: -------------------------------------------------------------------------------- 1 | eventLoop = \Workerman\Events\Fiber::class; 14 | 15 | $worker->onWorkerStart = function() { 16 | // Create RabbitMQ Client 17 | $client = Client::factory([ 18 | 'host' => 'host.docker.internal', 19 | 'port' => 5672, 20 | 'user' => 'guest', 21 | 'password' => 'guest', 22 | 'vhost' => '/', 23 | 'heartbeat' => 60, 24 | 'heartbeat_callback' => function () { 25 | echo " [-] coroutine-consumer-heartbeat\n"; 26 | }, 27 | 'interval' => [100, 300] 28 | ])->connect(); 29 | $channel = $client->channel(); 30 | $channel->queueDeclare('hello-coroutine'); 31 | 32 | // Consumer 33 | $channel->consume(function (Message $message, Channel $channel, \Bunny\AbstractClient $client) { 34 | echo " [>] Received ", $message->content, "\n"; 35 | }, 36 | 'hello-coroutine', 37 | '', 38 | false, 39 | true 40 | ); 41 | $client->run(); 42 | echo ' [*] Waiting for messages. To exit press CTRL+C', "\n"; 43 | 44 | // Producer 45 | \Workerman\Timer::add($interval = 5 , function () use ($channel) { 46 | $channel->publish($message = 'Hello World By Self Timer. ' . time(), [], '', 'hello-coroutine'); 47 | echo " [<] Sent $message\n"; 48 | }); 49 | echo " [!] Producer timer created, interval: $interval s.\n"; 50 | 51 | }; 52 | Worker::runAll(); 53 | -------------------------------------------------------------------------------- /examples/workerman/consumer_use_eventloop_client.php: -------------------------------------------------------------------------------- 1 | onWorkerStart = function () { 15 | // Create RabbitMQ Client by promise 16 | $c = null; 17 | (new Client([ 18 | 'host' => 'host.docker.internal', 19 | 'port' => 5672, 20 | 'user' => 'guest', 21 | 'password' => 'guest', 22 | 'vhost' => '/', 23 | 'heartbeat' => 60, 24 | 'heartbeat_callback' => function () { 25 | echo " [-] eventloop-consumer-heartbeat\n"; 26 | } 27 | ]))->connect()->then(function (Client $client) { 28 | return $client->channel(); 29 | })->then(function (Channel $channel) use (&$c) { 30 | return $channel->queueDeclare('hello-eventloop')->then(function () use ($channel, &$c) { 31 | $c = $channel; 32 | return $channel; 33 | }); 34 | })->then(function (Channel $channel) { 35 | echo ' [*] Waiting for messages. To exit press CTRL+C', "\n"; 36 | // Consumer 37 | $channel->consume( 38 | function (Message $message, Channel $channel, Client $client) { 39 | echo " [>] Received ", $message->content, "\n"; 40 | }, 41 | 'hello-eventloop', 42 | '', 43 | false, 44 | true 45 | ); 46 | }); 47 | 48 | // Producer 49 | \Workerman\Timer::add($interval= 5, function () use (&$c) { 50 | if ($c) { 51 | $c->publish($message = 'Hello World By Self Timer. ' . time(), [], '', 'hello-eventloop'); 52 | echo " [<] Sent '$message'\n"; 53 | } 54 | }); 55 | echo " [!] Producer timer created, interval: $interval s.\n"; 56 | }; 57 | Worker::runAll(); -------------------------------------------------------------------------------- /examples/workerman/producer_in_workerman_use_coroutine_client.php: -------------------------------------------------------------------------------- 1 | eventLoop = \Workerman\Events\Fiber::class; 12 | 13 | $worker->onWorkerStart = function() { 14 | $client = Client::factory([ 15 | 'host' => 'host.docker.internal', 16 | 'port' => 5672, 17 | 'user' => 'guest', 18 | 'password' => 'guest', 19 | 'vhost' => '/', 20 | 'heartbeat' => 60, 21 | 'heartbeat_callback' => function () { 22 | echo "coroutine-producer-heartbeat\n"; 23 | } 24 | ])->connect(); 25 | $channel = $client->channel(); 26 | $channel->queueDeclare('hello-coroutine'); 27 | 28 | // 每5秒发一个消息 29 | \Workerman\Timer::add(5, function () use ($channel) { 30 | $channel->publish($message = 'Hello World By Workerman Env Producer. ' . time(), [], '', 'hello-coroutine'); 31 | echo " [x] Sent '$message'\n"; 32 | }); 33 | }; 34 | Worker::runAll(); 35 | -------------------------------------------------------------------------------- /examples/workerman/producer_in_workerman_use_eventloop_client.php: -------------------------------------------------------------------------------- 1 | eventLoop = \Workerman\Events\Fiber::class; 13 | 14 | $worker->onWorkerStart = function() { 15 | $c = null; 16 | (new Client([ 17 | 'host' => 'host.docker.internal', 18 | 'port' => 5672, 19 | 'user' => 'guest', 20 | 'password' => 'guest', 21 | 'vhost' => '/', 22 | 'heartbeat' => 60, 23 | 'heartbeat_callback' => function () { 24 | echo "eventloop-producer-heartbeat\n"; 25 | } 26 | ]))->connect()->then(function (Client $client) { 27 | return $client->channel(); 28 | })->then(function (Channel $channel) { 29 | return $channel->queueDeclare('hello-eventloop')->then(function () use ($channel) { 30 | return $channel; 31 | }); 32 | })->then(function (Channel $channel) use (&$c) { 33 | $c = $channel; 34 | }); 35 | 36 | // 每5秒发一个消息 37 | \Workerman\Timer::add(5, function () use (&$c) { 38 | if ($c) { 39 | $c->publish($message = 'Hello World By Workerman Env Producer. ' . time(), [], '', 'hello-eventloop'); 40 | echo " [x] Sent '$message'\n"; 41 | } 42 | }); 43 | }; 44 | Worker::runAll(); 45 | -------------------------------------------------------------------------------- /examples/workerman/producer_use_coroutine_client.php: -------------------------------------------------------------------------------- 1 | 'host.docker.internal', 11 | 'port' => 5672, 12 | 'user' => 'guest', 13 | 'password' => 'guest', 14 | 'vhost' => '/', 15 | 'heartbeat' => 60, 16 | 'heartbeat_callback' => function () { 17 | echo "coroutine-producer-heartbeat\n"; 18 | } 19 | ])->connect(); 20 | $channel = $client->channel(); 21 | $channel->queueDeclare('hello-coroutine'); 22 | $res = $channel->publish($message = 'Hello World By Normal Producer. ' . time(), [], '', 'hello-coroutine'); 23 | 24 | echo " [x] Sent '$message', success: $res\n"; 25 | -------------------------------------------------------------------------------- /src/Client.php: -------------------------------------------------------------------------------- 1 | [10, 100] 50 | * ] {@see AbstractClient::__construct()} and {@see \Workerman\RabbitMQ\Client::authResponse()} 51 | * @param LoggerInterface|null $logger 52 | */ 53 | public function __construct(array $options = [], LoggerInterface $logger = null) 54 | { 55 | // 安装了workerman 56 | if (InstalledVersions::isInstalled('workerman/workerman')) { 57 | // after workerman start 58 | if ($this->workerman = (Worker::$globalEvent !== null)) { 59 | $this->coroutine = in_array(Worker::$eventLoopClass, [ 60 | Fiber::class, Swow::class, Swoole::class 61 | ]) and InstalledVersions::isInstalled('workerman/coroutine'); 62 | } 63 | } 64 | // 设置出让间隔 65 | $this->interval = $this->options['interval'] ?? [10, 100]; 66 | // 日志 67 | $this->setLogger($logger); 68 | // 注册认证机制 69 | static::registerMechanismHandler('PLAIN', function (string $mechanism, MethodConnectionStartFrame $start) { 70 | return $this->connectionStartOk([], $mechanism, sprintf("\0%s\0%s", $this->options["user"], $this->options["password"]), "en_US"); 71 | }); 72 | static::registerMechanismHandler('AMQPLAIN', function (string $mechanism, MethodConnectionStartFrame $start) { 73 | $responseBuffer = new Buffer(); 74 | $this->writer->appendTable([ 75 | "LOGIN" => $this->options["user"], 76 | "PASSWORD" => $this->options["password"], 77 | ], $responseBuffer); 78 | 79 | $responseBuffer->discard(4); 80 | 81 | return $this->connectionStartOk([], $mechanism, $responseBuffer->read($responseBuffer->getLength()), "en_US"); 82 | }); 83 | parent::__construct($options); 84 | } 85 | 86 | /** @inheritdoc **/ 87 | public function __destruct() 88 | { 89 | $this->coroutine = false; 90 | parent::__destruct(); 91 | } 92 | 93 | /** 94 | * 协程出让 95 | * 96 | * @return void 97 | */ 98 | protected function sleep(): void 99 | { 100 | if ($this->workerman and $this->coroutine) { 101 | list($min, $max) = $this->interval; 102 | Timer::sleep(rand((int)$min, (int)$max) / 1000); 103 | } 104 | } 105 | 106 | /** @inheritdoc */ 107 | public function connect() 108 | { 109 | $res = parent::connect(); 110 | if ($this->workerman) { 111 | // 非阻塞 112 | stream_set_blocking($this->getStream(), 0); 113 | // 心跳 114 | $this->heartbeatTimer = Timer::add($this->options["heartbeat"], function () { 115 | $this->writer->appendFrame(new HeartbeatFrame(), $this->writeBuffer); 116 | $this->flushWriteBuffer(); 117 | if (is_callable( 118 | $this->options['heartbeat_callback'] ?? null 119 | )) { 120 | $this->options['heartbeat_callback']->call($this); 121 | } 122 | }); 123 | } 124 | return $res; 125 | } 126 | 127 | /** 128 | * @return void 129 | */ 130 | public function onDataAvailable(): void 131 | { 132 | $this->read(); 133 | while (($frame = $this->reader->consumeFrame($this->readBuffer)) !== null) { 134 | if ($frame->channel === 0) { 135 | $this->onFrameReceived($frame); 136 | 137 | } else { 138 | if (!isset($this->channels[$frame->channel])) { 139 | throw new ClientException( 140 | "Received frame #{$frame->type} on closed channel #{$frame->channel}." 141 | ); 142 | } 143 | 144 | $this->channels[$frame->channel]->onFrameReceived($frame); 145 | } 146 | } 147 | } 148 | 149 | /** @inheritdoc */ 150 | public function disconnect($replyCode = 0, $replyText = "") 151 | { 152 | if ($this->heartbeatTimer) { 153 | Timer::del($this->heartbeatTimer); 154 | } 155 | if ($this->workerman) { 156 | Worker::$globalEvent->offReadable($this->getStream()); 157 | } 158 | return parent::disconnect($replyCode, $replyText); 159 | } 160 | 161 | /** 162 | * Override to support PLAIN mechanism 163 | * @param MethodConnectionStartFrame $start 164 | * @return bool|Promise\PromiseInterface 165 | */ 166 | protected function authResponse(MethodConnectionStartFrame $start): Promise\PromiseInterface|bool 167 | { 168 | $mechanism = $this->options['mechanism'] ?? 'AMQPLAIN'; 169 | if (!str_contains($start->mechanisms, $mechanism)) { 170 | throw new ClientException("Server does not support $mechanism mechanism (supported: {$start->mechanisms})."); 171 | } 172 | // 认证机制 173 | if ($handler = static::getMechanismHandler($mechanism)) { 174 | return $handler($mechanism, $start); 175 | } 176 | throw new ClientException("Client does not support $mechanism mechanism. "); 177 | } 178 | 179 | /** 180 | * override flushWriteBuffer 181 | * 182 | * @inheritdoc 183 | */ 184 | protected function flushWriteBuffer(): bool 185 | { 186 | if ($this->workerman and $this->coroutine) { 187 | $barrier = Coroutine\Barrier::create(); 188 | Coroutine::create(function () use ($barrier) { 189 | while (!$this->writeBuffer->isEmpty()) { 190 | $this->write(); 191 | $this->sleep(); 192 | } 193 | }); 194 | Coroutine\Barrier::wait($barrier); 195 | return true; 196 | } else { 197 | return parent::flushWriteBuffer(); 198 | } 199 | } 200 | 201 | /** 202 | * override run 203 | * 204 | * @inheritdoc 205 | */ 206 | public function run($maxSeconds = null): void 207 | { 208 | $this->running = true; 209 | if ($this->workerman) { 210 | if (Worker::$eventLoopClass !== Fiber::class) { 211 | throw new \RuntimeException('CoroutineClient only support Fiber event loop'); 212 | } 213 | // 可读事件 214 | Worker::$globalEvent->onReadable($this->getStream(), [$this, 'onDataAvailable']); 215 | } 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /src/Clients/EventloopClient.php: -------------------------------------------------------------------------------- 1 | "127.0.0.1", 30 | * "port" => 5672, 31 | * "vhost" => "/", 32 | * "mechanism" => "AMQPLAIN" 33 | * "user" => "guest", 34 | * "password" => "guest", 35 | * "timeout" => 10, 36 | * "restart_interval" => 0, 37 | * "heartbeat" => 60, 38 | * "heartbeat_callback" => function(){} 39 | * ] {@see AbstractClient::__construct()} and {@see \Workerman\RabbitMQ\Client::authResponse()} 40 | * @param LoggerInterface|null $logger 41 | */ 42 | public function __construct(array $options = [], LoggerInterface $logger = null) 43 | { 44 | $options['async'] = true; 45 | $this->setLogger($logger); 46 | // 注册认证机制 47 | static::registerMechanismHandler('PLAIN', function (string $mechanism) { 48 | return $this->connectionStartOk([], $mechanism, sprintf("\0%s\0%s", $this->options["user"], $this->options["password"]), "en_US"); 49 | }); 50 | static::registerMechanismHandler('AMQPLAIN', function (string $mechanism) { 51 | $responseBuffer = new Buffer(); 52 | $this->writer->appendTable([ 53 | "LOGIN" => $this->options["user"], 54 | "PASSWORD" => $this->options["password"], 55 | ], $responseBuffer); 56 | 57 | $responseBuffer->discard(4); 58 | 59 | return $this->connectionStartOk([], $mechanism, $responseBuffer->read($responseBuffer->getLength()), "en_US"); 60 | }); 61 | AbstractClient::__construct($options); 62 | $this->eventLoop = Worker::$globalEvent; 63 | } 64 | 65 | /** 66 | * Asynchronously sends buffered data over the wire. 67 | * 68 | * - Calls {@link eventLoops}'s addWriteStream() with client's stream. 69 | * - Consecutive calls will return the same instance of promise. 70 | * 71 | * @return Promise\PromiseInterface 72 | */ 73 | protected function flushWriteBuffer(): Promise\PromiseInterface 74 | { 75 | if ($this->flushWriteBufferPromise) { 76 | return $this->flushWriteBufferPromise; 77 | 78 | } else { 79 | $deferred = new Promise\Deferred(); 80 | 81 | $streamFunction = function ($stream) use ($deferred) { 82 | try { 83 | $this->write(); 84 | 85 | if ($this->writeBuffer->isEmpty()) { 86 | $this->eventLoop->offWritable($stream); 87 | $this->flushWriteBufferPromise = null; 88 | $deferred->resolve(true); 89 | } 90 | 91 | } catch (\Exception $e) { 92 | $this->eventLoop->offWritable($stream); 93 | $this->flushWriteBufferPromise = null; 94 | $deferred->reject($e); 95 | } 96 | }; 97 | $this->eventLoop->onWritable($this->getStream(), $streamFunction); 98 | return $this->flushWriteBufferPromise = $deferred->promise(); 99 | } 100 | } 101 | 102 | /** 103 | * Override to support PLAIN mechanism 104 | * @param MethodConnectionStartFrame $start 105 | * @return bool|Promise\PromiseInterface 106 | */ 107 | protected function authResponse(MethodConnectionStartFrame $start): Promise\PromiseInterface|bool 108 | { 109 | $mechanism = $this->options['mechanism'] ?? 'AMQPLAIN'; 110 | if (!str_contains($start->mechanisms, $mechanism)) { 111 | throw new ClientException("Server does not support $mechanism mechanism (supported: {$start->mechanisms})."); 112 | } 113 | // 认证机制 114 | if ($handler = static::getMechanismHandler($mechanism)) { 115 | return $handler($mechanism, $start); 116 | } 117 | throw new ClientException("Client does not support $mechanism mechanism. "); 118 | } 119 | 120 | /** 121 | * Connects to AMQP server. 122 | * 123 | * Calling connect() multiple times will result in error. 124 | * 125 | * @return Promise\PromiseInterface 126 | */ 127 | public function connect(): Promise\PromiseInterface 128 | { 129 | if ($this->state !== ClientStateEnum::NOT_CONNECTED) { 130 | return Promise\reject(new ClientException("Client already connected/connecting.")); 131 | } 132 | 133 | $this->state = ClientStateEnum::CONNECTING; 134 | $this->writer->appendProtocolHeader($this->writeBuffer); 135 | 136 | try { 137 | $this->eventLoop->onReadable($this->getStream(), [$this, "onDataAvailable"]); 138 | } catch (\Exception $e) { 139 | return Promise\reject($e); 140 | } 141 | 142 | return $this->flushWriteBuffer()->then(function () { 143 | return $this->awaitConnectionStart(); 144 | 145 | })->then(function (MethodConnectionStartFrame $start) { 146 | return $this->authResponse($start); 147 | 148 | })->then(function () { 149 | return $this->awaitConnectionTune(); 150 | 151 | })->then(function (MethodConnectionTuneFrame $tune) { 152 | $this->frameMax = $tune->frameMax; 153 | if ($tune->channelMax > 0) { 154 | $this->channelMax = $tune->channelMax; 155 | } 156 | return $this->connectionTuneOk($tune->channelMax, $tune->frameMax, $this->options["heartbeat"]); 157 | 158 | })->then(function () { 159 | return $this->connectionOpen($this->options["vhost"]); 160 | 161 | })->then(function () { 162 | if (isset($this->options["heartbeat"]) && $this->options["heartbeat"] > 0) { 163 | $this->heartbeatTimer = Timer::add($this->options["heartbeat"], [$this, "onHeartbeat"]); 164 | } 165 | 166 | $this->state = ClientStateEnum::CONNECTED; 167 | return $this; 168 | 169 | }); 170 | } 171 | 172 | /** 173 | * Disconnects client from server. 174 | * 175 | * - Calling disconnect() if client is not connected will result in error. 176 | * - Calling disconnect() multiple times will result in the same promise. 177 | * 178 | * @param int $replyCode 179 | * @param string $replyText 180 | * @return Promise\PromiseInterface|null 181 | */ 182 | public function disconnect($replyCode = 0, $replyText = ""): ?Promise\PromiseInterface 183 | { 184 | if ($this->state === ClientStateEnum::DISCONNECTING) { 185 | return $this->disconnectPromise; 186 | } 187 | 188 | if ($this->state !== ClientStateEnum::CONNECTED) { 189 | return Promise\reject(new ClientException("Client is not connected.")); 190 | } 191 | 192 | $this->state = ClientStateEnum::DISCONNECTING; 193 | 194 | $promises = []; 195 | 196 | if ($replyCode === 0) { 197 | foreach ($this->channels as $channel) { 198 | $promises[] = $channel->close($replyCode, $replyText); 199 | } 200 | } 201 | else{ 202 | foreach($this->channels as $channel){ 203 | $this->removeChannel($channel->getChannelId()); 204 | } 205 | } 206 | 207 | if ($this->heartbeatTimer) { 208 | Timer::del((int)$this->heartbeatTimer); 209 | $this->heartbeatTimer = null; 210 | } 211 | 212 | return $this->disconnectPromise = Promise\all($promises)->then(function () use ($replyCode, $replyText) { 213 | if (!empty($this->channels)) { 214 | throw new \LogicException("All channels have to be closed by now."); 215 | } 216 | if($replyCode !== 0){ 217 | return null; 218 | } 219 | return $this->connectionClose($replyCode, $replyText, 0, 0); 220 | })->then(function () use ($replyCode, $replyText){ 221 | $this->eventLoop->offReadable($this->getStream()); 222 | $this->closeStream(); 223 | $this->init(); 224 | if ($replyCode !== 0) { 225 | // 触发重启事件回调 226 | if ($this->restartCallback) { 227 | return call_user_func($this->restartCallback, $this, $replyCode, $replyText); 228 | } 229 | // 默认重启流程 230 | else { 231 | // 延迟重启 232 | if (($restartInterval = $this->options['restart_interval'] ?? 0) > 0) { 233 | Worker::log("RabbitMQ client will restart in $restartInterval seconds. "); 234 | 235 | $timerFunction = function () use ($replyCode, $replyText, $restartInterval) { 236 | Worker::stopAll(0,"RabbitMQ client disconnected: [{$replyCode}] {$replyText}"); 237 | }; 238 | $this->eventLoop->delay($restartInterval, $timerFunction); 239 | return null; 240 | } 241 | // 立即重启 242 | else { 243 | Worker::stopAll(0,"RabbitMQ client disconnected: [{$replyCode}] {$replyText}"); 244 | } 245 | } 246 | } 247 | return $this; 248 | }); 249 | } 250 | 251 | /** 252 | * Callback when heartbeat timer timed out. 253 | */ 254 | public function onHeartbeat(): void 255 | { 256 | $this->writer->appendFrame(new HeartbeatFrame(), $this->writeBuffer); 257 | $this->flushWriteBuffer()->then( 258 | function () { 259 | if (is_callable( 260 | $this->options['heartbeat_callback'] ?? null 261 | )) { 262 | $this->options['heartbeat_callback']->call($this); 263 | } 264 | }, 265 | function (\Throwable $throwable){ 266 | $this->logger?->debug( 267 | 'OnHeartbeatFailed', 268 | [ 269 | $throwable->getMessage(), 270 | $throwable->getCode(), 271 | $throwable->getFile(), 272 | $throwable->getLine() 273 | ] 274 | ); 275 | Worker::stopAll(0,"RabbitMQ client heartbeat failed: [{$throwable->getCode()}] {$throwable->getMessage()}"); 276 | }); 277 | } 278 | 279 | /** 280 | * override run 281 | * 282 | * @param $maxSeconds 283 | * @return void 284 | */ 285 | public function run($maxSeconds = null) 286 | { 287 | 288 | } 289 | } 290 | -------------------------------------------------------------------------------- /src/Traits/LoggerMethods.php: -------------------------------------------------------------------------------- 1 | logger = $logger; 22 | } 23 | 24 | /** 25 | * @return LoggerInterface|null 26 | */ 27 | public function getLogger(): ?LoggerInterface 28 | { 29 | return $this->logger; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Traits/MechanismMethods.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | protected static array $mechanismHandlers = []; 13 | 14 | /** 15 | * @param string $mechanism 16 | * @param callable $handler 17 | */ 18 | public static function registerMechanismHandler(string $mechanism, callable $handler): void 19 | { 20 | self::$mechanismHandlers[$mechanism] = $handler; 21 | } 22 | 23 | /** 24 | * @return array 25 | */ 26 | public static function getMechanismHandlers(): array 27 | { 28 | return self::$mechanismHandlers; 29 | } 30 | 31 | /** 32 | * @param string $mechanism 33 | * @return callable|null 34 | */ 35 | public static function getMechanismHandler(string $mechanism): callable|null 36 | { 37 | return self::$mechanismHandlers[$mechanism] ?? null; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Traits/RestartMethods.php: -------------------------------------------------------------------------------- 1 | restartCallback = $callback; 25 | return $this; 26 | } 27 | 28 | /** 29 | * 移除重启回调 30 | * 31 | * @return Client 32 | */ 33 | public function unregisterRestartCallback(): Client 34 | { 35 | $this->restartCallback = null; 36 | return $this; 37 | } 38 | } 39 | --------------------------------------------------------------------------------