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