zeroMq的使用

ZeroMQ简介:

ZeroMQ是一种基于消息队列的多线程网络库.提供跨越多种传输协议(TCP:传输控制协议,当传输出现错误时,能自动予以纠正;UDP:用户数据包协议,当传输出现错误时会将错误信息丢弃;)的套接字,ZeroMQ是一个可伸缩层,可并行运行,分散在分布式系统间.

zeroMQ在设计上主要采用了以下几个高能性的特征:

1.无锁的队列模型
2.批量处理的算法
3.多核下的线程绑定,无须CPU切换
 

ZMQ提供了一组单播传输协议(inporc, ipc, tcp),和两个广播协议(epgm, pgm)。

TCP套接字和ZMQ套接字之间在传输数据方面的区别:

  • ZMQ套接字传输的是消息,而不是字节(TCP)或帧(UDP)。消息指的是一段指定长度的二进制数据块,我们下文会讲到消息,这种设计是为了性能优化而考虑的,所以可能会比较难以理解。
  • ZMQ套接字在后台进行I/O操作,也就是说无论是接收还是发送消息,它都会先传送到一个本地的缓冲队列,这个内存队列的大小是可以配置的。
  • ZMQ套接字可以和多个套接字进行连接(如果套接字类型允许的话)。TCP协议只能进行点对点的连接,而ZMQ则可以进行一对多(类似于无线广播)、多对多(类似于邮局)、多对一(类似于信箱),当然也包括一对一的情况。
  • ZMQ套接字可以发送消息给多个端点(扇出模型),或从多个端点中接收消息(扇入模型)

ZeroMQ模式详解:

1.Request+Reply模式
问答模式:又请求端发起请求,然后等待回应端应答.一个请求必须对于一个回应,从请求端的角度来看是发-收配对,从回应端的角度是收-发对,请求端可以是1~N个,该模型主要用于远程调用及任务分配等.

使用REQ-REP套接字发送和接收消息是需要遵循一定规律的,客户端首先使用zmq_send()发送消息,在使用zmq_recv()接收,如此循环,如果打乱了这个顺序(如连续发送两次)则会报错.类似地,服务端必须先进行接收,后进行发送.

package zmq;

import org.zeromq.ZMQ;
import java.util.Scanner;

/**
 * 模仿zeromq,问答模式中客户端
 */
public class ZeroMqREQ {
    public static void main(String[] args) {
        //创建zmq实例
        ZMQ.Context context = ZMQ.context(1);
        //zmq的套接字模式
        ZMQ.Socket req = context.socket(ZMQ.REQ);
        //连接ip和端口
        req.connect("tcp://localhost:5555");
        //循环发送,接收数据
        while(Thread.currentThread().isInterrupted()){
            //发送数据给客服端
            //创建控制台连接
            Scanner sc = new Scanner(System.in);
            //获取控制台的内容
            String next = sc.next();
            //发送
            req.send("hello"+next);
            //接收
            byte[] re = req.recv();
            System.out.println(new String(re));
        }
        //关闭req
        req.close();
        //关闭context
        context.term();
    }
}
package zmq;

import org.zeromq.ZMQ;
/**
 * 模仿zeromq问答模式中回应端
 */
public class ZeroMqREP {
    public static void main(String[] args) throws InterruptedException {
        //创建zmq实例
        ZMQ.Context context = ZMQ.context(1);
        //连接模式
        ZMQ.Socket rep = context.socket(ZMQ.REP);
        //绑定端口
        rep.bind("tcp://localhost:5555");
        //开始交互
        while(true){
            //先接受数据
            String s = rep.recvStr();
            //睡眠200ms
            Thread.sleep(200);
            //回答问题
            rep.send("hello,zeromq");
        }
    }
}

2.Pub-Sub模式
发布订阅模式,发布端单向分发数据,且不关心是否把全部信息发送给订阅端,如果发布端开始发布信息时,订阅端尚未连接上来,则这些信息会被直接丢弃.订阅端未连接导致信息丢失的问题,可以通过与请求回应模式组合来解决.订阅端只负责接收,而不能反馈,且在订阅端消费速度慢于发布端的情况下,会在订阅端堆积数据.该模型主要用于数据分发.天气预报,微博明星粉丝可以应用这种经典模型.

在使用SUB套接字时,必须使用subscribe()方法来设置订阅的内容,如果你不设置订阅内容,那将什么消息都收不到,订阅信息可以是任何字符串,可以设置多次.只要消息满足其中一条订阅信息.SUB套接字就会接收到.

PUB-SUB套接字组合是异步的,客户端在一个循环中使用zmq_recv()接收消息.如果向SUB套接字发送消息则会报错;类似地,服务端可以不断使用zmq_send()发送消息,但不能在PUB套接字上使用zmq_recv()

关于PUB-SUB套接字,还有一点需要注意,你无法得知SUB是何时开始接收消息的,就算你先打开了SUB套接字,后打开PUB发送消息,这时SUB还是会丢失一些消息的,因为建立连接是需要一些时间,很少,但并不是零.

关于发布-订阅模式的几点说明:

  • 订阅者可以连接多个发布者,轮流接收消息;
  • 如果发布者没有订阅者与之相连,那它发送的消息将直接被丢弃;
  • 如果你使用TCP协议,那当订阅者处理速度过慢时,消息会在发布者处堆积。
  • 在目前版本的ZMQ中,消息的过滤是在订阅者处进行的。也就是说,发布者会向订阅者发送所有的消息,订阅者会将未订阅的消息丢弃。
package zmq;

import org.zeromq.ZMQ;
//模仿zermq中订阅模式sub(客户端--订阅者)
public class Sub {
    public static void main(String[] args) {
        //zmq实例
        ZMQ.Context context = ZMQ.context(1);
        //模式
        ZMQ.Socket sub = context.socket(ZMQ.SUB);
        //连接ip和port
        sub.connect("tcp://localhost:9000");
        //设置接受形式(不设置,接收不到参数)
        sub.subscribe("");
        //开始接受数据
        while(!Thread.currentThread().isInterrupted()){
            byte[] recv = sub.recv();
            System.out.println(new String(recv));
        }
        //释放资源
        sub.close();
        context.term();
    }
}

 

package zmq;

import org.zeromq.ZMQ;
//模仿zmq中订阅模式中pub(服务端--发布者)
public class Pub {
    public static void main(String[] args) throws InterruptedException {
      //context
        ZMQ.Context context = ZMQ.context(1);
        //建立模式
        ZMQ.Socket pub = context.socket(ZMQ.PUB);
        //绑定ip和port
        pub.bind("tcp://localhost:9000");
        //发送数据
        int i = 0;
        while(true){
            //发送数据
            Thread.sleep(100);
            System.out.println(i);
            //只能发送,不能接收
            pub.send("第"+i+"次都对你说,hello");
            i++;
        }
    }
}

3.Push-Pull模式

Server端作为Push端,而Client端作为Pull端,如果有多个Client端同时连接到Server端,则Server端会在内部做一个负载均衡,采用平均分配的算法,将所有消息均衡发布到Client端上。如果有多个Server端同时连接到Client端,这里Push与Pull之间的对应关系是多个Push角色对应一个Pull角色,在ZeroMQ中,给这种结构取的名叫做公平队列,这里也就是说将Pull角色理解为一个队列,各个Push角色不断的向这个队列中发送数据。与发布订阅模型相比,推拉模型在没有消费者的情况下,发布的消息不会被消耗掉;在消费者能力不够的情况下,能够提供多消费者并行消费解决方案。该模型主要用于多任务并行处理。

package zmq;

import org.zeromq.ZMQ;

//模仿zmq中推拉模式的pull(客户端)
public class ZeroMQPull {
    public static void main(String[] args) {
        ZMQ.Context context = ZMQ.context(1);
        ZMQ.Socket pull = context.socket(ZMQ.PULL);

        pull.connect("tcp://localhost:5556");
        while(!Thread.currentThread().isInterrupted()){
            byte[] recv = pull.recv();
            System.out.println(new String(recv));
        }
        pull.close();
        context.term();
    }
}
package zmq;

import org.zeromq.ZMQ;

//模仿zmq中推拉模式push(服务端)
public class ZeroMQPUSH {
    public static void main(String[] args) throws InterruptedException {
        ZMQ.Context context = ZMQ.context(1);
        ZMQ.Socket push = context.socket(ZMQ.PUSH);
        push.bind("tcp://localhost:5556");
        int i = 0;
        while (true){
            push.send("hello"+i);
            System.out.println(i);
            i++;
            Thread.sleep(200);
        }

    }
}

代理:

package zmq;

import org.zeromq.ZMQ;

//模仿代理模式
public class ZMQProxy {
    public static void main(String[] args) {
        ZMQ.Context context = ZMQ.context(1);
        //创建pull
        ZMQ.Socket pull = context.socket(ZMQ.PULL);
        pull.connect("tcp://localhost:5555");
        //创建push
        ZMQ.Socket push = context.socket(ZMQ.PUSH);
        push.bind("tcp://localhost:5556");

        //启动代理
        ZMQ.proxy(pull,push,null);

        //释放资源
        pull.close();
        push.close();
        context.term();

    }
}

 

你可能感兴趣的:(zeroMQ)