jeroMq示例之[2] [req-rep-envelopes msg identity]

Request-Reply Envelopes

在请求-应答模式中,信封里保存了应答目标的位置。这就是为什么ØMQ网络虽然是无状态的,但仍能完成请求-应答的过程。

在一般使用过程中,你并不需要知道请求-应答信封的工作原理。使用REQ、REP时,ØMQ会自动处理消息信封。下一章讲到的装置(device),使用时也只需保证读取和写入所有的信息即可。ØMQ使用多段消息的方式来存储信封,所以在复制消息时也会复制信封。

然而,在使用高级请求-应答模式之前是需要了解信封这一机制的,以下是信封机制在ROUTER中的工作原理:

  • 从ROUTER中读取一条消息时,ØMQ会包上一层信封,上面注明了消息的来源。
  • 向ROUTER写入一条消息时(包含信封),ØMQ会将信封拆开,并将消息递送给相应的对象。

如果将从ROUTER A中获取的消息(包含信封)写入ROUTER B(即将消息发送给一个DEALER,该DEALER连接到了ROUTER),那么在从ROUTER B中获取该消息时就会包含两层信封。

信封机制的根本作用是让ROUTER知道如何将消息递送给正确的应答目标,你需要做的就是在程序中保留好该信封。回顾一下REP套接字,它会将收到消息的信封逐个拆开,将消息本身传送给应用程序。而在发送时,又会在消息外层包裹该信封,发送给ROUTER,从而传递给正确的应答目标。

我们可以使用上述原理建立起一个ROUTER-DEALER装置:

[REQ] <--> [REP]
[REQ] <--> [ROUTER--DEALER] <--> [REP]
[REQ] <--> [ROUTER--DEALER] <--> [ROUTER--DEALER] <--> [REP]
...etc.

当你用REQ套接字去连接ROUTER套接字,并发送一条请求消息,你会从ROUTER中获得一条如下所示的消息:

1

  • 第三帧是应用程序发送给REQ套接字的消息;
  • 第二帧的空信息是REQ套接字在发送消息给ROUTER之前添加的;
  • 第一帧即信封,是由ROUTER套接字添加的,记录了消息的来源。

如果我们在一条装置链路上传递该消息,最终会得到包含多层信封的消息。最新的信封会在消息的顶部。

2

以下将详述我们在请求-应答模式中使用到的四种套接字类型:

  • DEALER是一种负载均衡,它会将消息分发给已连接的节点,并使用公平队列的机制处理接受到的消息。DEALER的作用就像是PUSH和PULL的结合。

  • REQ发送消息时会在消息顶部插入一个空帧,接受时会将空帧移去。其实REQ是建立在DEALER之上的,但REQ只有当消息发送并接受到回应后才能继续运行。

  • ROUTER在收到消息时会在顶部添加一个信封,标记消息来源。发送时会通过该信封决定哪个节点可以获取到该条消息。

  • REP在收到消息时会将第一个空帧之前的所有信息保存起来,将原始信息传送给应用程序。在发送消息时,REP会用刚才保存的信息包裹应答消息。REP其实是建立在ROUTER之上的,但和REQ一样,必须完成接受和发送这两个动作后才能继续。

REP要求消息中的信封由一个空帧结束,所以如果你没有用REQ发送消息,则需要自己在消息中添加这个空帧。

你肯定会问,ROUTER是怎么标识消息的来源的?答案当然是套接字的标识。我们之前讲过,一个套接字可能是瞬时的,它所连接的套接字(如ROUTER)则会给它生成一个标识,与之相关联。一个套接字也可以显式地给自己定义一个标识,这样其他套接字就可以直接使用了。

这是一个瞬时的套接字,ROUTER会自动生成一个UUID来标识消息的来源。

3

这是一个持久的套接字,标识由消息来源自己指定。

4

下面让我们在实例中观察上述两种操作。下列程序会打印出ROUTER从两个REP套接字中获得的消息,其中一个没有指定标识,另一个指定了“Hello”作为标识。

 

 1 package guide;
 2 
 3 import org.zeromq.ZMQ;
 4 import org.zeromq.ZMQ.Context;
 5 import org.zeromq.ZMQ.Socket;
 6 
 7 /**
 8  * Demonstrate identities as used by the request-reply pattern.
 9  */
10 public class identity {
11 
12     public static void main (String[] args) throws InterruptedException {
13 
14         Context context = ZMQ.context(1);
15         Socket sink = context.socket(ZMQ.ROUTER);
16         sink.bind("inproc://example");
17 
18         //  First allow 0MQ to set the identity, [00] + random 4byte
19         Socket anonymous = context.socket(ZMQ.REQ);
20 
21         anonymous.connect("inproc://example");
22         anonymous.send ("ROUTER uses a generated UUID",0);
23         ZHelper.dump (sink);
24 
25         //  Then set the identity ourself
26         Socket identified = context.socket(ZMQ.REQ);
27         identified.setIdentity("Hello".getBytes (ZMQ.CHARSET));
28         identified.connect ("inproc://example");
29         identified.send("ROUTER socket uses REQ's socket identity", 0);
30         ZHelper.dump (sink);
31 
32         sink.close ();
33         anonymous.close ();
34         identified.close();
35         context.term();
36 
37     }
38 }

 dump 出来三帧消息,第一帧为id

----------------------------------------
[005] 00F275910E
[000]
[028] ROUTER uses a generated UUID
----------------------------------------
[005] Hello
[000]
[040] ROUTER socket uses REQ's socket identity

posted @ 2014-09-15 15:33  ScottGu  阅读(345)  评论(0编辑  收藏  举报