依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
yml配置文件
spring:
rabbitmq:
host: 127.0.0.1 #ip
port: 5672 #端口
username: guest #账号
password: guest #密码
virtualHost: #链接的虚拟主机
addresses: 127.0.0.1:5672 #多个以逗号分隔,与host功能一样。
requestedHeartbeat: 60 #指定心跳超时,单位秒,0为不指定;默认60s
publisher-confirm-type: correlated #发布确认机制是否启用
publisherReturns: #发布返回是否启用
connectionTimeout: #链接超时。单位ms。0表示无穷大不超时
### ssl相关
ssl:
enabled: #是否支持ssl
keyStore: #指定持有SSL certificate的key store的路径
keyStoreType: #key store类型 默认PKCS12
keyStorePassword: #指定访问key store的密码
trustStore: #指定持有SSL certificates的Trust store
trustStoreType: #默认JKS
trustStorePassword: #访问密码
algorithm: #ssl使用的算法,例如,TLSv1.1
verifyHostname: #是否开启hostname验证
### cache相关
cache:
channel:
size: #缓存中保持的channel数量
checkoutTimeout: #当缓存数量被设置时,从缓存中获取一个channel的超时时间,单位毫秒;如果为0,则总是创建一个新channel
connection:
mode: #连接工厂缓存模式:CHANNEL 和 CONNECTION
size: #缓存的连接数,只有是CONNECTION模式时生效
### listener
listener:
type: #两种类型,SIMPLE,DIRECT
## simple类型
simple:
concurrency: #最小消费者数量
maxConcurrency: #最大的消费者数量
transactionSize: #指定一个事务处理的消息数量,最好是小于等于prefetch的数量
missingQueuesFatal: #是否停止容器当容器中的队列不可用
## 与direct相同配置部分
autoStartup: #是否自动启动容器
acknowledgeMode: #表示消息确认方式,其有三种配置方式,分别是none、manual和auto;默认auto
prefetch: #指定一个请求能处理多少个消息,如果有事务的话,必须大于等于transaction数量
defaultRequeueRejected: #决定被拒绝的消息是否重新入队;默认是true(与参数acknowledge-mode有关系)
idleEventInterval: #container events发布频率,单位ms
##重试机制
retry:
stateless: #有无状态
enabled: #是否开启
maxAttempts: #最大重试次数,默认3
initialInterval: #重试间隔
multiplier: #对于上一次重试的乘数
maxInterval: #最大重试时间间隔
direct:
consumersPerQueue: #每个队列消费者数量
missingQueuesFatal:
#...其余配置看上方公共配置
## template相关
template:
mandatory: #是否启用强制信息;默认false
receiveTimeout: #`receive()`接收方法超时时间
replyTimeout: #`sendAndReceive()`超时时间
exchange: #默认的交换机
routingKey: #默认的路由
defaultReceiveQueue: #默认的接收队列
## retry重试相关
retry:
enabled: #是否开启
maxAttempts: #最大重试次数
initialInterval: #重试间隔
multiplier: #失败间隔乘数
maxInterval: #最大间隔
Java 配置文件
@Configuration
public class RabbitmqMQConfig {
/**
* SimpleMessageListenerContainer
*
* @param connectionFactory
* @return
*/
@Bean(name = "simpleContainerFactory")
public SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory simpleContainerFactory = new SimpleRabbitListenerContainerFactory();
//设置连接工厂
simpleContainerFactory.setConnectionFactory(connectionFactory);
//接收消息采用Jackson2JsonMessageConverter序列化
simpleContainerFactory.setMessageConverter(this.jackson2JsonMessageConverter());
//设置初始消费者数量(SimpleRabbitListenerContainerFactory配置类的配置优先级比配置文件高)
simpleContainerFactory.setConcurrentConsumers(2);
//设置最大消费者数量(SimpleRabbitListenerContainerFactory配置类的配置优先级比配置文件高)
simpleContainerFactory.setMaxConcurrentConsumers(10);
//设置消费者每次获取的消息数,默认250(SimpleRabbitListenerContainerFactory配置类的配置优先级比配置文件高)
simpleContainerFactory.setPrefetchCount(30);
//消费者listener抛出异常,是否重回队列,默认true:重回队列, false为不重回队列(结合死信交换机)
simpleContainerFactory.setDefaultRequeueRejected(false);
//应答模式NONE:不确认模式,MANUAL:手动确认模式,AUTO:自动确认模式
simpleContainerFactory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
return simpleContainerFactory;
}
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate rabbitTemplate = new RabbitTemplate();
//设置连接工厂
rabbitTemplate.setConnectionFactory(connectionFactory);
//接收消息采用Jackson2JsonMessageConverter序列化(支持java 8时间)
rabbitTemplate.setMessageConverter(this.jackson2JsonMessageConverter());
//Mandatory为true时,消息通过交换器无法匹配到队列会返回给生产者,为false时匹配不到会直接被丢弃
rabbitTemplate.setMandatory(true);
return rabbitTemplate;
}
@Bean("jacksonMessageConverter")
public MessageConverter jackson2JsonMessageConverter() {
ObjectMapper mapper = getMapper();
return new Jackson2JsonMessageConverter(mapper);
}
/**
* 使用com.fasterxml.jackson.databind.ObjectMapper
* 对数据进行处理包括java8里的时间
* @return
*/
private ObjectMapper getMapper() {
ObjectMapper mapper = new ObjectMapper();
//设置可见性
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//默认键入对象
mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
//设置Java 8 时间序列化
JavaTimeModule timeModule = new JavaTimeModule();
timeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
timeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
timeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
timeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
timeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
timeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
//禁用把时间转为时间戳
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
//遇到未知属性或者属性不匹配的时候不抛出异常
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.registerModule(timeModule);
return mapper;
}
}
基本组件定义
队列
定义queue
@Bean
public Queue queue(){
return new Queue(String name, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments);
}
队列参数
-
x-message-ttl 发送到队列的消息在丢弃之前可以存活多长时间(毫秒)。
-
x-expires 队列在被自动删除(毫秒)之前可以使用多长时间。
-
x-max-length 队列在开始从头部删除之前可以包含多少就绪消息。
-
x-max-length-bytes 队列在开始从头部删除之前可以包含的就绪消息的总体大小。
-
x-dead-letter-exchange 设置队列溢出行为。这决定了在达到队列的最大长度时消息会发生什么。有效值为drop-head或reject-publish。交换的可选名称,如果消息被拒绝或过期,将重新发布这些名称。
-
x-dead-letter-routing-key 可选的替换路由密钥,用于在消息以字母为单位时使用。如果未设置,将使用消息的原始路由密钥。
-
x-max-priority 队列支持的最大优先级数;如果未设置,队列将不支持消息优先级。
-
x-queue-mode 将队列设置为延迟模式,在磁盘上保留尽可能多的消息以减少内存使用;如果未设置,队列将保留内存缓存以尽快传递消息。
-
x-queue-master-locator 将队列设置为主位置模式,确定在节点集群上声明时队列主机所在的规则。
交换器
分类
RabbitMQ的Exchange(交换器)分为四类:
- direct 如果路由键匹配的话,消息就投递到相应的队列
- headers
- fanout 把消息广播到所有附加到这个交换器的队列上
- topic 使用路由键进行消息(规则)匹配
topic路由器的关键在于定义路由键,定义routingKey名称不能超过255字节,使用“.”作为分隔符,例如:com.mq.rabbit.error。
消费消息的时候routingKey可以使用下面字符匹配消息:
"*"匹配一个分段(用“.”分割)的内容;
"#"匹配0和多个字符;
创建交换器
@Bean
public DirectExchange fanoutExchange(){
return new DirectExchange(String name, boolean durable, boolean autoDelete, Map<String, Object> arguments);
}
交换器参数
绑定
@Bean
public Binding binding(){
return BindingBuilder.bind(queue()).to(fanoutExchange()).with(ROUTING_KEY);
}
发送端
消息发送
@Resource
RabbitTemplate rabbitTemplate; //使用RabbitTemplate,这提供了接收/发送等等方法
1.直接发送
public void convertAndSend(String exchange, String routingKey, Object message, MessagePostProcessor messagePostProcessor, @Nullable CorrelationData correlationData)
复制代码
2.提前设置参数
public void setExchange(@Nullable String exchange)
public void setRoutingKey(String routingKey)
public void send(Message message) throws AmqpException {
this.send(this.exchange, this.routingKey, message);
}
消息持久化
public void sendMessage(String exchange,String routingKey, Object messageBody){
/*消息持久化*/
MessagePostProcessor processor = message -> {
message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
return message;
};
rabbitTemplate.convertAndSend(exchange, routingKey, messageBody,processor);
}
发送确认机制
修改配置文件
publisher-confirm-type: correlated #发布确认机制是否启用
修改rabbitTemplate配置
rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
if(ack){
System.out.println("发送成功");
}else {
System.out.println("发送失败");
}
});
发送失败处理机制
参考博客
修改配置文件
publisherReturns: true #发布返回是否启用
mandatory: true # 交换器无法依据自身类型和路由键路由键找到队列时返回给生产者
修改rabbitTemplate配置
rabbitTemplate.setReturnsCallback(returned -> {
log.error("发往交换器:"+returned.getExchange()+",路由键为"+returned.getRoutingKey()+"的消息:"+
returned.getMessage().toString()+" 失败,回复信息为"+returned.getReplyText());
});
消费端
Qos
RabbitMQ 提供了一种 qos (服务质量保证)功能,即在非自动确认消息的前提下,如果一定数目的消息(通过基于 consume 或者 channel 设置 Qos 的值)未被确认前,不进行消费新的消息
prefetch: 10 每次取10条信息,默认250
接收消息
@RabbitListener 与 @RabbitHandler
//基础注解,指定queue的名称,可以多个。如果是simple不需要交换机的直接写队列名称就好。
//如果是其他的也想只指定一个queues——name的话,需要上面的配置类配置queue或者其他绑定关系
@RabbitListener(queues = "ly_simple")
@RabbitHandler
public void processSimpleMsg(String message) {
System.out.println("########################received simple" + message);
}
//如果不想使用配置类,可以直接注解通过bindings,绑定,spring会根据注解生成绑定
//ps:如果已有同名称的类。不会覆盖。会影响功能
@RabbitListener(bindings = { @QueueBinding(value = @Queue(value = "ly_direct", durable = "true"),
exchange = @Exchange(value = "ly_direct", type = "direct"), key = "ly") })
@RabbitHandler
public void processDirectMsg(String message) {
System.out.println("########################received" + message);
}
@RabbitListener(bindings = { @QueueBinding(value = @Queue(value = "ly_fanout", durable = "true"),
exchange = @Exchange(value = "ly_fanout", type = "fanout")) })
@RabbitHandler
public void processFanoutMsg(String message) {
System.out.println("########################received" + message);
}
手动确认
SpringBoot+RabbitMQ发送确认和消费手动确认机制
修改配置文件
spring.rabbitmq.listener.direct.acknowledge-mode=manual
手动确认
@RabbitListener(queuesToDeclare =@Queue("boot_queue_ManuallySigned")//使用默认的交换机 @Queue:队列消息配置)
public voidreceiveMsgToManuallySigned(String msg, @Header(AmqpHeaders.DELIVERY_TAG) longdeliveryTag, Channel channel) throws IOException {
channel.basicAck(deliveryTag,false);
channel.basicNack(deliveryTag,false, false);
}
}