传输层的各种模式——ZeroMQ 库的使用 .

最近在研究 ZeroMQ 库的使用,所以在这里总结一下各种模式,以便日后拿来使用。

关于 ZeroMQ 库,我就不多介绍了,大家可以参考下面一些文章,以及他的官网使用指南API 参考项目仓库等内容。

开源点评:ZeroMQ简介

ZeroMQ的学习和研究

ZeroMQ 的模式

ZeroMQ 的目标是成为 OSI 模型的传输层(Transport Layer)的标准协议,所以他支持各种操作系统,支持多达30种以上的语言,是跨平台、跨语言的传输层库,而且性能是其绝对优势。所以对于进程间通信、节点间通信均可以使用他,可以用他直接替代 socket 的操作。而且用过之后就知道,他用起来非常的简单,学习成本很低,我只用了 1 天时间就把他的 3 种常用模式用 Python 实现了,代码在这里,大家可以参考一下,接下来准备用 C++ 再实现一遍。

ZeroMQ 总结的通信模式如下:

  • PUB and SUB
  • REQ and REP
  • REQ and ROUTER
  • DEALER and REP
  • DEALER and ROUTER
  • DEALER and DEALER
  • ROUTER and ROUTER
  • PUSH and PULL
  • PAIR and PAIR

ZeroMQ 总结的应用模式如下:

  • Request-reply, which connects a set of clients to a set of services. This is a remote procedure call and task distribution pattern.
  • Publish-subscribe, 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.

当然,实际使用中还得了解一些解决具体问题的模式,所以下面把使用指南中的一些模式整理如下,方便自己日后拿来使用。

最常用的三种模式:

1. Request-Reply

Request-Reply

Extended Request-reply

Request-reply Broker

Multithreaded Server

服务器端代码:

package zmq20140508;

import org.zeromq.ZMQ;
/**
 * 
 * @author lijianhua
 *
 */

public class ZMQServer {
	
	public static void main(String[] args) {
		Client client =new Client();
		client.start();
	}
public static class Client extends Thread{
	private String url_worker="inproc://workers";
	private String url_client="tcp://127.0.0.1:6666";
	private ZMQ.Poller poller;
	private ZMQ.Context context;
	private ZMQ.Socket clients;
	private ZMQ.Socket workers;
	@SuppressWarnings("deprecation")
	public void run(){
		context = ZMQ.context(1);
		clients = context.socket(ZMQ.ROUTER);
		clients.bind(url_client);
		
		workers = context.socket(ZMQ.DEALER);
		workers.bind(url_worker);
		
		for(int i=0;i<10;i++){
			new Worker(context,url_worker).start();
		}

		ZMQ.device(ZMQ.QUEUE, clients, workers);//开始因这个犯错,搞了半天
		poller=new ZMQ.Poller(2);//创建一个大小为2的poller
		//分别将上述的pull注册到poller上,注册的事件是读
		ZMQ.PollItem citem = new ZMQ.PollItem(clients, ZMQ.Poller.POLLIN);
		ZMQ.PollItem witem = new ZMQ.PollItem(workers, ZMQ.Poller.POLLIN);
		poller.register(citem);
		poller.register(witem);
		boolean ok =true;
		while(ok){
			poller.poll(); 
				if(poller.getItem(0).isReadable()){
					System.out.println("当前发送:clients");
					byte[] recv = clients.recv();
					workers.send(recv);
				}else{
					System.out.println("当前发送:workers");
					byte[] recv = workers.recv();
					clients.send(recv);
				}
		}
		closeAll();
	}
	public void closeAll(){
		clients.close();
		workers.close();
		context.term();
	}
}
	public static class Worker extends Thread{
		private String url_worker1;
		private ZMQ.Context context1;
		public Worker(ZMQ.Context context,String url_worker) {
			this.url_worker1=url_worker;
			this.context1=context;
		}
		public void run() {
			ZMQ.Socket socket = context1.socket(ZMQ.REP);
			socket.connect(url_worker1);
			ZMQ.Poller poller=new ZMQ.Poller(1);
			ZMQ.PollItem item = new ZMQ.PollItem(socket, ZMQ.Poller.POLLIN);
			poller.register(item);
			while (true){
				if(poller.poll()==ZMQ.Poller.POLLIN){
					byte[] recv = socket.recv();
					System.out.println("fix========"+new String(recv));
					try {
						Thread.sleep(1);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					socket.send("world========");
				}
			}
		}
	}
}

客服端代码

package zmq20140508;

import org.zeromq.ZMQ;
import org.zeromq.ZMQ.Context;
import org.zeromq.ZMQ.Socket;

public class Request {

	public static void main(String[] args) {
		System.out.println("客户端开始.........");
		Context context = ZMQ.context(1);
		Socket resSocket = context.socket(ZMQ.REQ);//客服端发送消息
//		resSocket.connect("tcp://115.236.73.253:5570");
		resSocket.connect("tcp://127.0.0.1:6666");
		int i=0;
		while(i<5){
			i++;
			try {
				resSocket.send("hello");
				byte[] msg = resSocket.recv();
				String outputStr = new String(msg);
				System.out.println("#### Client Receive:" + outputStr);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

	}

}


2. Publish-Subscribe

Publish-Subscribe

Pub-Sub Network with a Proxy

Extended Publish-Subscribe

Pub-Sub Forwarder Proxy

Pub-Sub Synchronization


3. Parallel Pipeline

Parallel Pipeline

Parallel Pipeline with Kill Signaling


其他模式:

其他模式只是上面三种模式的加强而已,所以也是分为三大类。
A. Request-Reply Patterns

1. The Load-balancing Pattern

Load-Balancing Broker

代码示例:

package zmq20140505;
import java.util.LinkedList;

import org.zeromq.ZFrame;
import org.zeromq.ZMQ;
import org.zeromq.ZMsg;

public class Balance {
	
	public static class Client {
		public void start() {
			new Thread(new Runnable(){

				public void run() {
					// TODO Auto-generated method stub
					ZMQ.Context context = ZMQ.context(1);
					ZMQ.Socket socket = context.socket(ZMQ.REQ);
					socket.connect("ipc://127.0.0.1:5555");  //连接router,想起发送请求
					
					while (!Thread.currentThread().isInterrupted()) {
						socket.send("hello".getBytes(), 0);  //发送hello请求
						String bb = new String(socket.recv());  //获取返回的数据
						System.out.println("recv Worker : "+bb);	
					}
					socket.close();
					context.term();
				}
				
			}).start();
		}
	}
	
	public static class Worker {
		public void start() {
			new Thread(new Runnable(){

				public void run() {
					// TODO Auto-generated method stub
					ZMQ.Context context = ZMQ.context(1);
					ZMQ.Socket socket = context.socket(ZMQ.REQ);
					
					socket.connect("ipc://127.0.0.1:6666");  //连接,用于获取要处理的请求,并发送回去处理结果
					
					socket.send("ready".getBytes());  //发送ready,表示当前可用
					 
					while (!Thread.currentThread().isInterrupted()) {
						ZMsg msg = ZMsg.recvMsg(socket);  //获取需要处理的请求,其实这里msg最外面的标志frame是router对分配给client的标志frame
						ZFrame request = msg.removeLast();   //最后一个frame其实保存的就是实际的请求数据,这里将其移除,待会用新的frame代替
						
						String now = new String(request.getData());
						System.out.println("recv Client : " + now);
						
						ZFrame frame = new ZFrame("hello fjs".getBytes());  
						msg.addLast(frame);  //将刚刚创建的frame放到msg的最后,worker将会收到
						msg.send(socket);  //将数据发送回去
					}
					socket.close();
					context.term();
				}
				
			}).start();
		}
	}
	
	public static class Middle {
		private LinkedList<ZFrame> workers;
		private LinkedList<ZMsg> requests;
		private ZMQ.Context context;
		private ZMQ.Poller poller;
		
		public Middle() {
			this.workers = new LinkedList<ZFrame>();
			this.requests = new LinkedList<ZMsg>();
			this.context = ZMQ.context(1);
			this.poller = new ZMQ.Poller(2);
		}
		
		public void start() {
			ZMQ.Socket fronted = this.context.socket(ZMQ.ROUTER);  //创建一个router,用于接收client发送过来的请求,以及向client发送处理结果
			ZMQ.Socket backend = this.context.socket(ZMQ.ROUTER);  //创建一个router,用于向后面的worker发送数据,然后接收处理的结果
			
			fronted.bind("ipc://127.0.0.1:5555");  //监听,等待client的连接
			backend.bind("ipc://127.0.0.1:6666");  //监听,等待worker连接
			
			//创建pollItem
			ZMQ.PollItem fitem = new ZMQ.PollItem(fronted, ZMQ.Poller.POLLIN);  
			ZMQ.PollItem bitem = new ZMQ.PollItem(backend, ZMQ.Poller.POLLIN);
			
			this.poller.register(fitem);  //注册pollItem
			this.poller.register(bitem);
			
			
			while (!Thread.currentThread().isInterrupted()) {
				this.poller.poll();
				if (fitem.isReadable()) {  //表示前面有请求发过来了
					ZMsg msg = ZMsg.recvMsg(fitem.getSocket());  //获取client发送过来的请求,这里router会在实际请求上面套一个连接的标志frame
					/**
					 * //以下3行查看中间层客服端发过来的值
					 */
					ZFrame request = msg.getLast();
					String now = new String(request.getData());
					System.out.println("fix===="+now);
					
					this.requests.addLast(msg);   //将其挂到请求队列
				}
				if (bitem.isReadable()) {  //这里表示worker发送数据过来了
					ZMsg msg = ZMsg.recvMsg(bitem.getSocket());  //获取msg,这里也会在实际发送的数据前面包装一个连接的标志frame
					//这里需要注意,这里返回的是最外面的那个frame,另外它还会将后面的接着的空的标志frame都去掉
					ZFrame workerID = msg.unwrap();  //把外面那层包装取下来,也就是router对连接的标志frame
					this.workers.addLast(workerID);  //将当前的worker的标志frame放到worker队列里面,表示这个worker可以用了
					ZFrame readyOrAddress = msg.getFirst(); //这里获取标志frame后面的数据,如果worker刚刚启动,那么应该是发送过来的ready,
					if (new String(readyOrAddress.getData()).equals("ready")) {  //表示是worker刚刚启动,发过来的ready
						msg.destroy();
					} else {
						msg.send(fronted);  //表示是worker处理完的返回结果,那么返回给客户端
					}
				}
				while (this.workers.size() > 0 && this.requests.size() > 0) {
					ZMsg request = this.requests.removeFirst();
					ZFrame worker = this.workers.removeFirst();
					
					request.wrap(worker);  //在request前面包装一层,把可以用的worker的标志frame包装上,这样router就会发给相应的worker的连接
					request.send(backend);  //将这个包装过的消息发送出去
				}
			}
			fronted.close();
			backend.close();
			this.context.term();
		}
	}
	
	
	public static void main(String args[]) {
		Worker worker = new Worker();
		worker.start();
		Client client = new Client();
		client.start();
		Middle middle = new Middle();
		middle.start();
		
	}
}


2. The Asynchronous Client-Server Pattern

Asynchronous Client-Server

Detail of Asynchronous Server

Cluster Architecture

Multiple Clusters

Idea 1 - Cross-connected Workers

Idea 2 - Brokers Talking to Each Other

Cross-connected Brokers in Federation Model


3. Client-side Reliability (Lazy Pirate Pattern)

The Lazy Pirate Pattern


4. Basic Reliable Queuing (Simple Pirate Pattern)

The Simple Pirate Pattern


5. Robust Reliable Queuing (Paranoid Pirate Pattern)

The Paranoid Pirate Pattern


6. Service-Oriented Reliable Queuing (Majordomo Pattern)

The Majordomo Pattern


7. Disconnected Reliability (Titanic Pattern)

The Titanic Pattern


8. High-availability Pair (Binary Star Pattern)

High-availability Pair, Normal Operation

High-availability Pair During Failover

Binary Star Finite State Machine


9. Brokerless Reliability (Freelance Pattern)

The Freelance Pattern


B. Publish-Subscribe Patterns

1. Pub-sub Tracing (Espresso Pattern)

2. Slow Subscriber Detection (Suicidal Snail Pattern)

3. High-speed Subscribers (Black Box Pattern)

The Simple Black Box Pattern

Mad Black Box Pattern


4. Reliable Publish-Subscribe (Clone Pattern)

Publishing State Updates

State Replication

Republishing Updates

Clone Client Finite State Machine

High-availability Clone Server Pair


C. Parallel Pipeline Patterns
### 回答1: zeromq-2.1.7.tar.gz是ZeroMQ软件的一个版本压缩包。ZeroMQ是一个高性能、异步通信,它可以用于构建分布式、并发的应用程序。 ZeroMQ的设计理念是简单而灵活的,它提供了一个消息传递机制,可以让应用程序之间以异步的方式进行通信。它的核心是一个通过TCP、IPC或inproc等传输协议进行通信的消息队列机制,通过这个机制,应用程序可以通过发送和接收消息来进行通信。 zeromq-2.1.7.tar.gz是ZeroMQ 2.1.7版本的源代码压缩包。它包含了ZeroMQ的所有源代码、编译脚本和示例程序等文件。用户可以通过解压这个压缩包,将源代码编译成可执行文件,并集成到自己的应用程序中使用ZeroMQ的一个重要特点是它的跨平台性,它可以在各种操作系统上运行,包括Windows、Linux、Mac等。此外,ZeroMQ还支持多种编程语言,包括C、C++、Python等。这使得开发人员可以灵活地选择自己熟悉的语言来使用ZeroMQ。 总之,zeromq-2.1.7.tar.gz是ZeroMQ 2.1.7版本的源代码压缩包,通过对这个压缩包进行解压和编译,开发人员可以使用ZeroMQ构建高性能、异步通信的应用程序。 ### 回答2: zeromq-2.1.7.tar.gz是ZeroMQ的一个版本。ZeroMQ是一个开源的消息传递框架,用于构建分布式和并行计算应用程序。它提供了高性能的消息传递模式,具有简单的API和多种语言的绑定。 ZeroMQ的2.1.7版本是在2011年发布的一个稳定版本。在这个版本中,ZeroMQ引入了一些新的功能和修复了一些bug,以提高系统的性能和稳定性。 ZeroMQ使用套接字(sockets)来提供消息传递。它支持多种消息传递模式,包括请求-应答、发布-订阅、推送-拉取等。这些模式使得开发者可以根据实际需求选择合适的模式来实现消息通信,并提供了高度灵活性和可扩展性。 ZeroMQ是一个轻量级的框架,可以在各种操作系统上运行。它不依赖于第三方,可以与其他编程语言集成,如C、C++、Python、Java等。这使得开发者可以利用自己熟悉的编程语言和工具来开发和扩展ZeroMQ应用程序。 通过使用ZeroMQ,开发者可以很容易地构建分布式系统,实现不同节点之间的通信和协作。它具有很好的可扩展性和弹性,可以适应各种场景和需求。 总的来说,zeromq-2.1.7.tar.gz是ZeroMQ框架的一个特定版本,它提供了高性能、灵活性和可扩展性的消息传递模式,可以用于构建分布式和并行计算应用程序。 ### 回答3: zeromq-2.1.7.tar.gz是一个开源软件包的压缩文件。其中的"zeromq"代表ZeroMQ,它是一个高性能、异步通信,用于构建分布式和并行应用程序。 ZeroMQ提供了一组易于使用的API,用于实现不同进程之间的可靠、高效的消息传递。它支持多种消息传输模式,包括请求-回应、发布-订阅和推送-拉取等。ZeroMQ使用封装的套接字(sockets)来实现通信,并提供了灵活的消息分发方式,以及高度定制化的应用程序架构。 zeromq-2.1.7.tar.gz是ZeroMQ的一个特定版本。版本号"2.1.7"表示该版本是ZeroMQ发布的第二个主要版本中的第一次小版本更新。通常,软件版本的更新会修复一些已知的问题,并添加新的功能或改进现有功能。因此,zeromq-2.1.7.tar.gz可能包含了ZeroMQ的新功能、性能改进以及错误修复。 压缩文件的.tar.gz扩展名表示该文件使用了tar和gzip两种压缩格式。tar用于将多个文件或目录打包成单个文件,而gzip则用于对该tar文件进行压缩。使用.tar.gz格式能够减小文件的大小,并方便传输和存储。 如果想要使用zeromq-2.1.7.tar.gz,可以通过解压缩该文件来获取ZeroMQ的安装文件。解压缩后的文件可能包含源代码、编译所需的配置文件以及文档等内容。根据不同的操作系统,安装ZeroMQ可能需要执行一系列的编译、配置和安装步骤。 总之,zeromq-2.1.7.tar.gz是ZeroMQ通信的一个特定版本的压缩文件,可以用于在分布式和并行应用程序中实现高性能的消息传递。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值