一.  ZeroMQ概述

ZeroMQ是一种基于消息队列的多线程网络库,其对套接字类型、连接处理、帧、甚至路由的底层细节进行抽象,提供跨越多种传输协议的套接字。ZeroMQ是网络通信中新的一层,介于应用层和传输层之间(按照TCP/IP划分),其是一个可伸缩层,可并行运行,分散在分布式系统间。

ZeroMQlooks like an embeddable networking library but acts like a concurrency framework. It gives you sockets that carry atomic messages across various transports like in-process, inter-process, TCP, and multicast. You can connect sockets N-to-N with patterns like fan-out, pub-sub, task distribution, and request-reply. It's fast enough to be the fabric for clustered products. Its asynchronous I/O model gives you scalable multicore applications, built as asynchronous message-processing tasks. It has a score of language APIs and runs on most operating systems.

ZMQ对系统调用进行封装,屏蔽了底层技术细节,实现了进程内、进程间、TCP和广播通信,可采用多种消息模型实现N-M通信。

针对C语言应用有两类库可应用,基础的libzmq和在基础库上封装的czmq。CZMQ对ZMQ进一步封装成class,并提供额外的功能封装。如下主要介绍标准的ZMQ。

二.  消息模型

2.1 消息模型基础

创建一个context时同步创建了一个I/O线程,其在后台处理I/O。线程数量的设定原则是每秒一GB数据的进出需要一个线程。对于大多数程序,一个线程足够了。无论是发送消息还是接收消息,ZMQ都会先将消息放入队列中,并保证进程不会因为内存溢出而崩溃,适时地将消息写入磁盘。

2.2四种核心消息模型

  • 请求回应模型,Request-reply, which connects a set of clients to a set of services. This is a remote procedure call and task distribution pattern.

  • 发布订阅模型,Pub-sub, which connects a set of publishers to a set of subscribers. This is a data distribution pattern.

  • 流水线模型,Pipeline, which connects nodes in a fan-out/fan-in pattern that can have multiple steps and loops. This is a parallel task distribution and collection pattern.

  • 一对一结对模型,Exclusive pair, which connects two sockets exclusively. This is a pattern for connecting two threads in a process, not to be confused with "normal" pairs of sockets.

在socket的connect-bind应用中,消息模型可以根据需要组合使用:

  • PUB and SUB
  • REQ and REP
  • REQ and ROUTER (take care, REQ inserts an extra null frame)
  • DEALER and REP (take care, REP assumes a null frame)
  • DEALER and ROUTER
  • DEALER and DEALER
  • ROUTER and ROUTER
  • PUSH and PULL
  • PAIR and PAIR

除上述组合外,其他组合模式不支持。

2.3 socket类型

与通用socket不同

l  通用socket是同步接口,zmq的socket是异步消息队列。

l  通用socket传输字节流stream或数据报datagrams,zmq的socket传输分散的messages。

l  zmq的socket自动处理网络检测与重连。

l  通用socket支持1-1或1-N,zmq的socket只是N-N(不包括ZMQ_PAIR)。

zmq_socket()用于创建socket,绑定特定的socket类型,socket类型决定了socket通信的规则。不同消息模型支持不同的socket类型,常用socket类型如下,以消息类型分类如下:参考:http://api.zeromq.org/4-2:zmq-socket

请求回复模型

ZMQ_REQ:client request,仅允许一问(zmq_send)一答(zmq_recv),同步。This socket type allows only an alternating sequence of zmq_send(request) and subsequent zmq_recv(reply) calls. Each request sent is round-robined among all services, and each reply received is matched with the last issued request.若服务不可用,阻塞。不会删除messages。

zmq对ZMQ_REQ消息封装成如下格式,在消息数据前有空的分割帧:

ZMQ_REP:server reply,同步。This socket type allows only an alternating sequence of zmq_recv(request) and subsequent zmq_send(reply) calls. Each request received is fair-queued from among all clients, and each reply sent is routed to the client that issued the last request. 假如请求者不再存在,删除回复。

zmq对ZMQ_REP消息封装成如下格式:

ZMQ_DEALER:扩展了request/reply,异步,Each message sent is round-robined among all connected peers, and each message received is fair-queued from all connected peers.轮询发送,公平队列接收。HWM时阻塞,删除消息。ZMQ_DEALER连接到ZMQ_REP时,发送的消息包含空消息部分(用作分割消息主体),后跟消息主体部分。对消息无封装,都是原始帧。

ZMQ_ROUTER:扩展了request/reply,异步,接收消息时,会在消息主体前增加id部分,然后发送给应用。公平队列接收;发送时根据消息id路由(删除消息的第一部分即id然后发送)。ZMQ_REQ连接ZMQ_ROUTER时,ZMQ_ROUTER接收到的消息包含id、分割部分和主体,ZMQ_ROUTER发送给ZMQ_REQ的消息应包含分割部分(delimiter)。对消息无封装,都是原始帧。

Think of REQ and DEALER sockets as "clients" and REP and ROUTER sockets as "servers". Mostly, you'll want to bind REP and ROUTER sockets, and connect REQ and DEALER sockets to them. It's not always going to be this simple, but it is a clean and memorable place to start.

发布订阅模型

ZMQ_PUB:分发消息到所有订阅者,不提供zmq_recv()函数(只发送)。在mute状态下(超过阈值HWM),ZMQ_PUB将丢弃所有发向指定订阅者的消息。绝不会阻塞。

ZMQ_SUB:订阅消息。须通过zmq_setsockopt()的ZMQ_SUBSCRIBE指定订阅选项。不提供zmq_send()函数(只接收)。

ZMQ_XPUB:和ZMQ_PUB等同,除了一点:可以接收订阅信息。 Subscription message is a byte 1 (for subscriptions) or byte 0 (for unsubscriptions) followed by the subscription body. Messages without a sub/unsub prefix are also received, but have no effect on subscription status. ZMQ_XPUB主要(或只)应用在PUB与SUB间的proxy中。

ZMQ_XSUB:和ZMQ_SUB等同,除了一点:可以发送订阅信息。Subscription message is a byte 1 (for subscriptions) or byte 0 (for unsubscriptions) followed by the subscription body. Messages without a sub/unsub prefix may also be sent, but have no effect on subscription status. ZMQ_XSUB主要(或只)应用在PUB与SUB间的proxy中。

流水线模型

The pipeline pattern is used for distributing data to nodes arranged in a pipeline. Data always flows down the pipeline, and each stage of the pipeline is connected to at least one node. When a pipeline stage is connected to multiple nodes data is round-robined among all connected nodes.

流水线模式包含多个阶段,每个阶段至少连接一个node。当一个阶段连接多个node时,采用轮询分发数据。

ZMQ_PUSH:下发消息,轮询分发消息。不提供zmq_recv()(只发送)。处于mute状态时或没有node时,阻塞,删除消息。

ZMQ_PULL:公平队列接收上游消息。不提供zmq_send()(只接收)。

一对一结对模型

ZMQ_PAIR:用于进程内线程间一对一通信。没有消息路由或过滤机制。

三.  安装

可参考https://github.com/zeromq/czmq安装部署zmq和czmq。

安装依赖

sudo apt-get install -y \
git build-essential libtool \
pkg-config autotools-dev autoconf automake cmake \
uuid-dev libpcre3-dev libsodium-dev valgrind

安装zmq

git clone git://github.com/zeromq/libzmq.git
cd libzmq
./autogen.sh
# do not specify "--with-libsodium" if you prefer to use internal tweetnacl security implementation (recommended for development)
./configure --with-libsodium
make check
sudo make install
sudo ldconfig

安装czmq

git clone git://github.com/zeromq/czmq.git
cd czmq
./autogen.sh && ./configure && make check
sudo make install
sudo ldconfig 

四.  编程应用

zmq将通信实体抽象为socket和message,zmq编程主要是应用socket和message API。

注意:sockets是空指针类型(void pointers),而messages是结构体。因此,C中调用socket直接使用变量即可,但调用message需要使用地址(引用)。ZMQ中多有套接字都由ZMQ管理,只有消息是由程序员管理的。

3.1 socket API

zmq socket API类似BSD sockets:

创建和关闭:zmq_socket(),zmq_close()

配置socket:zmq_setsockopt(),zmq_getsockopt()

绑定连接:zmq_bind(),zmq_connect()

收发消息:zmq_send(),zmq_recv(),zmq_msg_send(),zmq_msg_recv()

注:绑定连接的地址形如:transport://address,支持的transport有tcp、ipc、inproc和pgm或epgm,如下示例:

tcp://*:5555
udp://192.168.1.1:5555
ipc:///tmp/feeds/0
inproc://somename

3.2 message API

有两类message API,简单的为zmq_send()和zmq_recv(),只适用于简单消息(消息被截断到所提供的的buffer大小;复杂的消息API基于zmq_msg_t结构体,有如下API:

  • Initialise a message:

zmq_msg_init()zmq_msg_init_size()zmq_msg_init_data().

zmq_msg_data()zmq_msg_size()zmq_msg_more().

需要注意的是,当你将一个消息对象传递给zmq_send()函数后,该对象的长度就会被清零,因此你无法发送同一个消息对象两次,也无法获得已发送消息的内容。

可以发送0字节长度的消息,作为一种信号。

3.3 CZMQ

基础版本zmq有些需要改进的地方,以便代码更易使用和阅读:

l  自动处理套接字。每次都要手动关闭套接字是很麻烦的事,手动定义过期时间也不是太有必要,所以,如果能在关闭上下文时自动关闭套接字就太好了。

l  便捷的线程管理。基本上所有的ØMQ应用都会用到多线程,但POSIX的多线程接口不可移植,所以也可以封装一下。

l  便捷的时钟管理。想要获取毫秒数、或是暂停运行几毫秒都不太方便,我们的API应该提供这个接口。

l  一个能够替代zmq_poll()的反应器。poll循环很简单,但比较笨拙,会造成重复代码:计算时间、处理套接字中的信息等。若有一个简单的反应器来处理套接字的读写以及时间的控制,将会很方便。

l  恰当地处理Ctrl-C按键。我么已经看到如何处理中断了,最好这一机制可以用到所有的程序里。

CZMQ实现了上述需求,(采用对象模型)提供了ZMQ的上层封装,甚至是数据结构(hashes和lists)。

五.  应用示例

zmq提供了多个语言版本的应用示例,可通过如下命令获取:

git clone --depth= https://github.com/imatix/zguide.git
#include <zmq.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h> int main(int argc, char *argv[])
{
void *ctx = zmq_ctx_new();
void *subscriber = zmq_socket(ctx, ZMQ_SUB); int rc = zmq_connect(subscriber, "tcp://localhost:5563");
assert(rc == ); rc = zmq_setsockopt(subscriber, ZMQ_SUBSCRIBE, "", );
assert(rc == ); while(){
char buffer[];
int size = zmq_recv(subscriber, buffer, , );
if(size == -){
printf("Receive error.\n");
exit(-);
}
buffer[size] = '\0'; printf("[%s]\n", buffer);
} zmq_close(subscriber);
zmq_ctx_destroy(ctx); return ;
}

go版本应用

package main

import (
"fmt"
"os" zmq "github.com/pebbe/zmq4"
) var zmq_addr string = "tcp://localhost:5563" func zmq_sub() {
ctx, _ := zmq.NewContext()
defer ctx.Term() fmt.Println("Starting sub...")
q, _ := zmq.NewSocket(zmq.SUB)
defer q.Close() fmt.Println("Connecting to incoming 0MQ at: " + zmq_addr )
err := q.Connect(zmq_addr)
if err != nil {
fmt.Println("Connect error")
os.Exit(-)
}
fmt.Println("Connect successfully")
q.SetSubscribe("") for {
msg, err := q.RecvMessage()
if err != nil {
id, _ := q.GetIdentity()
fmt.Printf("Error getting message %s", id)
} else {
for _, str := range msg {
fmt.Println(str)
}
}
}
} func main(){
zmq_sub()
}

参考:

1.   http://zguide.zeromq.org/page:all zmq指导文档

2.   http://api.zeromq.org/4-3:_start v4.3.1版本API

3.   https://github.com/zeromq/libzmq zmq github

4.   https://github.com/zeromq/czmq czmq github

5.   http://czmq.zeromq.org/   CZMQ

6.   ZeroMQ研究与应用分析

7.   https://www.cnblogs.com/fengbohello/tag/zeromq/ 中文API文档 博客

8.   https://github.com/anjuke/zguide-cn 中文zguide文档  基于2.1.0

9.   https://github.com/pebbe/zmq4 zmq golang 绑定 edgex中应用

ZMQ应用的更多相关文章

  1. ZeroMQ(ZMQ)函数接口英汉直译

    找了好多地方都找不到ZMQ接口函数的中文文档,就厚着脸皮自己翻译了下.但因为作者本人涉世未深,翻译有错误的地方还请大家不吝赐教,在下感激不尽. 因为时间有限,只能一点一点翻译了. ZMQ接口文档的官方 ...

  2. ZeroMQ接口函数之 :zmq - 0MQ 轻量级消息传输内核

    官方网址:http://api.zeromq.org/4-0:zmq zmq(7) 0MQ Manual - 0MQ/3.2.5 Name zmq – ØMQ 轻量级消息传输内核 Synopsis # ...

  3. ZeroMQ接口函数之 :zmq_close - 关闭ZMQ socket

    ZeroMQ 官方地址 :http://api.zeromq.org/4-0:zmq_close zmq_close(3) ØMQ Manual - ØMQ/3.2.5 Name zmq_close  ...

  4. ZeroMQ接口函数之 :zmq_ctx_destroy - 销毁一个ZMQ环境上下文

    ZeroMQ 官方地址 :http://api.zeromq.org/4-0:zmq_ctx_destroy zmq_ctx_destroy(3) ØMQ Manual - ØMQ/3.2.5 Nam ...

  5. ZeroMQ接口函数之 :zmq_ctx_new – 创建一个新的ZMQ 环境上下文

    ZeroMQ 官方地址 :http://api.zeromq.org/4-0:zmq_ctx_new zmq_ctx_new(3)               ØMQ Manual - ØMQ/3.2 ...

  6. ZeroMQ接口函数之 :zmq_ctx_shutdown - 停止一个ZMQ context

    ZeroMQ 官方地址 :http://api.zeromq.org/4-0:zmq_ctx_shutdown zmq_ctx_shutdown(3) ØMQ Manual - ØMQ/4.1.0 N ...

  7. ZeroMQ接口函数之 :zmq_ctx_term - 终结一个ZMQ环境上下文

    ZeroMQ 官方地址 :http://api.zeromq.org/4-0:zmq_ctx_term zmq_ctx_term(3) ØMQ Manual - ØMQ/4.1.0 Name zmq_ ...

  8. ZeroMQ接口函数之 :zmq_init - 初始化ZMQ环境上下文

    ZeroMQ 官方地址 :http://api.zeromq.org/4-0:zmq_init zmq_init(3) ØMQ Manual - ØMQ/3.2.5 Name zmq_init - 初 ...

  9. ZeroMQ接口函数之 :zmq_version – 返回ZMQ链接库的版本

    ZeroMQ 官方地址 :http://api.zeromq.org/4-2:zmq_version zmq_version(3)          ØMQ Manual - ØMQ/4.1.0 Na ...

  10. ZeroMQ接口函数之 :zmq_msg_init - 初始化一个空的ZMQ消息结构

    ZeroMQ 官方地址 :http://api.zeromq.org/4-1:zmq_msg_init zmq_msg_init(3) ØMQ Manual - ØMQ/3.2.5 Name zmq_ ...

随机推荐

  1. cmd 域名生效检测

    nslookup -qt=ns xxx.baidu.comnslookup -qt=txt xxx.baidu.com

  2. cmd大全

    CMD命令:开始->运行->键入cmd或command(在命令行里可以看到系统版本.文件系统版本) 1. appwiz.cpl:程序和功能 2. calc:启动计算器 3. certmgr ...

  3. 读Secrets of the JavaScript Ninja(一)函数

    理解JavaScript为什么应该作为函数式 在JavaScript中,函数是程序执行过程中的主要模块单元 函数是第一类对象 通过字面量创建 function ninjaFunction(){} 赋值 ...

  4. python 异常处理(25)

    在python开发中,代码书写时难免有疏忽或者意向不到的bug,导致程序run的过程中有可能会直接崩溃:然后对于程序猿而言,程序因bug崩溃是家常便饭,为了增加程序的健壮性,防止程序崩溃,我们可以对程 ...

  5. oracle数据恢复

    比较简单的操作,如有更好的方法欢迎补充 一.查询到某个时间点删除的数据select * from table_name as of timestamp to_timestamp('2019-11-13 ...

  6. IIS提速的几个优化

    一.内存池右键高级设置 1.设置队列5000 2.设置固定回收时间 3.设置空闲时间Suspend 二.网站右键高级设置 1.启用预加载

  7. pyhthon Opencv截取视频中的图片

    import os import cv2 ##加载OpenCV模块 def video2frames(pathIn='', pathOut='', imgname='', only_output_vi ...

  8. AtCoder-arc060 (题解)

    A - 高橋君とカード / Tak and Cards (DP) 题目链接 题目大意: 有 \(n\) 个数字,要求取出一些数字,使得它们的平均数恰好为 \(x\) ,问有几种取法. 大致思路: 只要 ...

  9. 用Qt实现一个计算器

    一· 介绍 目的: 做一个标准型的计算器.用于学习Qt基础学习. 平台: Qt 5.12.0 二· 结构框架设计 2.1最终产品样式 界面的设计大体按照win系统自带的计算器做模仿.左边是win7 的 ...

  10. Django框架之第八篇(模型层补充)--数据库的查询与优化:only/defer,select_related与prefetch_related,事务

    在设置外键字段时需要注意: 当你使用django2.x的版本时候,在建立外键关系时,需要你手动添加几个关键点参数 models.cascade #设置级联删除 db_constraints 数据库查询 ...