RabbitMQ之消息发布订阅与信息持久化技术

Stella981
• 阅读 723

===

信息发布与订阅

Rabbit的核心组件包含Queue(消息队列)和Exchanges两部分,Exchange的主要部分就是对信息进行路由,通过将消息队列绑定到Exchange上,则可以实现订阅形式的消息发布及Publish/Subscribe在这种模式下消息发布者只需要将信息发布到相应的Exchange中,而Exchange则自动将信息分发到不同的Queue当中。

    这种模式下Exchange充当的角色

    在命令行中可以使用

sudo rabbitmqctl list_exchanges

sudo rabbitmqctl list_bindings

    分别查看当前系统种存在的Exchange和Exchange上绑定的Queue信息。

消息发布者EmitLog.java

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

public class EmitLog {

    private static final String  EXCHANGE_NAME="logs";
    
    public static void main(String[] args) throws java.io.IOException{
        
        //创建链接工厂
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        //创建链接
        Connection connection = factory.newConnection();
        
        //创建信息管道
        Channel channel = connection.createChannel();
        
        //生命Exchange 非持久化
        channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
        
        String message = "Message "+Math.random();
        
        //第一个参数是对应的Exchange名称,如果为空则使用默认Exchange
        channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());
        System.out.println("[x] Sent '"+message+"'");
        
        //关闭链接
        channel.close();
        connection.close();
        
    }
    
}

消息消费者ReceiveLogs.java

import java.io.IOException;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.ConsumerCancelledException;
import com.rabbitmq.client.QueueingConsumer;
import com.rabbitmq.client.ShutdownSignalException;

public class ReceiveLogs {

    private static final String EXCHANGE_NAME = "logs";

    public static void main(String[] args) throws IOException, ShutdownSignalException, ConsumerCancelledException, InterruptedException {

        //创建链接工厂
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        //创建链接
        Connection connection = factory.newConnection();
        
        //创建消息管道
        Channel channel = connection.createChannel();

        //声明Exchange
        channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
        
        //利用系统自动声明一个非持久化的消息队列,并返回唯一的队列名称
        String queueName = channel.queueDeclare().getQueue();

        //将消息队列绑定到Exchange
        channel.queueBind(queueName, EXCHANGE_NAME, "");

        System.out.println(" [*] Waiting for messages. To exit press CTRL+C");

        //声明一个消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        channel.basicConsume(queueName, true, consumer);

        while (true) {
            
            //循环获取信息
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println(" [x] Received '" + message + "'");
            
        }

    }

}

运行时启动一个EmitLog.java多个ReceiveLogs.java则可以看到发布者每次发布信息,只要绑定到了相应Exchange的消费者都可以获取到信息。

RabbitMQ信息持久化技术

    上面的例子中我们实现了Publisher/Subscribe的消息分发方式,但是其中存在一些问题。比如当我们运行一个ReceiveLog都对应了一个特定的消息队列,可以利用list_queues进行查看,同时这些消息队列是帮到到名为logs的Exchange中,这是发布消息每个消费者都可以接收到,可以当关闭ReceiveLog程序后这些消息队列就都会自动销毁,因为他们是非持久化的。同样对于EmitLog程序也一样,每次关闭后之前生命的Exchange也将自动销毁。

    这就产生了一些问题。如果当ReceiveLog为运行时,此时就并没有一个消息队列是绑定到Exchange上的,在发布消息后再启动ReceiveLog程序是无法接受到之前发布的信息。这就是为什么要进行消息的持久化。

    通过持久化技术,我们可以生命一个持久化的Exchange,以及持久化的Queue这样,在把Queue绑定到Exchange后,即使没有消费者程序运行,信息依然能保存在Queue当中,当下次启动消费者程序时依然能获取到发布的所有信息。就好比当一个消费者程序在执行消息序列中的任务时,如果突然出现了异常那么重新启动后,依然能从上一次发生错误的位置继续运行,对于某些需要一个有序性和连续性的操作,这点显的尤为重要。

    下面还是给出一个例子,在持久化过程中,可以借助list_exchanges,list_bindings,list_queues来查看服务器中相关信息来帮组分析过程。

Publisher.java

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;

public class Publisher {
    
    private static final String  EXCHANGE_NAME="persi";//定义Exchange名称
    private static final boolean durable = true;//消息队列持久化
    
    public static void main(String[] args) throws java.io.IOException {

        ConnectionFactory factory = new ConnectionFactory();//创建链接工厂
        factory.setHost("localhost");
        Connection connection = factory.newConnection();//创建链接
        Channel channel = connection.createChannel();//创建信息通道
                
        channel.exchangeDeclare(EXCHANGE_NAME, "fanout", durable);//创建交换机并生命持久化

        String message = "Hello Wrold "+Math.random();
                //消息的持久化
        channel.basicPublish(EXCHANGE_NAME, "", MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes());
        
        System.out.println("[x] Sent '" + message + "'");

        channel.close();
        connection.close();

    }
    
}

Subscriber.java

public class Subscriber {

    
    //private static final String[] QUEUE_NAMES= {"que_001","que_002","que_003","que_004","que_005"};
    private static final String[] QUEUE_NAMES= {"que_006","que_007","que_008","que_009","que_0010"};
    
    public static void main(String[] args){

        for(int i=0;i<QUEUE_NAMES.length;i++){
            
            SubscriberThead sub = new SubscriberThead(QUEUE_NAMES[i]);
            Thread t = new Thread(sub);
            t.start();
            
        }
        
    }
}

SubscriberThead.java

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;
import com.rabbitmq.client.AMQP.Queue.DeclareOk;

public class SubscriberThead implements Runnable {

    private String queue_name = null;
    private static final String EXCHANGE_NAME = "persi";// 定义交换机名称
    private static final boolean durable = true;//消息队列持久化
    
    public SubscriberThead(String queue_name) {
        
        this.queue_name = queue_name;
    
    }

    @Override
    public void run() {

        try{
        
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        channel.exchangeDeclare(EXCHANGE_NAME, "fanout", durable);

        DeclareOk ok = channel.queueDeclare(queue_name, durable, false,
                false, null);
        String queueName = ok.getQueue();
        

        channel.queueBind(queueName, EXCHANGE_NAME, "");

        System.out.println(" ["+queue_name+"] Waiting for messages. To exit press CTRL+C");

        channel.basicQos(1);//消息分发处理
        QueueingConsumer consumer = new QueueingConsumer(channel);
        channel.basicConsume(queueName, false, consumer);

        while (true) {

            Thread.sleep(2000);
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println(" ["+queue_name+"] Received '" + message + "'");
            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);

        }
        }catch(Exception e){
            
            e.printStackTrace();
        }
        

    }

}

通过持久化处理后rabbitMQ将保存Exchange信息以及Queue信息,甚至在rabbitMQ服务器关闭后信息依然能保存,这样就提供了消息传递的可靠性

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
RabbitMQ如何通过持久化保证消息99.99%不丢失?
1\.本篇概要要解决该问题,就要用到RabbitMQ中持久化的概念,所谓持久化,就是RabbitMQ会将内存中的数据(Exchange交换器,Queue队列,Message消息)固化到磁盘,以防异常情况发生时,数据丢失。其中,RabblitMQ的持久化分为三个部分:1.交换器(Exchange
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
RabbitMQ 基础概念介绍
AMQP消息模型RabbitMQ是基于AMQP(高级消息队列协议)的一个开源实现,其内部实际也是AMQP的基本概念。AMQP的消息发送流程有如下几个步骤:1.消息生产者(producer)将消息发布到Exchange中;2.Exchange根据队列的绑定关系将消息分发到不同的队列(Queue
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这