ZeroMQ介绍

一.介绍

MQ有很多种,比如MSMQ,ActiveMQ,RabbitMQ等等

今天主要介绍的MQ是ZeroMQ,其具有一个独特的非中间件的模式,不需要安装和运行一个消息服务器,或中间件。你只需要简单的引用ZeroMQ程序库,然后你就可以愉快的在应用程序之间发送消息了,因此部署是非常简单的。就性能上来说比其他几个MQ要强的非常多。

ZeroMQ,新一层的网络堆栈,轻量级,socket风格的接口,支持多种消息传送模式,如:P2P,订阅发布模式以及请求-响应模式等等,支持多种底层协议,支持多种平台和CPU架构。

 

二.模型

服务端:

public classServer {

         publicstatic voidmain(String[] args) throws Exception{

                  // Prepare our context andsockets

                  final ZMQ.Context context =ZMQ.context(1);

                  ZMQ.Socket socket = context.socket(ZMQ.REP);

                  socket.bind("tcp://*:5555"); 

                  while (true) {

                           byte[]request = socket.recv(0);

                           System.out.println(new String(request));

                           while (socket.hasReceiveMore()){

                                    request =socket.recv(0);

                                    System.out.println(new String(request));

                           }

                           String s = new String(request);

                           long r = Long.parseLong(s) + 100;

                       // reply = request+100

                           socket.send(String.valueOf(r).getBytes(), 0);

                  }

        }

}

客户端:

public classClient {

         publicstatic voidmain(String[] args) {

                  // Prepare our context andsocket

                  final ZMQ.Context context =ZMQ.context(1);

                  ZMQ.Socket socket = context.socket(ZMQ.REQ); 

                  System.out.println("Connecting tohello world server...");

                  socket.connect("tcp://localhost:5555"); 

                  while (true) {

                           long l = System.nanoTime();

                           String s = String.valueOf(l);

                           socket.send(s.getBytes(), 0);

                           byte[] r = socket.recv(0);

                           System.out.println(l + " + 100 = "+ newString(r));

               }

       }

}

三.设计模式

ZeroMQ 把通讯的需求看成四类。其中一类是一对一结对通讯,用来支持传统的 TCP socket 模型,但并不推荐使用。常用的通讯模式只有三类

1.    请求回应模型。由请求端发起请求,并等待回应端回应请求。从请求端来看,一定是一对收发配对的;反之,在回应端一定是发收对。请求端和回应端都可以是 1:N 的模型。通常把 1 认为是 server N 认为是 Client ZeroMQ 可以很好的支持路由功能(实现路由功能的组件叫作 Device),把 1:N 扩展为 N:M (只需要加入若干路由节点)。从这个模型看,更底层的端点地址是对上层隐藏的。每个请求都隐含有回应地址,而应用则不关心它。

2.    发布订阅模型。这个模型里,发布端是单向只发送数据的,且不关心是否把全部的信息都发送给订阅端。如果发布端开始发布信息的时候,订阅端尚未连接上来,这些信息直接丢弃。不过一旦订阅端连接上来,中间会保证没有信息丢失。同样,订阅端则只负责接收,而不能反馈。如果发布端和订阅端需要交互(比如要确认订阅者是否已经连接上),则使用额外的 socket 采用请求回应模型满足这个需求。

3.    管道模型。这个模型里,管道是单向的,从 PUSH 端单向的向 PULL 端单向的推送数据流。如果有多个client端同时连接到这个server,则服务器会在内部做一个负载均衡,采用平均分配的算法,将所有的消息均衡发布到client端上

2,3两种模式中,无论是server端还是client端在启动时 都是不知道client实际数量的,这就意味着,一个使用zeromq搭建的服务,可以进行”热更新”.

 

1)request-reply
就是一般的C/S架构中,client与server之间一问一答的通信模式,比如最经典的echo服务.需要注意的是,client发送一个request,server必须有一个回应.
这个pattern并不是什么亮点,下面亮点来了.

2)publish-subscribe
server端作为publish端,而任何连接到服务端的client都会成为subscribe端.也就是说,server端会把当前的所有需要publish出去的消息全部发送到当前连接上去的client上.

3)push-pull
server端作为push端,而client端作为pull端.如果有多个client端同时连接到这个server,则服务器会在内部做一个负载均衡,采用平均分配的算法,将所有的消息均衡发布到client端上.

看上去,很稀松平常?接下来亮点真的来了.
2),3)两种模式中,无论是server端还是client端在启动时 都是不知道client实际数量的,这就意味着,一个使用zeromq搭建的服务,可以进行”热更新”.
考虑如下一种场景.一个server端做为一组服务器集群最上层的一个proxy,起到负载均衡的作用,将请求按照它下面对应服务器集群依次派发到不同的 client端进行处理.某个时刻可能处理的机器只有2台,而随着负载越来越大,可能需要3台机器了,这个时候如果使用zeromq的push-pull 搭建的proxy端,则可以不用对之前搭建的server,client端进行停机,只需要新启动一个client连接上去,proxy层就会自动根据当 前的机器分配平均派发任务了.cool.

实际上,这些模式并不是什么新东西,只不过zeromq为使用者做了一个封装,而不是像libevent,ACE等还局限在网络层部分的封装,它关注的是通信层,通信模式等.



四.搭建安装

Linux安装

下载地址:http://download.zeromq.org/zeromq-2.1.7.tar.gz

解压后下载文件

cd zeromq-2.1.7

./configure --prefix=/usr/local/zeromq

make

make install

 

Window安装:

下载地址:http://download.zeromq.org/zeromq-2.1.7.zip

需要C编译环境,VC2008或者MinGW,RubyInstall中的DevKit

下载MinGW

地址: http://sourceforge.net/projects/mingw/files/Automated%20MinGW%20Installer/mingw-get-inst/mingw-get-inst-20110530/mingw-get-inst-20110530.exe/download

点击打开,根据需要选择下载的功能,开始安装下载(需要网络)

安装完毕后,在window环境变量中设置PATH路径

下载DevKit

地址:http://rubyinstaller.org/downloads

下载exe文件安装即可,在Window环境变量中设置PATH路径

 

五.支持的客户端

 

C,C++,JAVA,PHP,Python,Ruby,C#,Erlang,Perl等等

 

六.运行(Linux系统,Java为例)

 

前提:需要定义环境变量JAVA_HOME

  需要先安装zeromq

下载jzero:https://github.com/zeromq/jzmq

解压下载文件

cd zeromq-jzmq-59616d9

$ ./autogen.sh

$ ./configure --prefix=/opt/jzmq --with-zeromq=/usr/local/zeromq

回车运行后,如果缺少什么会有提示,根据提示解决就可以

比如 zmq.h 缺少,那就是证明 --with-zeromq配置不正确

Java找不到,说JAVA_HOME没有设置正确

$ make

$ make install

 

编译完成,编写测试java文件

package com.jzmq;

import org.zeromq.ZMQ;

import org.zeromq.ZMQQueue;

public class MqQueue {  

    public static void main(String[] args) {

        ZMQ.Context context =ZMQ.context(1);

        ZMQ.Socket clients =context.socket(ZMQ.XREP);

        clients.bind("inproc://gate_clients");

        ZMQ.Socket workers =context.socket(ZMQ.XREQ);

        workers.bind("inproc://gate_workers");

        ZMQ.Socket client =context.socket(ZMQ.REQ);

        client.connect("inproc://gate_clients");

        ZMQ.Socket worker =context.socket(ZMQ.REP);

        worker.connect("inproc://gate_workers");

        Thread t = new Thread(new ZMQQueue(context, clients,workers));

            t.start();

            for (int i = 0; i < 100; i++) {

                byte[] req = ("request" + i).getBytes();

                byte[] rsp = ("response" + i).getBytes();

                client.send(req, 0);

                // worker receives request

                byte[] reqTmp = worker.recv(0);

                System.out.println(new String(reqTmp));

                // worker sends response

                worker.send(rsp, 0);

                // client receives response

                byte[] rspTmp = client.recv(0);

                System.out.println(new String(rspTmp));

            }      

        t.interrupt();
        }

}

编译前准备

设置exportJAVA_HOME=/usr/java/jdk1.6.0.22

设置exportCLASSPATH=$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/dt.jar:/usr/local/zeromq/share/java/zmq.jar:.

设置exportLD_LIBRARY_PATH=/opt/jzmq/lib/:/usr/local/zeromq/lib/

 

javac com/jzmq/MqQueue.java

java com.jzmq.MqQueue

response0

request1

response1

request2

response2

request3

response3

..............

<think>嗯,用户之前询问了Linux下C语言线程间通信的开源实现,我推荐了ZeroMQ和基于pthread的方案。现在用户更明确地要求深入理解ZeroMQ在C语言和Linux线程通信中的应用。看来ta对ZeroMQ产生了兴趣,可能正在评估是否将其用于实际项目。用户引用的材料显示ta已经注意到ZeroMQ的两个关键优势:号称"史上最快消息队列"以及支持inproc线程通信协议。这些恰好能直接解决ta的线程通信需求。不过ta可能还面临实际集成问题:如何在C语言中调用ZeroMQAPI?inproc模式具体怎么配置?结合我之前的回答,这次需要更系统地展开四点:首先是ZeroMQ的线程通信核心机制,要强调inproc协议的本质是零拷贝内存传递;其次是代码示例必须完整可运行,需包含创建上下文、绑定套接字等关键步骤;然后要补充高级模式支持,因为用户引用中提到了发布订阅模式;最后不能忽略多线程编程中的资源清理陷阱。注意到用户特别标注"开源库",说明license兼容性可能很重要,好在ZeroMQ的LGPL许可对商业项目友好。另外用户提及"Linux"环境,要强调ZeroMQ在epoll系统调用优化上的特点——这正是它能实现百万级消息吞吐的基础。或许还应该暗示性能对比:虽然基于pthread互斥锁的方案在小数据量时足够用,但当线程需要传递图像数据(联想到用户引用中的CxImage库)这类大消息时,ZeroMQ的内存复用机制会有显著优势。</think> ### ZeroMQ 在 C 语言中实现 Linux 线程间通信的完整指南 ZeroMQ(ØMQ)是一个高性能异步消息库,专为分布式/并发系统设计,其 **`inproc` 传输协议**可直接用于同一进程内的线程间通信(ITC),无需内核介入,实现零拷贝的高效数据传递。 --- #### 一、ZeroMQ 核心优势 1. **高性能通信** - 线程间使用 **内存队列** 直接传递消息,避免系统调用开销,吞吐量可达 **百万级消息/秒** [^1] - 支持异步 I/O 和多路复用(如 Linux 的 `epoll`) 2. **灵活通信模式** | 模式 | 适用场景 | |---------------|----------------------------| | `PAIR` | 1:1 双向通信(线程专用) | | `PUB/SUB` | 1:N 广播(发布-订阅) | | `PUSH/PULL` | 流水线任务分发(生产者-消费者)| 3. **跨平台支持** - 代码可无缝运行于 Linux/Windows/macOS - 协议包括:`inproc`(线程间)、`ipc`(进程间)、`tcp`(网络) --- #### 二、Linux 线程通信的 C 语言实现 ##### 1. **基础 PAIR 模式示例** ```c #include <zmq.h> #include <pthread.h> #include <stdio.h> // 线程 1:发送消息 void* thread_sender(void* ctx) { void* socket = zmq_socket(ctx, ZMQ_PAIR); zmq_connect(socket, "inproc://thread-comm"); // 连接inproc端点 zmq_send(socket, "Hello", 5, 0); // 发送消息 zmq_close(socket); return NULL; } // 线程 2:接收消息 void* thread_receiver(void* ctx) { void* socket = zmq_socket(ctx, ZMQ_PAIR); zmq_bind(socket, "inproc://thread-comm"); // 绑定inproc端点 char buf[10]; zmq_recv(socket, buf, sizeof(buf), 0); // 接收消息 printf("Received: %s\n", buf); // 输出: "Hello" zmq_close(socket); return NULL; } int main() { void* ctx = zmq_ctx_new(); // 创建 ZeroMQ 上下文 pthread_t t1, t2; pthread_create(&t2, NULL, thread_receiver, ctx); // 先启动接收者 sleep(1); // 确保绑定完成 pthread_create(&t1, NULL, thread_sender, ctx); pthread_join(t1, NULL); pthread_join(t2, NULL); zmq_ctx_destroy(ctx); // 清理上下文 return 0; } ``` - **编译命令**: ```bash gcc -o thread_comm thread_comm.c -lzmq -lpthread ``` ##### 2. **PUB/SUB 广播模式** ```c // 发布者线程 void* publisher(void* ctx) { void* pub_socket = zmq_socket(ctx, ZMQ_PUB); zmq_bind(pub_socket, "inproc://broadcast"); zmq_send(pub_socket, "UPDATE_DATA", 12, 0); // 广播消息 zmq_close(pub_socket); return NULL; } // 订阅者线程 void* subscriber(void* ctx) { void* sub_socket = zmq_socket(ctx, ZMQ_SUB); zmq_connect(sub_socket, "inproc://broadcast"); zmq_setsockopt(sub_socket, ZMQ_SUBSCRIBE, "", 0); // 订阅所有消息 char msg[20]; zmq_recv(sub_socket, msg, sizeof(msg), 0); printf("Subscriber got: %s\n", msg); // 输出: "UPDATE_DATA" zmq_close(sub_socket); return NULL; } ``` - **关键点**:订阅者需设置 `ZMQ_SUBSCRIBE` 过滤条件(空字符串表示接收所有消息)[^1]。 --- #### 三、高级特性 1. **多帧消息**:支持原子性发送多个消息帧 ```c zmq_send(socket, "HEADER", 6, ZMQ_SNDMORE); // 发送第一部分 zmq_send(socket, "PAYLOAD", 7, 0); // 发送最后部分 ``` 2. **超时控制**: ```c int timeout = 1000; // 1秒 zmq_setsockopt(socket, ZMQ_RCVTIMEO, &timeout, sizeof(int)); ``` 3. **线程安全**: - ZeroMQ 套接字 **不可跨线程共享**,但上下文 `zmq_ctx` 是线程安全的 - 每个线程需创建独立套接字 --- #### 四、与其他方案对比 | **方案** | 性能 | 复杂度 | 适用场景 | |------------------|------|--------|-----------------------| | ZeroMQ(inproc) | ⭐⭐⭐⭐ | 中 | 高吞吐/低延迟线程通信 | | Pthread 信号量 | ⭐⭐ | 高 | 简单同步(如互斥锁) | | System V 消息队列 | ⭐⭐ | 高 | 进程间通信(非线程优化) | > ZeroMQ 在 **避免数据拷贝**(通过内存队列)和 **减少上下文切换** 上显著优于传统方案 [^1]。 --- #### 五、使用建议 1. **资源管理**: - 使用 `zmq_ctx_term()` 而非 `zmq_ctx_destroy()` 确保安全退出 - 检测返回值:所有 ZeroMQ API 返回 `-1` 表示失败(用 `zmq_errno()` 获取错误码) 2. **性能调优**: - 设置 `ZMQ_IO_THREADS` 调整 I/O 线程数: ```c int threads = 2; zmq_ctx_set(ctx, ZMQ_IO_THREADS, threads); ``` 3. **调试工具**: - 使用 `ZMQ_MONITOR` 套接字跟踪消息流 - 结合 `valgrind --tool=helgrind` 检测线程竞争 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值