普通maven工程中
工作队列模式
- pom.xml
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>4.0.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
- 消息发送方
//通过连接工厂创建新的连接和mq建立连接
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.199.132");
connectionFactory.setPort(5672);
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
//设置虚拟机,一个mq服务可以设置多个虚拟机,每个虚拟机相当于一个独立的mq
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try{
//建立新连接
connection = connectionFactory.newConnection();
//创建会话通道,生产者和mq服务所有通信都在channel通道中完成
channel = connection.createChannel();
//监听队列
// 声明队列,交换机默认
//queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
/**
* String queue 队列名称
* boolean durable 是否持久化,即mq重启是否还在
* boolean exclusive 是否独占连接,即连接关闭时队列自动删除,true是删除
* boolean autoDelete 是否自动删除,队列不再使用时删除,与exclusive都设为true就可以实现临时队列
* Map<String, Object> arguments 参数,可以设置一个队列的扩展参数,如存活时间
*/
String queue = "Hello World !";
channel.queueDeclare(queue,true,false,false,null);
//发送消息
//参数:basicPublish(String exchange, String routingKey, AMQP.BasicProperties props, byte[] body)
/**
* String exchange 交换机,不指定将使用默认的
* String routingKey 路由key,交换机根据路由key来转发到指定的队列
* 如果使用默认交换机,routingKey设置为队列名称
* props 消息的属性
* body 消息内容
*/
String message = "hello RabbitMQ !";
channel.basicPublish("",queue,null,message.getBytes());
System.out.println("send");
}catch(Exception e){
e.printStackTrace();
}finally {
channel.close();
connection.close();
}
- 消息接收方
//通过连接工厂创建新的连接和mq建立连接
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.199.132");
connectionFactory.setPort(5672);
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
//设置虚拟机,一个mq服务可以设置多个虚拟机,每个虚拟机相当于一个独立的mq
connectionFactory.setVirtualHost("/");
Connection connection = null;
//建立新连接
connection = connectionFactory.newConnection();
//创建会话通道,生产者和mq服务所有通信都在channel通道中完成
Channel channel = connection.createChannel();
//监听队列
// 声明队列,交换机默认
//queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
/**
* String queue 队列名称
* boolean durable 是否持久化,即mq重启是否还在
* boolean exclusive 是否独占连接,即连接关闭时队列自动删除,true是删除
* boolean autoDelete 是否自动删除,队列不再使用时删除,与exclusive都设为true就可以实现临时队列
* Map<String, Object> arguments 参数,可以设置一个队列的扩展参数,如存活时间
*/
String queue = "Hello World !";
channel.queueDeclare(queue,true,false,false,null);
//开始不同
// 实现消费方法
DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
/**
*
* @param consumerTag 消费者标签,用来标识消费者,可在监听队列时设置channel.basicConsume
* @param envelope 信封,通过envelope获取
* @param properties 消息属性
* @param body 消息内容
* @throws IOException
*/
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
//交换机
String exchange = envelope.getExchange();
//消息id,mq在channel中用来表示消息的id,可用于确认消息已接收
long deliveryTag = envelope.getDeliveryTag();
//消息内容
String message = new String(body, "utf-8");
System.out.println(message);
}
};
//basicConsume(String queue, boolean autoAck, Consumer callback)
/**
* String queue 队列名称
* boolean autoAck 自动回复,当消费者接收到消息后要告诉mq消息已接收,设置true表示自动回复mq,false要编程实现回复
* Consumer callback 消费方法,当消费者接受到消息要执行的方法
*/
//监听队列
channel.basicConsume(queue,true,defaultConsumer);
Publish模式
- 服务提供者
//通过连接工厂创建新的连接和mq建立连接
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.199.132");
connectionFactory.setPort(5672);
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
//设置虚拟机,一个mq服务可以设置多个虚拟机,每个虚拟机相当于一个独立的mq
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try{
//建立新连接
connection = connectionFactory.newConnection();
//创建会话通道,生产者和mq服务所有通信都在channel通道中完成
channel = connection.createChannel();
// 声明两个队列,交换机默认
channel.queueDeclare(QUEUE_INFORM_EMAIL,true,false,false,null);
channel.queueDeclare(QUEUE_INFORM_SMS,true,false,false,null);
//声明交换机
//exchangeDeclare(String exchange, String type)
/**
* String exchange 名称
* String type 类型 BuiltinExchangeType.TYPE
* 1. fanout : 对应 publish/subscribe
* 2. direct : Routing
* 3. topic : Topics
* 4. headers : headers
*/
channel.exchangeDeclare(EXCHANGE_FANOUT_INFORM, BuiltinExchangeType.FANOUT);
// 交换机和队列进行绑定
//queueBind(String queue, String exchange, String routingKey)
/**
* String queue : 队列名称
* String exchange : 交换机名称
* String routingKey :路由key,交换机根据路由key将消息转发到指定的队列中,在发布订阅中协调为空字符串
*/
channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_FANOUT_INFORM,"");
channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_FANOUT_INFORM,"");
//发送消息
for (int i = 0; i < 5; i++) {
String message = "send inform message";
/**
* String exchange 交换机,不指定将使用默认的,""
* String routingKey 路由key,交换机根据路由key来将校级转发到指定的队列
* 如果使用默认交换机,routingKey设置为队列名称
* props 消息的属性
* body 消息内容
*/
channel.basicPublish(EXCHANGE_FANOUT_INFORM,"",null,message.getBytes());
System.out.println("send");
}
}catch(Exception e){
e.printStackTrace();
}finally {
channel.close();
connection.close();
}
- 服务消费者
//队列名称
private static String QUEUE_INFORM_EMAIL = "queue_inform_email";
//private static String QUEUE_INFORM_SMS = "queue_inform_sms"; 可作为另一个类
private static String EXCHANGE_FANOUT_INFORM = "exchange_fanout_inform";
public static void main(String[] args) throws Exception{
//通过连接工厂创建新的连接和mq建立连接
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.199.132");
connectionFactory.setPort(5672);
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
//设置虚拟机,一个mq服务可以设置多个虚拟机,每个虚拟机相当于一个独立的mq
connectionFactory.setVirtualHost("/");
Connection connection = null;
//建立新连接
connection = connectionFactory.newConnection();
//创建会话通道,生产者和mq服务所有通信都在channel通道中完成
Channel channel = connection.createChannel();
//监听队列
// 声明队列,交换机默认
//queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
/**
* String queue 队列名称
* boolean durable 是否持久化,即mq重启是否还在
* boolean exclusive 是否独占连接,即连接关闭时队列自动删除,true是删除
* boolean autoDelete 是否自动删除,队列不再使用时删除,与exclusive都设为true就可以实现临时队列
* Map<String, Object> arguments 参数,可以设置一个队列的扩展参数,如存活时间
*/
channel.queueDeclare(QUEUE_INFORM_EMAIL,true,false,false,null);
/**
* String queue : 队列名称
* String exchange : 通道名称
* String routingKey :路由key,交换机根据路由key将消息转发到指定的队列中,在发布订阅中协调为空字符串
*/
channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_FANOUT_INFORM,"");
//开始不同
// 实现消费方法
DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
/**
*
* @param consumerTag 消费者标签,用来标识消费者,可在监听队列时设置channel.basicConsume
* @param envelope 信封,通过envelope获取
* @param properties 消息属性
* @param body 消息内容
* @throws IOException
*/
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
//交换机
String exchange = envelope.getExchange();
//消息id,mq在channel中用来表示消息的id,可用于确认消息已接收
long deliveryTag = envelope.getDeliveryTag();
//消息内容
String message = new String(body, "utf-8");
System.out.println(message);
}
};
//basicConsume(String queue, boolean autoAck, Consumer callback)
/**
* String queue 队列名称
* boolean autoAck 自动回复,当消费者接收到消息后要告诉mq消息已接收,设置true表示自动回复mq,false要编程实现回复
* Consumer callback 消费方法,当消费者接受到消息要执行的方法
*/
//监听队列
channel.basicConsume(QUEUE_INFORM_EMAIL,true,defaultConsumer);
}
消息应答与消息持久化
- 接收方消息应答
消息应答确认
NONE:自动确认
AUTO : 看情况确认
manual :手动确认
- 生产方-事务机制
channel.txSelect(); //开始
channel.txCommit(); // 提交
channel.txRollback(); // 回滚
- 生产方-Confirm模式
Confirm模式最大的好处在于他是异步
开启:channel.confirmSelect()
Publisher Confirms
编程模式:
- 普通,发一条:waitForConfirms()
- 批量,发一批:waitForConfirms()
- 异步:提供一个回调方法
SpringBoot中
配置
- pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
- application.yml
server:
port: 44000
spring:
application:
name: test-rabbitmq
rabbitmq:
host: 192.168.199.132
port: 5672
username: guest
password: guest
virtual-host: /
- 自定义RabbitmqConfig
@Configuration
public class RabbitmqConfig {
// 声明交换机
@Bean("direct")
public DirectExchange directExchange(){
//交换机名字,是否持久化,是否自动删除
return new DirectExchange("direct",false,false);
}
//声明队列
@Bean("sms")
public Queue queSms(){
//队列名,是否持久化
return new Queue("sms",false);
}
@Bean("email")
public Queue queEmail(){
return new Queue("email",false);
}
//绑定交换机和队列
@Bean
Binding bindingSms(Queue sms,DirectExchange directExchange){
// 队列, 交换机 , routingKey
return BindingBuilder.bind(sms).to(directExchange).with("");
}
@Bean
Binding bindingEmail(Queue email,DirectExchange directExchange){
return BindingBuilder.bind(email).to(directExchange).with("");
}
}
发送方
@Autowired
RabbitTemplate rabbitTemplate;
//使用rabbitTemplate发送消息
@Test
void contextLoads() {
//交换机,routingKey,信息
rabbitTemplate.convertAndSend("direct",""," send boot");
}
接收方
启动时就会自动接收
import org.springframework.amqp.core.Message;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class ReceiveHandler {
@RabbitListener(queues = {"email"})
public void send_email(String msg, Message message, Channel channel){
System.out.println("Get message is email-" + msg);
}
@RabbitListener(queues = {"sms"})
public void send_sms(String msg, Message message, Channel channel){
System.out.println("Get message is sms-" + msg);
}
}
大佬配置RabbitMQ默认配置
import com.google.common.collect.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.support.CorrelationData;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.amqp.SimpleRabbitListenerContainerFactoryConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import java.util.HashMap;
/**
* @Author: XF-DD
* @Date: 20/05/19 23:04
* 通用化 Rabbitmq 的配置
*/
@Configuration
public class RabbitmqConfig {
private final static Logger logger = LoggerFactory.getLogger(RabbitmqConfig.class);
@Autowired
private Environment environment;
//建立通道连接,缓存连接工厂
@Autowired
private CachingConnectionFactory connectionFactory;
//消费者所在容器工厂
@Autowired
private SimpleRabbitListenerContainerFactoryConfigurer factoryConfigurer;
/**
* 单一消费者
*
* @return
*/
@Bean(name = "singleListenerContainer")
public SimpleRabbitListenerContainerFactory listenerContainer() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setMessageConverter(new Jackson2JsonMessageConverter()); //消息传输格式
factory.setConcurrentConsumers(1);
factory.setMaxConcurrentConsumers(1);
factory.setPrefetchCount(1);
factory.setTxSize(1);
return factory;
}
/**
* 多个消费者
*
* @return
*/
@Bean(name = "multiListenerContainer")
public SimpleRabbitListenerContainerFactory multiListenerContainer() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factoryConfigurer.configure(factory, connectionFactory);
factory.setMessageConverter(new Jackson2JsonMessageConverter());
//确认消费模式-NONE
factory.setAcknowledgeMode(AcknowledgeMode.NONE);
//多少个消费者
factory.setConcurrentConsumers(environment.getProperty("spring.rabbitmq.listener.simple.concurrency", int.class));
//最多多少个消费者
factory.setMaxConcurrentConsumers(environment.getProperty("spring.rabbitmq.listener.simple.max-concurrency", int.class));
//每个消费者预拉取数量
factory.setPrefetchCount(environment.getProperty("spring.rabbitmq.listener.simple.prefetch", int.class));
return factory;
}
@Bean
public RabbitTemplate rabbitTemplate() {
//必须要设置,发送确认
connectionFactory.setPublisherConfirms(true);
connectionFactory.setPublisherReturns(true);
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMandatory(true);
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
/**
* @param correlationData 唯一标识,有了这个唯一标识,我们就知道可以确认(失败)哪一条消息了
* @param ack
* @param cause
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
logger.info("消息发送成功:correlationData({}),ack({}),cause({})", correlationData, ack, cause);
}
});
rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
logger.warn("消息丢失:exchange({}),route({}),replyCode({}),replyText({}),message:{}", exchange, routingKey, replyCode, replyText, message);
}
});
return rabbitTemplate;
}
}