ZMQ多部分冲洗器:是否可以通过ZeroMQ接收通过多部分消息接收的多部分总数而不读取全部内容?

时间:2019-09-26 14:46:27

标签: c networking zeromq distributed-system low-latency

我正在使用多部分消息传递在C中使用ZeroMQ实现简单的 REQ-REP 模式。我的大部分邮件都严格分为四个部分(往返),但有一些例外。要执行该规则,我需要确定收到的多部分邮件的部分总数。知道是否小于等于4很容易。这是我的接收器功能:

#define BUFMAX  64 // Maximum size of text buffers
#define BUFRCV  63 // Maximum reception size of text buffers (reserve 1 space to add a terminal '\0')

char mpartstr[4][BUFMAX];


int recv_multi(void *socket,int *aremore)

// Receive upto the first 4 parts of a multipart message into mpartstr[][].
// Returns the number of parts read (upto 4) or <0 if there is an error.
// Returns -1 if there is an error with a zmq function.
// It sets aremore=1 if there are still more parts to read after the fourth
// part (or aremore=0 if not).
{
 int len,rc,rcvmore,pdx,wrongpard=0;
 size_t rcvmore_size = sizeof(rcvmore);

 pdx=0;
 len=zmq_recv(socket, mpartstr[pdx], BUFRCV, 0);
 if(len==-1) return len;

 mpartstr[pdx][len]='\0';
 rc=zmq_getsockopt(socket,ZMQ_RCVMORE,&rcvmore,&rcvmore_size); if(rc) return -1;

 pdx++;
 if(rcvmore==0){*aremore=0; return pdx;}

 while(rcvmore){
   len=zmq_recv (socket, mpartstr[pdx], BUFRCV, 0); if(len==-1) return len; mpartstr[pdx][len]='\0';
   rc=zmq_getsockopt(socket,ZMQ_RCVMORE,&rcvmore,&rcvmore_size); if(rc) return -1; 
   pdx++;
   if(pdx==4) break;
 }

 *aremore=rcvmore;
 return pdx;
}

很好。但是现在在我的main()函数中,我通过查看aremore的值来检查是否还有更多部分。在那些我不期望更多的情况下,我会将错误消息发送回发件人,但我发现,如果我不阅读多部分消息的所有部分,则ZeroMQ不喜欢它(它会读取其余部分下次我再次调用zmq_recv()函数时,即使在我发送一条消息并期望有一个新的干净的多部分响应后,我也会再次调用此旧的多部分消息的一部分。

因此,我真正需要的是一种“冲洗”功能,用于清除消息的剩余部分,该消息包含要丢弃的4个以上部分。到目前为止,我唯一需要做的就是这样一个丑陋的任意蛮力用尽函数({aremore的值开头为1-它是由上一个函数设置的):

int recv_exhaust(void *socket,int *aremore)

// Receive the remainder of a multipart message and discard the contents.
// Use this to clean out a multi-part 'inbox' from a wrongly sent message.
// Returns 0 on success
// Returns -1 on zmq function failure
// Returns -2 on failure to exhaust even after 1000 parts.
{                                                          
 int len,rc,rcvmore,pdx;
 size_t rcvmore_size = sizeof(rcvmore);

 pdx=1;
 rcvmore=*aremore;

 while(rcvmore){
   len=zmq_recv(socket, mpartstr[0], BUFRCV, 0); if(len==-1) return len;
   rc=zmq_getsockopt(socket,ZMQ_RCVMORE,&rcvmore,&rcvmore_size); if(rc) return -1; 
   pdx++;
   if(pdx>1000) return -2;
 }

 return 0;
}

如果没有专用的“冲洗器” API,那么如果我能够提前知道给定的多部分消息有多少部分,那么至少我可以摆脱任意的1000条消息限制。 ZeroMQ当然知道这一点,因为多部分消息是作为一个整体发送的。谁能指出我找到该信息的方式?还是那里有适当的“冲洗器”功能/方法? (对于标准C,请使用-而不是C ++ / C#等)。预先感谢。

1 个答案:

答案 0 :(得分:1)

  

Q 有人可以指出我的查找方式吗?

是的

  

Q 那里是否有适当的“冲洗器”功能/方法?

是和否:

直到ZeroMoQ v2.x到v4.3.1为止,都没有对“冲洗器”的显式API调用

ZeroMQ设计提供的低延迟智能消息传递的美丽和力量是基于精心设计的Zen-of-Zero:始终偏爱性能而不是舒适性-零复制,零保修和其他范式表明

天真(我为此付出了很多辛苦,只能求助于使用原始的recv()...)“冲洗器”必须一直持续到 ZMQ_RCVMORE < / strong>不会在多帧最后消息(或zmq_msg_more() == 0确实符合)之外对NACK标记“超出”更多部分。尽管如此,所有这些操作仅执行指针处理,没有数据从RAM进行“移动/复制/读取”,仅分配了指针,因此它确实既快速又具有I / O效率:

int    more;
size_t more_size = sizeof ( more );
do {
      zmq_msg_t part;                       /* Create an empty ØMQ message to hold the message part */
      int rc = zmq_msg_init (&part);           assert (rc == 0 && "MSG_INIT failed" );
      rc = zmq_msg_recv (&part, socket, 0); /* Block until a message is available to be received from socket */
                                               assert (rc != -1 && "MSG_RECV failed" );
                                            /* Determine if more message parts are to follow */
      rc = zmq_getsockopt (socket, ZMQ_RCVMORE, &more, &more_size);
                                               assert (rc == 0 && "GETSOCKOPT failed" );
      zmq_msg_close (&part);
      } while (more);

鉴于 RFC-23 / ZMTP 文件中记载的属性,只有少数(有线级遥测编码)担保:

1):所有邮件均已发送/发送:

  • 原子上(要么是无错误的二进制相同的所有帧,要么完全没有)
  • 最多一次(每个相关对等方)
  • 顺序

2)多部分消息还获得内部(带内)遥测状态的“建议”

  • 带有标记的状态 { 1: more-frames-follow| 0: no-more-frames }
  • 标记为 size-type { 0: 8b-direct-octet | 1: 64b-"network-endian"-coded }
  • 的位
  • 大小 的建议{ 0~255: direct-size | 0~2^63-1: 64b-"network-endian"-coded-size }

已记录的zmq_recv() API与此类似:

  

多部分邮件   

  ØMQ消息由1个或多个消息部分组成。每个消息部分本身就是一个独立的 zmq_msg_t 。 ØMQ确保消息的原子传递:对等方应该接收消息的所有消息部分,或者根本不接收消息。 消息部分的总数不受可用存储空间的限制。   

  处理多部分消息的应用程序在调用zmq_msg_recv()确定是否还有其他部分要接收之后,必须使用ZMQ_RCVMORE zmq_getsockopt(3)选项。


无论在初读时看起来多么“丑陋”,内存中最坏的情况都是在多部分消息帧中存储大量的小消息。

确定的“摆脱错误”时间不是零,但是,紧凑高效的内部ZMTP遥测和低延迟流处理的好处是更重要的目标(并且已经实现)。

如有疑问,请首先对最坏情况进行基准测试:

a)“产生”大约1E9多部分消息帧,传输零大小的有效载荷(无数据,但所有消息框架)

b)“设置”是最简单的“拓扑” PUSH/PULL

c)“选择”您选择的 运输等级 -最佳无堆叠 { inproc:// | ipc:// | tipc:// | ... | vmci:// } (我将对此进行压力测试)

d)秒表,当inproc://进行POSACK验证时,在 ReferencePoint-S: 之间盲目机械零快捷方式“刷新” zmq_poll( ZMQ_POLLIN ) ,这是由多部分组成的多部分消息中的最后一个被盲目的“冲洗器”马戏团所环绕。


结果解释:

ReferencePoint-E: [S] 之间花费的纳秒级确实是金额最坏情况的证据“替罪羊” 进入了一个已知的“冲动”循环马戏团的时间。在现实的用例中,还有其他原因可能会花费更多的时间进行相同的操作。

不过, 责任发送这种{已知大小的|格式不正确的}-多帧BEAST )是处理操作风险的根本原因,否则,延迟(几乎是线性的)可扩展性消息传递/信令框架。

零之禅的艺术使这种情况成为可能。多亏了MartinSÚSTRIK领导的Pieter HINTJENS和他的团队,我们都为能够继续处理他们的遗产而深表歉意。