在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
2019年12月10日10:05:54 原文:https://www.rabbitmq.com/tutorials/tutorial-six-php.html
远程过程调用(RPC)(使用php-amqplib)
先决条件本教程假定RabbitMQ 已在标准端口(5672)的本地主机上安装并运行。如果您使用其他主机,端口或凭据,则连接设置需要进行调整。 在哪里获得帮助如果您在阅读本教程时遇到困难,可以 通过邮件列表与我们联系。
在第二篇教程中,我们学习了如何使用工作队列在多个工作人员之间分配耗时的任务。 但是,如果我们需要在远程计算机上运行功能并等待结果怎么办?好吧,那是一个不同的故事。这种模式通常称为“ 远程过程调用”或“ RPC”。 在本教程中,我们将使用RabbitMQ构建RPC系统:客户端和可伸缩RPC服务器。由于我们没有值得分配的耗时任务,因此我们将创建一个虚拟RPC服务,该服务返回斐波那契数。 客户端界面为了说明如何使用RPC服务,我们将创建一个简单的客户端类。它将公开一个名为call的方法,该方法 发送RPC请求并阻塞,直到收到答案为止: $fibonacci_rpc = new FibonacciRpcClient(); $response = $fibonacci_rpc->call(30); echo ' [.] Got ', $response, "\n";
回调队列通常,通过RabbitMQ进行RPC很容易。客户端发送请求消息,服务器发送响应消息。为了接收响应,我们需要发送带有请求的“回调”队列地址。我们可以使用默认队列。让我们尝试一下: list($queue_name, ,) = $channel->queue_declare("", false, false, true, false); $msg = new AMQPMessage( $payload, array('reply_to' => $queue_name) ); $channel->basic_publish($msg, '', 'rpc_queue'); # ... then code to read a response message from the callback_queue ...
关联ID在上面介绍的方法中,我们建议为每个RPC请求创建一个回调队列。那是相当低效的,但是幸运的是有更好的方法-让我们为每个客户端创建一个回调队列。 这引起了一个新问题,在该队列中收到响应后,尚不清楚响应属于哪个请求。那就是使用correlation_id属性的时候 。我们将为每个请求将其设置为唯一值。稍后,当我们在回调队列中收到消息时,我们将查看该属性,并基于此属性将响应与请求进行匹配。如果我们看到未知的 related_id值,则可以放心地丢弃该消息-它不属于我们的请求。 您可能会问,为什么我们应该忽略回调队列中的未知消息,而不是因错误而失败?这是由于服务器端可能出现竞争状况。尽管可能性不大,但RPC服务器可能会在向我们发送答案之后但在发送请求的确认消息之前死亡。如果发生这种情况,重新启动的RPC服务器将再次处理该请求。这就是为什么在客户端上我们必须妥善处理重复的响应,并且理想情况下RPC应该是幂等的。 摘要我们的RPC将像这样工作:
放在一起斐波那契任务: function fib($n) { if ($n == 0) { return 0; } if ($n == 1) { return 1; } return fib($n-1) + fib($n-2); } 我们声明我们的斐波那契函数。它仅假定有效的正整数输入。(不要指望这种方法适用于大量用户,它可能是最慢的递归实现)。 我们的RPC服务器rpc_server.php的代码如下所示: <?php require_once __DIR__ . '/vendor/autoload.php'; use PhpAmqpLib\Connection\AMQPStreamConnection; use PhpAmqpLib\Message\AMQPMessage; $connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest'); $channel = $connection->channel(); $channel->queue_declare('rpc_queue', false, false, false, false); function fib($n) { if ($n == 0) { return 0; } if ($n == 1) { return 1; } return fib($n-1) + fib($n-2); } echo " [x] Awaiting RPC requests\n"; $callback = function ($req) { $n = intval($req->body); echo ' [.] fib(', $n, ")\n"; $msg = new AMQPMessage( (string) fib($n), array('correlation_id' => $req->get('correlation_id')) ); $req->delivery_info['channel']->basic_publish( $msg, '', $req->get('reply_to') ); $req->delivery_info['channel']->basic_ack( $req->delivery_info['delivery_tag'] ); }; $channel->basic_qos(null, 1, null); $channel->basic_consume('rpc_queue', '', false, false, false, false, $callback); while ($channel->is_consuming()) { $channel->wait(); } $channel->close(); $connection->close(); 服务器代码非常简单:
我们的RPC客户端rpc_client.php的代码: <?php require_once __DIR__ . '/vendor/autoload.php'; use PhpAmqpLib\Connection\AMQPStreamConnection; use PhpAmqpLib\Message\AMQPMessage; class FibonacciRpcClient { private $connection; private $channel; private $callback_queue; private $response; private $corr_id; public function __construct() { $this->connection = new AMQPStreamConnection( 'localhost', 5672, 'guest', 'guest' ); $this->channel = $this->connection->channel(); list($this->callback_queue, ,) = $this->channel->queue_declare( "", false, false, true, false ); $this->channel->basic_consume( $this->callback_queue, '', false, true, false, false, array( $this, 'onResponse' ) ); } public function onResponse($rep) { if ($rep->get('correlation_id') == $this->corr_id) { $this->response = $rep->body; } } public function call($n) { $this->response = null; $this->corr_id = uniqid(); $msg = new AMQPMessage( (string) $n, array( 'correlation_id' => $this->corr_id, 'reply_to' => $this->callback_queue ) ); $this->channel->basic_publish($msg, '', 'rpc_queue'); while (!$this->response) { $this->channel->wait(); } return intval($this->response); } } $fibonacci_rpc = new FibonacciRpcClient(); $response = $fibonacci_rpc->call(30); echo ' [.] Got ', $response, "\n"; 现在是时候看看rpc_client.php和rpc_server.php的完整示例源代码了 。 我们的RPC服务现已准备就绪。我们可以启动服务器: php rpc_server.php # => [x] Awaiting RPC requests 要请求斐波那契编号,请运行客户端: php rpc_client.php # => [x] Requesting fib(30) 这里介绍的设计不是RPC服务的唯一可能的实现,但是它具有一些重要的优点:
我们的代码仍然非常简单,并且不会尝试解决更复杂(但很重要)的问题,例如:
|
2022-08-30
2022-08-15
2022-08-17
2022-11-06
2022-08-18
请发表评论