Zeromq模式详解

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

1.Request-Reply模式

问答一一对应

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

使用REQ-REP套接字发送和接受消息是需要遵循一定规律的。客户端首先使用zmq_send()发送消息,再用zmq_recv()接收,如此循环。如果打乱了这个顺序(如连续发送两次)则会报错。类似地,服务端必须先进行接收,后进行发送。
也就是说Request-Reply模式是严格同步的,Request端必须先发送后接受,reply端必须先接受后发送。

在这里插入图片描述

req问

package zeromqDemo;

import org.zeromq.ZMQ;

//模拟问答模式中的问
public class ZmqReq {
    public static void main(String[] args) {
        //创建zeromq客户端
        ZMQ.Context zmq = ZMQ.context(1);
        //创建一个问的客户端
        ZMQ.Socket req = zmq.socket(ZMQ.REQ);
        //连接ip和端口
        req.connect("tcp://localhost:5555");
        //定义标志位
        int i = 0;
        //接收数据
        while (!Thread.currentThread().isInterrupted()){
            //发送请求
            String str = "hello world";
            //发送
            req.send(str+i);
            System.out.println(str+i);
            //接收数据
            byte[] recv = req.recv();
            //打印
            System.out.println(new String(recv));

            i++;
        }
        //关闭资源
        req.close();
        zmq.close();
    }
}

rep答

package zeromqDemo;

import org.zeromq.ZMQ;

//模拟问答模式中的答
public class ZmqRep {
    public static void main(String[] args) throws InterruptedException {
        //创建zmq实例
        ZMQ.Context zmq = ZMQ.context(1);
        //创建答的实例
        ZMQ.Socket rep = zmq.socket(ZMQ.REP);
        //绑定ip和端口
        rep.bind("tcp://localhost:5555");
        //进行交互
        while (true){
            //接收数据


            Thread.sleep(500);

            //发送
            rep.send("wo bu hao");
            rep.recv();
        }
    }
}

2.Pub-Sub模式

pub服务端(只发)——sub客户端(只接)
先启动订阅,再发布
订阅者可以连接多个发布者,轮流接收消息
发布者可以连接多个接受者,同时接收相同数据
如果发布者没有订阅者与之相连,那它发送的消息将直接被丢弃
如果你使用TCP协议,那当订阅者处理速度过慢时,消息会在发布者处堆积
在目前版本的ZMQ中,消息的过滤是在订阅者处进行的。也就是说,发布者会向订阅者发送所有的消息,订阅者会将未订阅的消息丢弃
‘使用sleep延缓发布,避免因握手导致连接正在建立,而数据已发出而丢弃’。

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

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

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

我们知道在建立TCP连接时需要进行三次握手,会耗费几毫秒的时间,而当节点数增加时这个数字也会上升。在这么短的时间里,ZMQ就可以发送很多很多消息了。举例来说,如果建立连接需要耗时5毫秒,而ZMQ只需要1毫秒就可以发送完这1000条消息。

所以需要发布者和订阅者同步,只有当订阅者准备好时发布者才会开始发送消息。有一种简单的方法来同步PUB和SUB,就是让PUB延迟一段时间再发送消息。现实编程中我不建议使用这种方式,因为它太脆弱了,而且不好控制。不过这里我们先暂且使用sleep的方式来解决。

另一种同步的方式则是认为发布者的消息流是无穷无尽的,因此丢失了前面一部分信息也没有关系.

在这里插入图片描述

sub订阅

package zeromqDemo;

import org.zeromq.ZMQ;

//订阅端
public class ZmqSub {
    public static void main(String[] args) {
        //创建zmq实例
        ZMQ.Context zmq = ZMQ.context(1);
        //实例化sub
        ZMQ.Socket sub = zmq.socket(ZMQ.SUB);
        //连接ip和端口
        sub.connect("tcp://localhost:5556");
        //调用subscribe进行订阅数据
        sub.subscribe("".getBytes());
        //接收数据
        while (!Thread.currentThread().isInterrupted()){
            //接收数据
            byte[] recv = sub.recv();
            System.out.println(new String(recv));
        }
        //关闭资源
        sub.close();
        zmq.close();
    }
}

pub发布

package zeromqDemo;

import org.zeromq.ZMQ;

//发布端
public class ZmqPub {
    public static void main(String[] args) throws InterruptedException {
        //zmq
        ZMQ.Context zmq = ZMQ.context(1);
        //实例化pub
        ZMQ.Socket pub = zmq.socket(ZMQ.PUB);
        //绑定ip和端口
        pub.bind("tcp://localhost:5555");
        //发布消息
        int i = 0;
        while (true){
            Thread.sleep(500);
            String str = "hello world";
            pub.send(str+i);
            i++;
        }
    }
}

3.Push-Poll模式

Server端作为Push端,而Client端作为Pull端
多pull——一个push,轮询平均分配
一个pull——多个push,数据存放队列中,轮询平均消费
如果没有消费,数据会放在队列里,不会销毁

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

pull拉

package zeromqDemo;

import org.zeromq.ZMQ;

//推拉模式客户端
public class ZmqPull {
    public static void main(String[] args) {
        //zmq
        ZMQ.Context zmq = ZMQ.context(1);
        //pull
        ZMQ.Socket pull = zmq.socket(ZMQ.PULL);
        //连接ip和端口
        //如果pull多个,就connect多个tcp
        pull.connect("tcp://localhost:5555");
        //接收数据
        while (!Thread.currentThread().isInterrupted()){
            //接收数据
            byte[] recv = pull.recv();
            System.out.println(new String(recv));
        }
        //释放资源
        pull.close();
        zmq.close();
    }
}

push推

package zeromqDemo;

import org.zeromq.ZMQ;

//推拉模式服务端
public class ZmqPush {
    public static void main(String[] args) throws InterruptedException {
        //实例化zmq
        ZMQ.Context zmq = ZMQ.context(1);
        //push
        ZMQ.Socket push = zmq.socket(ZMQ.PUSH);
        //绑定ip和端口
        push.bind("tcp://localhost:5555");
        //发布消息
        int i = 0;
        while (true){
            String str = "hello world";
            push.send(str+i);
            i++;
            Thread.sleep(500);
        }
    }
}

2.代理服务

发布-订阅代理

我们经常会需要将发布-订阅模式扩充到不同类型的网络中。比如说,有一组订阅者是在外网上的,我们想用广播的方式发布消息给内网的订阅者,而用TCP协议发送给外网订阅者。
我们要做的就是写一个简单的代理服务装置,在发布者和外网订阅者之间搭起桥梁。这个装置有两个端点,一端连接内网上的发布者,另一端连接到外网上。它会从发布者处接收订阅的消息,并转发给外网上的订阅者们。
我们称这个装置为代理,因为它既是订阅者,又是发布者。这就意味着,添加该装置时不需要更改其他程序的代码,只需让外网订阅者知道新的网络地址即可。

在这里插入图片描述

外网订阅端

package zmq;

import org.zeromq.ZMQ;

//订阅端
public class Sub {
    public static void main(String[] args) {
        //创建zmq实例
        ZMQ.Context zmq = ZMQ.context(1);
        //实例化sub
        ZMQ.Socket sub = zmq.socket(ZMQ.SUB);
        //连接ip和端口
        sub.connect("tcp://localhost:1011");
        //调用subscribe进行订阅数据
        sub.subscribe("".getBytes());
        //接收数据
        while (!Thread.currentThread().isInterrupted()){
            //接收数据
            byte[] recv = sub.recv();
            System.out.println(new String(recv));
        }
        //关闭资源
        sub.close();
        zmq.close();
    }
}

代理

package zmq;

import org.zeromq.ZMQ;

public class ZmqPro {
    public static void main(String[] args) throws InterruptedException {
        ZMQ.Context context = ZMQ.context(1);
        ZMQ.Socket sub = context.socket(ZMQ.SUB);
        ZMQ.Socket pub = context.socket(ZMQ.PUB);
        sub.connect("tcp://localhost:1012");
        sub.subscribe("".getBytes());
        pub.bind("tcp://localhost:1011");


        //调用代理服务
        //null代表是否在本地备份
        ZMQ.proxy(sub,pub,null);
        pub.close();
        sub.close();
        context.close();

    }
}

内网发布端

package zmq;
import org.zeromq.ZMQ;


//发布端
public class Pub {
    public static void main(String[] args) throws InterruptedException {

        //zmq
        ZMQ.Context zmq = ZMQ.context(1);
        //实例化pub
        ZMQ.Socket pub = zmq.socket(ZMQ.PUB);
        //绑定ip和端口
        pub.bind("tcp://localhost:1012");
        //发布消息
        int i = 0;
        while (true){
            Thread.sleep(500);
            String str = "hello world";
            pub.send(str+i);
            i++;
        }
    }
}
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值