在项目中引入RabbitMQ通常会考虑它会带来的好处:解耦应用程序,实现不同编程语言之间的互通,解除对特定通信协议的依赖,解除应用程序在时序上执行的依赖(异步).落实到代码层面就是两种常用应用模式:"发后即忘"(fire-and-forget)和RPC.

 

fire-and-forget 

 

  之前提到过[链接],RabbitMQ解决的是应用程序之间互联(connect)和规模(scale)的问题,消息发送和接收是隔离,发送方不知道消息最终由谁接收,接收方也不必关心消息是谁步发出的;发送和接收是隔离的,消息本质上就是异步的.这种隔离也就解耦了应用程序之间的依赖.RabbitMQ的角色就是应用程序中间的路由器.对于消息的发布方来讲这是一种"发后即忘"(fire-and_forget)的发布方式.
  
  fire-and-forget模式发送消息,消息的发送方和接收方彼此隔离,但是如果是RPC调用,如何实现呢?!
  

RPC

 
    RPC需要双向通信,或者说RPC Server需要明确知道要把消息发送给谁.我们可以在payload的数据部分附加 "发给谁" 这种EndPoint信息. RabbitMQ提供的解决方案:在每一个AMQP的消息头上有一个reply_to字段.这样消息的producer就可以指定Queue name,RPC Server接受到消息检查reply_to字段,创建一个消息包含Response并把queue name作为routing key,订阅了这个队列的Client就拿到了消息.

    这里有两件事情要保证:1.要为队列创建随机Name 2.即使Name随机还是有可能冲突,还需要保证消息通信的独占性,看看RabbitMQ是怎么满足这两点的:

 [1] 之前提到过,如果创建的队列不指定queue name,RabbitMQ就会创建一个随机的Name.

 [2] 独占只需要exclusive参数即可

   总而言之,需要做的就是Client创建一个temporary,exclusive,anonymou的queue,并把queue name设置在RPC 消息的reply_to字段即可.注意这里RPC Server已经知道要投递到哪个Queue,所以不需要指定Exchange(后面我们会提到在实现层面Queue和Exchange的不同,简单讲queue会有对应的Erlang进程,而exchang只是执行一些模式匹配的检查并没有进程实体对应).看下图:

 

 

略有不同

 

  传统的RPC调用Client和Server紧密依赖,客户端连接上服务器,发送一个请求然后阻塞等待服务器响应.这样的做的特点是客户端和服务器端是知道对方的.如果RPC Server崩溃掉,客户端需要重连,如果Server彻底崩掉就要重新找一个提供同样服务的Server,然后客户端重连过去.

 

   用RabbitMQ来实现RPC,依然保持Client Server信息隐藏的特点,Client依赖的不是特定的Server而是特定的消息,在有多个等效Server的情况下,一个Server的状态是否正常不会影响到客户端的状态.

  总结一下,使用RabbitMQ是先RPC,客观上还实现了下面的效果:

  1. 容错 一个Server崩溃不影响 Client
  2. 解耦了对特定通信协议和接口的依赖,统一走AMQP消息.
  3. 在多个RPC Server之间的负载均衡由RabbitMQ完成

 

RabbitMQ 入门文档中专门提到了RPC的场景,Python和Java版本的代码都有,很容易写一个.net版本这里不再堆叠,请移步这里:

 http://www.rabbitmq.com/tutorials/tutorial-six-python.html

 

 

  

插播几条关于技术选型,有几个Nosql相关的旧闻,值得借鉴:

 

 NoSQL不是万能的,Twitter重新审视Cassandra使用策

 

 Hadoop不是万能的

 

 Basho:少一些谎言,多一些实践

 

 

最后贴小图一张,电影 柔道龙虎榜 里面的采儿