一.  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. Swift编码总结10

    1.打开App显示文件已损坏,打不开,您应该将它移到废纸篓,怎么办? 终端输入执行:sudo spctl --master-disable 2.Mac的Siri打开网页控制台,进入开发中模式:不过我觉 ...

  2. 从支付宝SDK的支付流程理解什么是公钥和私钥,什么是加密和数字签名

    ------------------- 这是自己总结: 支付宝SDK支付用到的公钥与私钥整理如下: 1.商户应用公钥    2.商户应用私钥 3.支付宝公钥 4.支付宝私钥   商户应用的公钥与私钥生 ...

  3. Kubernetes之在k8s中部署Java应用

    部署好了k8s以后 部署参考https://www.cnblogs.com/minseo/p/12055731.html 怎么在k8s部署应用 项目迁移到k8s平台是怎样的流程 1,制作镜像 2,控制 ...

  4. Java Web 应用概述

    1.java Web 应用是建立在java语言基础上的企业web应用系统,oracle公司根据行业发展和便于开发制定了一套规范:Java EE规范,截至到当前(2016.3.11)是java EE7规 ...

  5. 【MongoDB学习之三】条件操作符

    环境 MongoDB 4.0 CentOS6.5_x64 一.条件操作符 (>) 大于 - $gt (<) 小于 - $lt (>=) 大于等于 - $gte (<= ) 小于 ...

  6. maven的依赖范围scope

    compile(编译范围) compile是默认的范围:如果没有提供一个范围,那该依赖的范围就是编译范 围.编译范围依赖在所有的classpath中可用,同时它们也会被打包. provided(已提供 ...

  7. Sqlite清空表数据以及重新设置主键操作

    Sqlite清空表数据以及重新设置主键操作 delete from 表名; //清空数据 update sqlite_sequence SET seq = 0 where name ='表名';//自 ...

  8. Linux nfs服务讲解

    nfs服务介绍 nfs(Network File System) 网络文件系统,能使用户访问服务器的文件系统,就像访问自己的本机的文件系统一样,并且多个 客户端共享访问该文件系统. 目前nfs服务,较 ...

  9. python_三目运算

    首先确定三目运算的使用条件, if只有两个才能用三目  只有 if:else:    先写个if else的小例子: if push == "lpush": self.conn.l ...

  10. UOJ399 CTSC2018 假面 期望、DP

    传送门 \(Q \leq 200000 , C \leq 1000 , m_i \leq 100\)-- 先考虑如何维护最后一次操作时所有人的血量期望.不难发现我们需要的复杂度是\(O(Qm_i)\) ...