返回顶部
首页 > 资讯 > 精选 >如何使用RabbitMQ实现RPC
  • 427
分享到

如何使用RabbitMQ实现RPC

2023-06-02 20:06:32 427人浏览 薄情痞子
摘要

这篇文章给大家分享的是有关如何使用RabbitMQ实现rpc的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。背景知识RabbitMQRabbitMQ 是基于 AMQP 协议实现的一个消息队列(Message Que

这篇文章给大家分享的是有关如何使用RabbitMQ实现rpc的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。

背景知识

RabbitMQ

RabbitMQ 是基于 AMQP 协议实现的一个消息队列(Message Queue),Message Queue 是一个典型的生产者/消费者模式。生产者发布消息,消费者消费消息,生产者和消费者之间是解耦的,互相不知道对方的存在。

如何使用RabbitMQ实现RPC

RPC

Remote Procedure Call:远程过程调用,一次远程过程调用的流程即客户端发送一个请求到服务端,服务端根据请求信息进行处理后返回响应信息,客户端收到响应信息后结束。

如何使用RabbitMQ实现RPC

如何使用 RabbitMQ 实现 RPC?

使用 RabbitMQ 实现 RPC,相应的角色是由生产者来作为客户端,消费者作为服务端。

但 RPC 调用一般是同步的,客户端和服务器也是紧密耦合的。即客户端通过 IP/域名和端口链接到服务器,向服务器发送请求后等待服务器返回响应信息。

但 MQ 的生产者和消费者是完全解耦的,那么如何用 MQ 实现 RPC 呢?很明显就是把 MQ 当作中间件实现一次双向的消息传递:

如何使用RabbitMQ实现RPC

客户端和服务端即是生产者也是消费者。客户端发布请求,消费响应;服务端消费请求,发布响应。

具体实现

MQ部分的定义

请求信息的队列

我们需要一个队列来存放请求信息,客户端向这个队列发布请求信息,服务端消费该队列处理请求。该队列不需要复杂的路由规则,直接使用 RabbitMQ 默认的 direct exchange 来路由消息即可。

响应信息的队列

存放响应信息的队列不应只有一个。如果存在多个客户端,不能保证响应信息被发布请求的那个客户端消费到。所以应为每一个客户端创建一个响应队列,这个队列应该由客户端来创建且只能由这个客户端使用并在使用完毕后删除,这里可以使用 RabbitMQ 提供的排他队列(Exclusive Queue):

channel.queueDeclare(queue:"", durable:false, exclusive:true, autoDelete:false, new HashMap<>())

并且要保证队列名唯一,声明队列时名称设为空 RabbitMQ 会生成一个唯一的队列名。

exclusive 设为 true 表示声明一个排他队列,排他队列的特点是只能被当前的连接使用,并且在连接关闭后被删除。

一个简单的 demo(使用 pull 机制)

我们使用一个简单的 demo 来了解客户端和服务端的处理流程。

发布请求

  • 编写代码前的一个小问题

我们在声明队列时为每一个客户端声明了独有的响应队列,那服务器在发布响应时如何知道发布到哪个队列呢?其实就是客户端需要告诉服务端将响应发布到哪个队列,RabbitMQ 提供了这个支持,消息体的 Properties 中有一个属性 reply_to 就是用来标记回调队列的名称,服务器需要将响应发布到 reply_to 指定的回调队列中。

解决了这个问题之后我们就可以编写客户端发布请求的代码了:

// 定义响应回调队列String replyQueueName = channel.queueDeclare("", false, true, false, new HashMap<>()).getQueue();// 设置回调队列到 PropertiesAMQP.BasicProperties properties = new AMQP.BasicProperties.Builder() .replyTo(replyQueueName) .build();String request = "request";// 发布请求channel.basicPublish("", "rpc_queue", properties, request.getBytes());

Direct reply-to:

RabbitMQ 提供了一种更便捷的机制来实现 RPC,不需要客户端每次都定义回调队列,客户端发布请求时将 replyTo 设为 amq.rabbitmq.reply-to ,消费响应时也指定消费 amq.rabbitmq.reply-to ,RabbitMQ 会为客户端创建一个内部队列

消费请求

接下来是服务端处理请求的部分,接收到请求后经过处理将响应信息发布到 reply_to 指定的回调队列:

// 服务端 Consumer 的定义public class RpcServer extends DefaultConsumer { public RpcServer(Channel channel) { super(channel); } @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws ioException { String msg = new String(body); String response = (msg + " Received"); // 获取回调队列名 String replyTo = properties.getReplyTo(); // 发布响应消息到回调队列 this.getChannel().basicPublish("", replyTo, new AMQP.BasicProperties(), response.getBytes()); }}...// 启动服务端 Consumerchannel.basicConsume("rpc_queue", true, new RpcServer(channel));

接收响应

客户端如何接收服务器的响应呢?有两种方式:1.轮询的去 pull 回调队列中的消息,2.异步的消费回调队列中的消息。我们在这里简单实现第一种方案。

GetResponse getResponse = null;while (getResponse == null) { getResponse = channel.basicGet(replyQueueName, true);}String response = new String(getResponse.getBody());

一个简单的基于 RabbitMQ 的 RPC 模型已经实现了,但这个 demo 并不实用,因为客户端每次发送完请求都要同步的轮询等待响应消息,只能每次处理一个请求。RabbitMQ 的 pull 模式效率也比较低。

实现一个完备可用的 RPC 模式需要做的工作还有很多,要处理的关键点也比较复杂,有句话叫不要重复造轮子,spring 已经实现了一个完备可用的 RPC 模式的库,接下来我们来了解一下。顺便在此给大家推荐一个Java架构方面的交流学习群:698581634,进群即可获取Java架构师资料:有Spring,mybatisNetty源码分析高并发、高性能、分布式微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系,群里一定有你需要的资料,大家赶紧加群吧。

Spring Rabbit 中的实现

和上面 demo 的 pull 模式一次只能处理一个请求相对应的:如何异步的接收响应并处理多个请求呢?关键点就在于我们需要记录请求和响应并将它们关联起来,RabbitMQ 也提供了支持,Properties 中的另一个属性 correlation_id 用来标识一个消息的唯一 id。

参考 spring-rabbit 中的 convertSendAndReceive 方法的实现,为每一次请求生成一个唯一的 correlation_id :

private final AtomicInteger messageTagProvider = new AtomicInteger();...String messageTag = String.valueOf(this.messageTagProvider.incrementAndGet());...message.getMessageProperties().setCorrelationId(messageTag);

并使用一个 ConcurrentHashMap 来维护 correlation_id 和响应信息的映射:

private final Map<String, PendingReply> replyHolder = new ConcurrentHashMap<String, PendingReply>();...final PendingReply pendingReply = new PendingReply();this.replyHolder.put(correlationId, pendingReply);

PendingReply 中有一个 BlockingQueue 存放响应信息,在发送完请求信息后调用 BlockingQueue 的 pull 方法并设置超时时间来获取响应:

private final BlockingQueue<Object> queue = new ArrayBlockingQueue<Object>(1);

public Message get ( long timeout , TimeUnit unit ) throws InterruptedException { Object reply = this . queue . poll ( timeout , unit ); return reply == null ? null : processReply ( reply

);

}

在获取响应后不论结果如何,都会将 PendingReply 从 replyHolder 中移除,防止 replyHolder 中积压超时的响应消息:

try { reply = exchangeMessages(exchange, routingKey, message, correlationData, channel, pendingReply,messageTag);} finally { this.replyHolder.remove(messageTag); ...}

响应信息是何时如何被放到这个 BlockingQueue 中的呢?看一下 RabbitTemplate 接收消息的地方:

public void onMessage(Message message) {String messageTag; if (this.correlationKey == null) { // using standard correlationId property messageTag = message.getMessageProperties().getCorrelationId(); } else { messageTag = (String) message.getMessageProperties() .getHeaders().get(this.correlationKey); } // 存在 correlation_id 才认为是RPC的响应信息,不存在时不处理 if (messageTag == null) { logger.error("No correlation header in reply"); return; } // 从 replyHolder 中取出 correlation_id 对应的 PendingReply PendingReply pendingReply = this.replyHolder.get(messageTag); if (pendingReply == null) { if (logger.isWarnEnabled()) { logger.warn("Reply received after timeout for " + messageTag); } throw new AmqpRejectAndDontRequeueException("Reply received after timeout"); } else { restoreProperties(message, pendingReply); // 将响应信息 add 到 BlockingQueue 中 pendingReply.reply(message); }}

以上的 spring 代码隐去了很多额外部分的处理和细节,只关注关键的部分。

至此一个完整可用的由 RabbitMQ 作为中间件实现的 RPC 模式就完成了。

总结

服务端

服务端的实现比较简单,和一般的 Consumer 的区别只在于需要将请求回复到 replyTo 指定的 queue 中并带上消息标识 correlation_id 即可

服务端的一点小优化

超时的处理是由客户端来实现的,那服务端有没有可以优化的地方呢?

答案是有的:如果我们的服务端处理比较耗时,如何判断客户端是否还在等待响应呢?

我们可以使用 passive 参数去检查 replyTo 的 queue 是否存在,因为客户端声明的是内部队列,客户端如果断掉链接了这个 queue 就不存在了,这时服务端就无需处理这个消息了。

客户端

客户端承担了更多的工作量,包括:

  • 声明 replyTo 队列(使用 amq.rabbitmq.reply-to 会简单很多)

  • 维护请求和响应消息(使用唯一的 correlation_id 来关联)

  • 消费服务端的返回

  • 处理超时等异常情况(使用BlockingQueue来阻塞获取)

好在 spring 已经实现了一套完备可靠的代码,我们在清楚了流程和关键点之后,可以直接使用 spring 提供的 RabbitTemplate ,无需自己实现。

使用 MQ 实现 RPC 的意义

通过 MQ 实现 RPC 看起来比客户端和服务器直接通讯要复杂一些,那我们为什么要这样做呢?或者说这样做有什么好处:

  1. 将客户端和服务器解耦:客户端只是发布一个请求到 MQ 并消费这个请求的响应。并不关心具体由谁来处理这个请求,MQ 另一端的请求的消费者可以随意替换成任何可以处理请求的服务器,并不影响到客户端。

  2. 减轻服务器的压力:传统的 RPC 模式中如果客户端和请求过多,服务器的压力会过大。由 MQ 作为中间件的话,过多的请求而是被 MQ 消化掉,服务器可以控制消费请求的频次,并不会影响到服务器。

  3. 服务器的横向扩展更加容易:如果服务器的处理能力不能满足请求的频次,只需要增加服务器来消费 MQ 的消息即可,MQ会帮我们实现消息消费的负载均衡

  4. 可以看出 RabbitMQ 对于 RPC 模式的支持也是比较友好地,

  5. amq.rabbitmq.reply-to , reply_to , correlation_id 这些特性都说明了这一点,再加上 spring-rabbit 的实现,可以让我们很简单的使用消息队列模式的 RPC 调用。

感谢各位的阅读!关于“如何使用RabbitMQ实现RPC”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!

--结束END--

本文标题: 如何使用RabbitMQ实现RPC

本文链接: https://lsjlt.com/news/231277.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 如何使用RabbitMQ实现RPC
    这篇文章给大家分享的是有关如何使用RabbitMQ实现RPC的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。背景知识RabbitMQRabbitMQ 是基于 AMQP 协议实现的一个消息队列(Message Que...
    99+
    2023-06-02
  • 利用RabbitMQ实现RPC(pyth
        RPC——远程过程调用,通过网络调用运行在另一台计算机上的程序的函数\方法,是构建分布式程序的一种方式。RabbitMQ是一个消息队列系统,可以在程序之间收发消息。利用RabbitMQ可以实现RPC。本文所有操作都是在CentOS7...
    99+
    2023-01-31
    RabbitMQ RPC pyth
  • 详解SpringBoot中使用RabbitMQ的RPC功能
    一、RabbitMQ的RPC简介 实际业务中,有的时候我们还需要等待消费者返回结果给我们,或者是说我们需要消费者上的一个功能、一个方法或是一个接口返回给我们相应的值,而往往大型的系统...
    99+
    2024-04-02
  • springboot+HttpInvoke如何实现RPC调用
    小编给大家分享一下springboot+HttpInvoke如何实现RPC调用,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!开始用springboot2+hession4实现RPC服务时,发现第一个服务可以调用成功,但第二...
    99+
    2023-06-29
  • 如何使用PHP和Swoole实现RPC远程调用
    如何使用PHP和Swoole实现RPC远程调用RPC(Remote Procedure Call)是一种远程调用的协议,可以让应用程序在不同计算机上进行函数调用。RPC通常被用于构建分布式系统,其可以让不同的微服务能够协同工作。在PHP和S...
    99+
    2023-05-14
    PHP rpc swoole
  • Python如何使用RPC
    本篇内容介绍了“Python如何使用RPC”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!主要内容所谓RPC,是远程过程调用(Remote P...
    99+
    2023-07-02
  • 详解如何利用PHP实现RPC
    目录1.什么是RPC2.从通信协议的层面3.从不同的开发语言和平台层面4.从调用过程来看5.常见的几种通信方式6.php实现简单的rpc1.目录结构2.rpc服务端3.rpc 客户端...
    99+
    2024-04-02
  • 如何在 Golang 中使用 RPC 实现文件上传?
    使用 rpc 实现文件上传:创建 rpc 服务器来处理文件上传请求,使用 net/rpc 包创建。创建 rpc 客户端来向服务器发起文件上传请求,使用 net/rpc 包创建,将文件序列...
    99+
    2024-05-13
    文件上传 rpc golang
  • SpringBoot中使用RabbitMQ的RPC功能案例分析
    这篇文章主要讲解了“SpringBoot中使用RabbitMQ的RPC功能案例分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“SpringBoot中使用RabbitMQ的RPC功能案例分析...
    99+
    2023-06-25
  • Golang如何实现简易的rpc调用
    这篇文章主要介绍“Golang如何实现简易的rpc调用”,在日常操作中,相信很多人在Golang如何实现简易的rpc调用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Golang如何实现简易的rpc调用”的疑...
    99+
    2023-07-05
  • Golang如何用RPC实现转发服务
    今天小编给大家分享一下Golang如何用RPC实现转发服务的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。首先,我们需要了解一...
    99+
    2023-07-06
  • laravel如何使用RabbitMQ
    这篇文章主要介绍“laravel如何使用RabbitMQ”,在日常操作中,相信很多人在laravel如何使用RabbitMQ问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”laravel如何使用RabbitMQ...
    99+
    2023-06-22
  • python如何通过protobuf实现rpc
    由于项目组现在用的rpc是基于google protobuf rpc协议实现的,所以花了点时间了解下protobuf rpc。rpc对于做分布式系统的人来说肯定不陌生,对于rpc不了解的童鞋可以自行goog...
    99+
    2022-06-04
    python protobuf rpc
  • node中如何实现RPC通信
    本篇内容主要讲解“node中如何实现RPC通信”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“node中如何实现RPC通信”吧!什么是RPC?RPC:Remote Procedure Call(远...
    99+
    2023-07-04
  • 使用spring boot如何实现对RabbitMQ进行整合
    使用spring boot如何实现对RabbitMQ进行整合?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。springboot集成RabbitMQ非常简单,如果...
    99+
    2023-05-31
    springboot rabbitmq
  • .NET Core中RabbitMQ使用死信队列如何实现
    本篇内容介绍了“.NET Core中RabbitMQ使用死信队列如何实现”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!在.NET ...
    99+
    2023-07-05
  • 如何在PHP中实现RPC远程调用?
    随着互联网的快速发展和云计算技术的广泛应用,分布式系统和微服务架构变得越来越普遍。在这样的背景下,远程过程调用(RPC)成为了一种常见的技术手段。RPC能够使得不同的服务在网络上实现远程调用,从而实现不同服务之间的互联操作,提高代码的复用性...
    99+
    2023-05-14
    PHP rpc 远程调用
  • 如何实现一个XML-RPC server/client
    本篇内容介绍了“如何实现一个XML-RPC server/client”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1 从大数据部...
    99+
    2023-06-02
  • rabbitmq削峰限流如何实现
    RabbitMQ的削峰限流可以通过以下方式实现:1. 预取(Prefetch)机制:可以设置每个消费者一次从队列中获取的消息数量。通...
    99+
    2023-10-09
    rabbitmq
  • 基于python如何实现rpc远程过程调用
    这篇文章主要介绍“基于python如何实现rpc远程过程调用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“基于python如何实现rpc远程过程调用”文章能帮助大家解决问题。一、主要内容所谓RPC,...
    99+
    2023-07-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作