[中间件]--ZMQ笔记总结(java)

官网地址:https://zeromq.org/get-started/

java-API文档:https://www.javadoc.io/doc/org.zeromq/jeromq/latest/index.html

ZreoMQ支持许多语言,这里用的都是java的。

本文图片是复制粘贴的,这个也没什么好画的,主要就是分享一下消息模式的代码。

原文链接:ZMQ简介

官方github:代码

在这里插入图片描述

一、简介

高性能的异步消息传递库,旨在用于分布式或者并发应用程序。它提供了一个消息队列,但与面向消息的中间件不同,ZeroMQ 系统可以在没有专用消息代理的情况下运行。

ZeroMQ 支持各种传输(TCP、进程内、进程间、多播<一个数据发送给不同子网中的一组接收者>、WebSocket 等)上的常见消息传递模式(发布/订阅、请求/回复、客户端/服务器等),使进程间消息传递变得简单作为线程间消息传递。这使得代码清晰、模块化且非常易于扩展。

ZeroMQ的0的意思:

0表示无代理、零延迟、零成本和零管理。

通俗的来说,零是指渗透到项目中的极简主义文化。我们通过消除复杂性1而不是暴露新功能来增加功能。


二、引入maven项目

    <dependency>
      <groupId>org.zeromq</groupId>
      <artifactId>jeromq</artifactId>
      <version>0.5.2</version>
    </dependency>

    <!-- for the latest SNAPSHOT --><!--最新的,这俩一个就够了-->
    <dependency>
      <groupId>org.zeromq</groupId>
      <artifactId>jeromq</artifactId>
      <version>0.5.3-SNAPSHOT</version>
    </dependency>

    <!-- If you can't find the latest snapshot -->
    <repositories>
      <repository>
        <id>sonatype-nexus-snapshots</id>
        <url>https://oss.sonatype.org/content/repositories/snapshots</url>
        <releases>
          <enabled>false</enabled>
        </releases>
        <snapshots>
          <enabled>true</enabled>
        </snapshots>
       </repository>
    </repositories>

三、消息模式

1. 应答模式

REQ-REP:客户端首先要使用send()发送消息,再使用recv()接收,如果打乱了顺序,就会报错。服务端也是这样,必须先进行接收,后进行发送。

在这里插入图片描述

  1. 客户端
public class Client {
    
    
    public static void main(String[] args) {
    
    
       ZContext context = new ZContext(1);
        ZMQ.Socket socket = context.createSocket(SocketType.REQ);
        socket.connect("tcp://localhost:5555");
        while (true) {
    
    
            socket.send("1");
            byte[] recv = socket.recv();
            System.out.println(new String(recv));
        }
        // 这是需要关流的
        context.close();
        socket.close();
    }
}
  1. 服务端
public class Server {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        ZContext context = new ZContext(1);
        ZMQ.Socket socket = context.createSocket(SocketType.REP);
        socket.bind("tcp://*:5555");
        while (true){
    
    
            byte[] recv = socket.recv();
            System.out.println(new String(recv));
            socket.send("接收到了,OKK");
        }
    }
}

2. 订阅发布模式

PUB-SUB:这个组合是异步的,客户单在一个循环体中使用recv()接收消息,使用SUB套接字时,不可以发送消息。服务端也是,只可以send()发送消息,不可以使用PUB套接字接收消息。

注意:因为无法得知SUB是何时开始接收消息的,就算先打开了SUB,后打开PUB发送消息,这时候的SUB还是会丢失一些消息,因为TCP建立链接是需要时间的。可以先让PUB休眠一段时间,确定链接到了再发送消息。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tXXo7Jwy-1641614318627)(https://secure2.wostatic.cn/static/eMzSj3L63Ye3D5JEbzAQav/image.png)]

  1. 客户端
public class ClientA {
    
    
    public static void main(String[] args) {
    
    
       ZContext context = new ZContext(3);
        ZMQ.Socket socket = context.createSocket(SocketType.SUB);
        socket.connect("tcp://localhost:5555");
        // 设置,指订阅前缀为A 的消息,设置为“” 表示全部接受
        socket.subscribe("A".getBytes());
        while (!Thread.currentThread().isInterrupted()) {
    
    
            byte[] recv = socket.recv();
            System.out.println(new String(recv));
        }
    }
}
public class ClientB {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        ZContext context = new ZContext(3);
        ZMQ.Socket socket = context.createSocket(SocketType.SUB);
        socket.connect("tcp://localhost:5555");
        socket.subscribe("B".getBytes());
        while (!Thread.currentThread().isInterrupted()) {
    
    
            byte[] recv = socket.recv();
            System.out.println(new String(recv));
        }
    }
}
  1. 服务端
public class Server {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        ZContext context = new ZContext(1);
        ZMQ.Socket socket = context.createSocket(SocketType.PUB);
        socket.bind("tcp://*:5555");
        // 设置一定的休眠时间,确保能链接上。
        Thread.sleep(1000);
        Scanner sc = new Scanner(System.in);
        while (true) {
    
    
            String next = sc.next();
            socket.send(next.getBytes(StandardCharsets.UTF_8), 0);
        }
    }
}

3. 管道模式

PUSH-PULL:将数据分发到下游链接的所有节点,数据是均衡分布的(循环发布)。

在这里插入图片描述

  1. 发送器
//定义最上面的分发任务的
public class Master {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        ZContext context = new ZContext(1);
        ZMQ.Socket socket = context.createSocket(SocketType.PUSH);
        // 工人会链接到这个用来接受消息的
        socket.bind("tcp://*:5555");
        // 只执行一次。告诉接收器开始工作
        ZMQ.Socket slink = context.createSocket(SocketType.PUSH);
        slink.connect("tcp://localhost:6666");
        // 设置休眠,保证连接
        Thread.sleep(1000);
        //生成100个任务,分发给工人
        slink.send("开始工作");
        for (int i = 1; i < 101; i++) {
    
    
        // 设置线程休眠,观察动态加入工作
            Thread.sleep(1000)
            socket.send(String.valueOf(i));
        }
    }
}
  1. 工作者
//定义 工作者,(可以多写几个 )除了执行任务逻辑不同,其他的相同
public class Worker1 {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        ZContext context = new ZContext(1);
        //链接发送器
        ZMQ.Socket socket = context.createSocket(SocketType.PULL);
        socket.connect("tcp://localhost:5555");
        //链接接收器
        ZMQ.Socket slink = context.createSocket(SocketType.PUSH);
        slink.connect("tcp://localhost:6666");
        //执行任务
        while (true){
    
    
            //接受数据
            byte[] recv = socket.recv();
            String s = new String(recv);
            System.out.println(s);
            //发送数据
//            Thread.sleep(1000);
            Thread.sleep(100);
            slink.send("A发送的消息:"+s);
        }
    }
}
  1. 接收器
//定义接收器
public class Receiver {
    
    
    public static void main(String[] args) {
    
    
        ZContext context  = new ZContext(1);
        ZMQ.Socket socket = context.createSocket(SocketType.PULL);
        socket.bind("tcp://*:6666");
        // 只负责接收数据
        while (true){
    
    
            System.out.println(new String(socket.recv()));
        }
    }
}

我遇到的一些问题:

上面描述的,必须得在确保链接上的情况发送消息,不然可能会出现,发送的消息,全部涌入一个节点,或者消息发送出去没有接收到。(这个ZMQ好快的)

再就是:

  1. 如果你停止接收器,消息是不会丢失的 ,上面的工作者会阻塞,当你重新启动接收器,会发现消息还是会整常的接收到。
  2. 当程序运行时,如果其中一个工作者宕机了,消息会就会均衡分配到其他的工作者;当宕机的工作者重新开机,在它链接到发送者之后,还是可以正常的收发消息,这就是上面描述的动态添加节点。

官网还有两个模式,我好像也用不到,之后用到了再继续给大家分享。

猜你喜欢

转载自blog.csdn.net/m0_56186460/article/details/122378288