rabbitMq的安装与demo应用 java

rabbitMq是使用erlang语言编写的,在安装rabbitMq之前需要先准备erlang环境

以ubuntu系统为例

下载otp,我的版本是20.0  https://www.erlang.org/downloads

解压压缩文件

root@ubuntu:/usr/local/otp# tar -xvf otp_src_20.0

在安装erlang之前,我们可能需要先安装ncurses

如果您的linux没有安装ncurses,则在安装erlang时可能会报o such file or directory error: No curses library functions foundio这个错误

下载ncurses源码http://ftp.gnu.org/gnu/ncurses/ 我的版本是6.1

解压、编译并安装ncurses

root@ubuntu:/usr/local/otp/ncurses-6.1# ./configure
root@ubuntu:/usr/local/otp/ncurses-6.1# make
root@ubuntu:/usr/local/otp/ncurses-6.1# make install

安装完ncurses后我们再编译、安装erlang

root@ubuntu:/usr/local/otp/otp_src_20.0# ./configure --prefix=/usr/local/erlang
root@ubuntu:/usr/local/otp/otp_src_20.0# make
root@ubuntu:/usr/local/otp/otp_src_20.0# make install

erlang安装目录下的文件如下

root@ubuntu:/usr/local/erlang# ls
bin  lib

配置环境变量 vim /etc/profile

JAVA_HOME=/usr/jdk1.8
CLASSPATH=.:$JAVA_HOME/lib/
PATH=$PATH:$JAVA_HOME/bin
GIT_HOME=/usr/local/bin
PATH=$PATH:$GIT_HOME
M2_HOME=/usr/maven/apache-maven-3.5.4
PATH=$PATH:$M2_HOME/bin
ERL_HOME=/usr/local/erlang
PATH=$PATH:$ERL_HOME/bin
export PATH JAVA_HOME CLASSPATH GIT_HOME M2_HOME ERL_HOME

CC=gcc
export CC 

查看erlang版本

root@ubuntu:/usr/local/erlang# source /etc/profile
root@ubuntu:/usr/local/erlang# erl -version
Erlang (SMP,ASYNC_THREADS,HIPE) (BEAM) emulator version 9.0
root@ubuntu:/usr/local/erlang#

下载rabbitMq

https://github.com/rabbitmq/rabbitmq-server/releases/tag/v3.7.10

我的版本为3.7.10

解压

root@ubuntu:/home/wl/mq# tar -xvf rabbitmq-server-generic-unix-3.7.10.tar.xz

进入sbin目录 启动broker

root@ubuntu:/home/wl/mq/rabbitmq_server-3.7.10/sbin# ./rabbitmq-server 

启动日志为

  ##  ##
  ##  ##      RabbitMQ 3.7.10. Copyright (C) 2007-2018 Pivotal Software, Inc.
  ##########  Licensed under the MPL.  See http://www.rabbitmq.com/
  ######  ##
  ##########  Logs: /home/wl/mq/rabbitmq_server-3.7.10/var/log/rabbitmq/[email protected]
                    /home/wl/mq/rabbitmq_server-3.7.10/var/log/rabbitmq/rabbit@ubuntu_upgrade.log

              Starting broker...
 completed with 0 plugins.

后台启动命令

./rabbitmq-server -detached

关闭broker服务

./rabbitmqctl stop

启动rabbitmq自带的web控制界面插件(默认监听端口为15672,默认账号密码均为guest)

 ./rabbitmq-plugins enable rabbitmq_management

关闭web控制界面插件

 ./rabbitmq-plugins disable rabbitmq_management

全部启动后浏览器访问http://localhost:15672

在rabbitmq 3.3.0开始禁止使用guest/guest权限通过除localhost外的访问

修改ebin目录下的rabbit.app文件

将{loopback_users, [<<"guest">>]}修改为{loopback_users, []}。下面是修改后的部分配置

            {default_user, <<"guest">>},
            {default_pass, <<"guest">>},
            {default_user_tags, [administrator]},
            {default_vhost, <<"/">>},
            {default_permissions, [<<".*">>, <<".*">>, <<".*">>]},
            {loopback_users, []},
            {password_hashing_module, rabbit_password_hashing_sha256},
            {server_properties, []},
            {collect_statistics, none},
            {collect_statistics_interval, 5000},
            {mnesia_table_loading_retry_timeout, 30000},

重新启动rabbitmq

非localhost也可以访问

rabbitmq环境准备完毕 下面是java demo(我的是spring-boot项目)

java客户端官方文档参考 https://www.rabbitmq.com/clients.html 中的java部分

http://rabbitmq.mr-ping.com/  有中文文档,可以先了解rabbitmq中的相应概念

导入依赖

<!-- https://mvnrepository.com/artifact/com.rabbitmq/amqp-client -->
    <dependency>
      <groupId>com.rabbitmq</groupId>
      <artifactId>amqp-client</artifactId>
      <version>5.2.0</version>
    </dependency>

ConnectionFactory配置

package com.wl.mq.config;

import com.rabbitmq.client.ConnectionFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Created by Administrator on 2021/3/12.
 */
@Configuration
public class RabbitMqConfig {

    @Bean
    public ConnectionFactory rabbitMqConnectionFactory(){

        ConnectionFactory factory = new ConnectionFactory();
        factory.setUsername("guest");
        factory.setPassword("guest");
        factory.setVirtualHost("/");
        factory.setHost("192.168.92.128");
        factory.setPort(5672);
        factory.setAutomaticRecoveryEnabled(false);
        return factory;
    }


}

1.queue队列(类似activeMq中的queue队列,无论多少个服务监听一个队列,都只会有一个服务执行消费。即点对点  point to point)

发送queue消息的RabbitQueueProducerService.java

package com.wl.mq.rabbit;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConnectionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * Created by Administrator on 2021/3/12.
 */
@Service
public class RabbitQueueProducerService {

    private Channel channel;

    @Autowired
    public RabbitQueueProducerService(ConnectionFactory factory) throws Exception{
        this.channel = factory.newConnection("queue-producer-connection").createChannel();
    }

    /**
     *  发送一个队列 queue
     * @param destination  队列名称
     * @param message    消息
     *  basicPublish中exchange为空字符串
     */
    public void sendQueueMessage(String destination,String message)throws Exception{
        //声明一个queue(destination 就是queue的名称)
        channel.queueDeclare(destination,false,false,false,null);

        channel.basicPublish("",destination,null,message.getBytes());

    }



}

消费queue的RabbitQueueConsumerService(监听的queue名为rabbit_queue)

package com.wl.mq.rabbit;

import com.rabbitmq.client.*;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;

/**
 * Created by Administrator on 2021/3/12.
 * 消费queue队列
 */
@Service
public class RabbitQueueConsumerService implements InitializingBean{

    private Channel channel;

    @Autowired
    public RabbitQueueConsumerService(ConnectionFactory factory) throws Exception{
        String connectionName = "consumer-queue-connection";
        this.channel = factory.newConnection(connectionName).createChannel();
    }

    private void initQueueConsumer()throws Exception{
        String queueName = "rabbit_queue";
        channel.queueDeclare(queueName,false,false,false,null);
        //channel.basicQos(1);
        /*
            autoAck false表示服务器默认该消息没有被消费,因此每次重新启动这个服务都会导致之前的消息被重新消费一次。
            如何手动通知服务器该消息已经被消费呢?需要在handleDelivery代码块中执行channel.basicAck(envelope.getDeliveryTag(), true);
         */
        boolean autoAck = false;
        channel.basicConsume(queueName,autoAck,new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body) throws IOException{
                channel.basicAck(envelope.getDeliveryTag(), true);
                System.out.println("=======================rabbit_queue=====================");
                System.out.println(new String(body));
                System.out.println("=======================rabbit_queue=====================");
            }
        });
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        initQueueConsumer();
    }
}

注意上面消费方法中的autoAck属性

如果autoAck为false,则表示服务器默认该消息没有被成功消费,因此每次重新启动这个服务都会导致之前被消费的消息(无论是否真的成功消费)都会被重新消费

如果autoAck为true,则表示服务器默认该消息已被成功消费,即使该消息没有被成功消费。(不会触发补偿机制)

如果您的autoAck为false,但是消息已被成功消费(handleDelivery方法被成功执行),则需要手动告诉rabbit服务器消息已被成功执行:在handleDelivery方法的业务执行完毕后加上channel.basicAck(envelope.getDeliveryTag(), true);

通过autoAck这个属性,可以实现执行失败的消息的重试!设置autoAck为false  捕获handleDelivery执行异常后执行channel.basicAck(envelope.getDeliveryTag(), false);

如果我们的RabbitQueueConsumerService 部署到了两个服务器上,queue消息会被均匀的消费(例如有1-10条消息 ,服务a消费奇数,服务b消费偶数)

加上channel.basicQos(1);表示同一时间服务器只发送一条消息。消息就不会均匀的消费(机器性能强的消费数据就会多)

测试代码

package com.wl.mq;

import com.wl.mq.rabbit.RabbitQueueProducerService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * Created by Administrator on 2021/3/12.
 */
@SpringBootTest(classes = Application.class)
@RunWith(SpringJUnit4ClassRunner.class)
//@Ignore
public class RabbitMqTest {

    @Autowired
    private RabbitQueueProducerService queueProducerService;

    @Test
    public void testSendQueueMessage()throws Exception{
        String destination = "rabbit_queue";
        String message = "hello this is rabbit queue message";
        queueProducerService.sendQueueMessage(destination,message);
        Thread.sleep(1000000);
    }
    
}

测试日志

通过web控制台发送queue消息

在queue栏目点击rabbit-queue

发送消息

日志

2.发布/订阅(将消息投送给多个消费者,这种模式被称为“发布/订阅。类似activemq的topic)

在介绍下面的内容之前,您可能需要先了解rabbitMq中的交换器概念  http://rabbitmq.mr-ping.com/AMQP/AMQP_0-9-1_Model_Explained.html

主要流程:生产者将消息发布到交换器,消费者将交换器绑定到队列,然后监听消息。

参考http://rabbitmq.mr-ping.com/tutorials_with_csharp/publish&subscribe.html

RabbitPubSubProducerService.java

package com.wl.mq.rabbit;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConnectionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * Created by Administrator on 2021/3/12.
 */
@Service
public class RabbitPubSubProducerService {
    private Channel channel;

    @Autowired
    public RabbitPubSubProducerService(ConnectionFactory factory) throws Exception{
        this.channel = factory.newConnection().createChannel();
    }

    /**
     * @param exchange  交换器名称
     * @param message   消息
     */
    public void sendMessage(String exchange,String message) throws Exception{

        channel.exchangeDeclare(exchange,"fanout");
        channel.basicPublish(exchange,"",null,message.getBytes());
    }

}

pubsub消费者(为了测试方便多加了一个initConsumerA)

package com.wl.mq.rabbit;

import com.rabbitmq.client.*;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;

/**
 * Created by Administrator on 2021/3/12.
 */
@Service
public class RabbitPubSubConsumerService implements InitializingBean{

    private Channel channel;

    @Autowired
    public RabbitPubSubConsumerService(ConnectionFactory factory) throws Exception{
        String connectionName = "consumer-pubsub-connection";
        this.channel = factory.newConnection(connectionName).createChannel();
    }

    private void initConsumer() throws Exception{

        String exchange = "rabbit-pubsub-exchange";
        //声明交换器
        channel.exchangeDeclare(exchange,"fanout");
        //获取rabbitmq默认生成的queue名称
        String queueName = channel.queueDeclare().getQueue();
        //绑定queue和交换器
        channel.queueBind(queueName,exchange,"");
        /*
            autoAck false表示服务器默认该消息没有被消费,因此每次重新启动这个服务都会导致之前的消息被重新消费一次。
            如何手动通知服务器该消息已经被消费呢?需要在handleDelivery代码块中执行channel.basicAck(envelope.getDeliveryTag(), true);
         */
        boolean autoAck = false;
        channel.basicConsume(queueName,autoAck,new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {
                channel.basicAck(envelope.getDeliveryTag(), true);
                System.out.println("=======================rabbit-pubsub-exchange=====================");
                System.out.println(new String(body));
                System.out.println("=======================rabbit-pubsub-exchange=====================");
            }
        });

    }

    private void initConsumerA() throws Exception{

        String exchange = "rabbit-pubsub-exchange";
        //声明交换器
        channel.exchangeDeclare(exchange,"fanout");
        //获取rabbitmq默认生成的queue名称
        String queueName = channel.queueDeclare().getQueue();
        //绑定queue和交换器
        channel.queueBind(queueName,exchange,"");
        /*
            autoAck false表示服务器默认该消息没有被消费,因此每次重新启动这个服务都会导致之前的消息被重新消费一次。
            如何手动通知服务器该消息已经被消费呢?需要在handleDelivery代码块中执行channel.basicAck(envelope.getDeliveryTag(), true);
         */
        boolean autoAck = false;
        channel.basicConsume(queueName,autoAck,new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {
                channel.basicAck(envelope.getDeliveryTag(), true);
                System.out.println("=======================rabbit-pubsub-exchange-consumerA=====================");
                System.out.println(new String(body));
                System.out.println("=======================rabbit-pubsub-exchange-consumerA=====================");
            }
        });

    }

    @Override
    public void afterPropertiesSet() throws Exception {
        initConsumer();
        initConsumerA();
    }
}

测试代码

    @Autowired
    private RabbitPubSubProducerService pubSubProducerService;

    @Test
    public void testPubSubSendMessage() throws Exception{
        String exchange = "rabbit-pubsub-exchange";
        String message = "hello this is rabbit pubsub message";
        pubSubProducerService.sendMessage(exchange,message);
        Thread.sleep(100000);
    }

测试日志

所有绑定rabbit-pubsub-exchange交换器的channel都消费了

与上面的queue不同的是,queue只需要声明队列,且不需要声明和绑定交换器(queue使用的是默认的交换器)而pub/sub模式需要声明交换器并将交换器与队列进行绑定,且可以不用声明队列(rabbitmq会生成默认的队列channel.QueueDeclare(),队列名称类似amq.gen-JzTY20BRgKO-HjmUJj0wLg)

注意:使用rabbit默认生成的queue在集群环境下会出现重复消费的问题,因为rabbit默认生成的queue都是不一样的。解决办法就是在consumer中自定义声明queue。

String queueName = channel.queueDeclare("pubsub-consumer-queue",false,false,false,null).getQueue();

上面的代码在集群环境下,会导致同一段代码被执行多次。下面我们通过一个terminal窗口和一个debug窗口来测试

web控制台发送消息

debug窗口打印

terminal窗口打印

这是因为我们在消费方法中绑定的queue是由rabbit默认生成的,导致相同的代码部署到两个服务器上,生成的queue是不一样的。这样exchang就相当于绑定到了2个各不相同的queue上。导致重复消费。

解决办法:在consumer中自定义声明queue 修改后的代码如下

package com.wl.mq.rabbit;

import com.rabbitmq.client.*;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;

/**
 * Created by Administrator on 2021/3/12.
 */
@Service
public class RabbitPubSubConsumerService implements InitializingBean{

    private Channel channel;

    @Autowired
    public RabbitPubSubConsumerService(ConnectionFactory factory) throws Exception{
        String connectionName = "consumer-pubsub-connection";
        this.channel = factory.newConnection(connectionName).createChannel();
    }

    private void initConsumer() throws Exception{

        String exchange = "rabbit-pubsub-exchange";
        //声明交换器
        channel.exchangeDeclare(exchange,"fanout");
        //自定义声明queue
        String queueName = channel.queueDeclare("pubsub-consumer-queue",false,false,false,null).getQueue();
        //绑定queue和交换器
        channel.queueBind(queueName,exchange,"");
        /*
            autoAck false表示服务器默认该消息没有被消费,因此每次重新启动这个服务都会导致之前的消息被重新消费一次。
            如何手动通知服务器该消息已经被消费呢?需要在handleDelivery代码块中执行channel.basicAck(envelope.getDeliveryTag(), true);
         */
        boolean autoAck = false;
        channel.basicConsume(queueName,autoAck,new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {
                channel.basicAck(envelope.getDeliveryTag(), true);
                System.out.println("=======================rabbit-pubsub-exchange=====================");
                System.out.println(new String(body));
                System.out.println("=======================rabbit-pubsub-exchange=====================");

            }
        });

    }

    private void initConsumerA() throws Exception{

        String exchange = "rabbit-pubsub-exchange";
        //声明交换器
        channel.exchangeDeclare(exchange,"fanout");
        //自定义声明queue
        String queueName = channel.queueDeclare("pubsub-consumerA-queue",false,false,false,null).getQueue();
        //绑定queue和交换器
        channel.queueBind(queueName,exchange,"");
        /*
            autoAck false表示服务器默认该消息没有被消费,因此每次重新启动这个服务都会导致之前的消息被重新消费一次。
            如何手动通知服务器该消息已经被消费呢?需要在handleDelivery代码块中执行channel.basicAck(envelope.getDeliveryTag(), true);
         */
        boolean autoAck = false;
        channel.basicConsume(queueName,autoAck,new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {
                channel.basicAck(envelope.getDeliveryTag(), true);
                System.out.println("=======================rabbit-pubsub-exchange-consumerA=====================");
                System.out.println(new String(body));
                System.out.println("=======================rabbit-pubsub-exchange-consumerA=====================");
            }
        });

    }

    @Override
    public void afterPropertiesSet() throws Exception {
        initConsumer();
        initConsumerA();
    }
}

只修改了一处String queueName = channel.queueDeclare(queueName,false,false,false,null).getQueue();

任然在debug和terminal窗口中启动服务

发送消息

terminal 窗口消费了(请忽略乱码)

debug窗口未消费

3.路由模式

RabbitRouteProducerService.java(生产者)

package com.wl.mq.rabbit;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConnectionFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * Created by Administrator on 2021/3/12.
 */
@Service
public class RabbitRouteProducerService{

    private Channel channel;

    @Autowired
    public RabbitRouteProducerService(ConnectionFactory factory) throws Exception{
        String connectionName = "producer-route-connection";
        this.channel = factory.newConnection(connectionName).createChannel();
    }
    
    public void sendMessage(String exchange,String routeKey,String message) throws Exception{
        channel.exchangeDeclare(exchange,"direct");
        channel.basicPublish(exchange,routeKey,null,message.getBytes());
    }
    
    
}

RabbitRouteConsumerService.java(消费者。为了测试方便多加了一个initConsumerA)

package com.wl.mq.rabbit;

import com.rabbitmq.client.*;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;

/**
 * Created by Administrator on 2021/3/12.
 */
@Service
public class RabbitRouteConsumerService implements InitializingBean {

    private Channel channel;

    @Autowired
    public RabbitRouteConsumerService(ConnectionFactory factory) throws Exception{
        String connectionName = "consumer-route-connection";
        this.channel = factory.newConnection(connectionName).createChannel();
    }
    
    private void initConsumer() throws Exception{

        String exchange = "rabbit-route-exchange";
        String routeKey = "rabbit-route-key";
        //声明交换器
        channel.exchangeDeclare(exchange,"direct");
        //自定义声明queue
        String queueName = channel.queueDeclare("route-consumer-queue",false,false,false,null).getQueue();
        //绑定queue和交换器
        channel.queueBind(queueName,exchange,routeKey);
        /*
            autoAck false表示服务器默认该消息没有被消费,因此每次重新启动这个服务都会导致之前的消息被重新消费一次。
            如何手动通知服务器该消息已经被消费呢?需要在handleDelivery代码块中执行channel.basicAck(envelope.getDeliveryTag(), true);
         */
        boolean autoAck = false;
        channel.basicConsume(queueName,autoAck,new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {
                channel.basicAck(envelope.getDeliveryTag(), true);
                System.out.println("=======================rabbit-route-exchange=====================");
                System.out.println(new String(body));
                System.out.println("=======================rabbit-route-exchange=====================");

            }
        });
    }

    private void initConsumerA() throws Exception{

        String exchange = "rabbit-route-exchange";
        String routeKey = "rabbit-route-key-A";
        //声明交换器
        channel.exchangeDeclare(exchange,"direct");
        //自定义声明queue
        String queueName = channel.queueDeclare("route-consumerA-queue",false,false,false,null).getQueue();
        //绑定queue和交换器
        channel.queueBind(queueName,exchange,routeKey);
        /*
            autoAck false表示服务器默认该消息没有被消费,因此每次重新启动这个服务都会导致之前的消息被重新消费一次。
            如何手动通知服务器该消息已经被消费呢?需要在handleDelivery代码块中执行channel.basicAck(envelope.getDeliveryTag(), true);
         */
        boolean autoAck = false;
        channel.basicConsume(queueName,autoAck,new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {
                channel.basicAck(envelope.getDeliveryTag(), true);
                System.out.println("=======================rabbit-routeA-exchange=====================");
                System.out.println(new String(body));
                System.out.println("=======================rabbit-routeA-exchange=====================");

            }
        });
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        initConsumer();
        initConsumerA();
    }
}

测试代码

    @Autowired
    private RabbitRouteProducerService routeProducerService;
    
    @Test
    public void testRouteSendMessage()throws Exception{
        String exchange = "rabbit-route-exchange";
        String routeKey = "rabbit-route-key";
        String message = "hello this is rabbit route message for routeKey=rabbit-route-key";
        routeProducerService.sendMessage(exchange,routeKey,message);
        Thread.sleep(1000000);
    }

执行测试代码如下

可以看到只有绑定rabbit-route-key的队列被消费了,而绑定rabbit-route-key-A没有被消费

4.主题交换机

上面的路由模式是根据路由key进行完整的匹配(完全相等才发送消息),这里的通配符模式通俗的来讲就是模糊匹配。路由名称间隔以英文句号相连 eg  animal.*.yellow 可以表示任何黄色的动物

符号“#”表示匹配一个或多个词,符号“*”表示匹配一个词。

RabbitTopicProducerService.java(生产者)

package com.wl.mq.rabbit;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConnectionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * Created by Administrator on 2021/3/12.
 */
@Service
public class RabbitTopicProducerService {

    private Channel channel;

    @Autowired
    public RabbitTopicProducerService(ConnectionFactory factory) throws Exception{
        String connectionName = "producer-topic-connection";
        this.channel = factory.newConnection(connectionName).createChannel();
    }

    public void sendMessage(String exchange,String routeKey,String message) throws Exception{
        channel.exchangeDeclare(exchange,"topic");
        channel.basicPublish(exchange,routeKey,null,message.getBytes());
    }

}

RabbitTopicConsumerService.java(消费者)

package com.wl.mq.rabbit;

import com.rabbitmq.client.*;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;

/**
 * Created by Administrator on 2021/3/12.
 */
@Service
public class RabbitTopicConsumerService implements InitializingBean {

    private Channel channel;

    @Autowired
    public RabbitTopicConsumerService(ConnectionFactory factory) throws Exception{
        String connectionName = "consumer-topic-connection";
        this.channel = factory.newConnection(connectionName).createChannel();
    }

    private void initConsumer() throws Exception{

        String exchange = "rabbit-topic-exchange";
        String routeKey = "animal.*.yellow";
        //声明交换器
        channel.exchangeDeclare(exchange,"topic");
        //自定义声明queue
        String queueName = channel.queueDeclare("route-topic-queue",false,false,false,null).getQueue();
        //绑定queue和交换器
        channel.queueBind(queueName,exchange,routeKey);
        /*
            autoAck false表示服务器默认该消息没有被消费,因此每次重新启动这个服务都会导致之前的消息被重新消费一次。
            如何手动通知服务器该消息已经被消费呢?需要在handleDelivery代码块中执行channel.basicAck(envelope.getDeliveryTag(), true);
         */
        boolean autoAck = false;
        channel.basicConsume(queueName,autoAck,new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {
                channel.basicAck(envelope.getDeliveryTag(), true);
                System.out.println("=======================rabbit-topic-exchange routeKye animal.*.yellow =====================");
                System.out.println(new String(body));
                System.out.println("=======================rabbit-topic-exchange routeKye animal.*.yellow=====================");

            }
        });
    }
    
    
    @Override
    public void afterPropertiesSet() throws Exception {
        initConsumer();
    }
}

测试代码

    @Autowired
    private RabbitTopicProducerService topicProducerService;

    @Test
    public void testTopicSendMessage() throws Exception{
        String exchange = "rabbit-topic-exchange";
        String routeKey = "animal.cat.yellow";
        String message = "hello this is rabbit topic message for routeKey=cat.yellow";
        topicProducerService.sendMessage(exchange,routeKey,message);
        Thread.sleep(1000000);
    }

打印日志

最后一个远程调用就参考http://rabbitmq.mr-ping.com/tutorials_with_csharp/rpc.html

有点啰嗦,但是很详细!希望能帮到你!唯一需要注意的可能就是避免集群环境下的消息被重复消费!需要在consumer中自己声明队列名称,不要使用rabbit客户端自定义的queue。避免出现名称未amq.gen类似名称的queue

猜你喜欢

转载自blog.csdn.net/name_is_wl/article/details/114676545
今日推荐