在application.yml配置
spring:
rabbitmq:
listener:
simple:
prefetch: 1 #单次获取的消息数量
acknowledge-mode: manual #手动确认
@RabbitListener(queues = RabbitConfig.QUEUE_MSM)
@RabbitHandler
public void msmProcess(Message message, Channel channel) throws IOException {
String msg = new String(message.getBody());
//TODO deal msg
//手动确认消息已处理
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
//手动拒绝消息,且rabbitmq服务器不再重新分发该条消息
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
}
spring boot amqp的注解声明那么方便,为什么不使用呢?
因为…项目要求提供注册Queue、Exchange、Binding的api…
@Component
public class RabbitHelper {
@Autowired
private AmqpTemplate amqpTemplate;
@Autowired
private AmqpAdmin amqpAdmin;
/**
* 发送消息到队列
*
* @param exchange
* @param routingKey
* @param message
*/
public void send(String exchange, String routingKey, String message) {
amqpTemplate.convertAndSend(exchange, routingKey, message);
}
/**
* 声明队列
*
* @param name
* @return
* @throws Exception
*/
public boolean declareQueue(String name) {
throwEx4Blank(name);
amqpAdmin.declareQueue(new Queue(name));
return true;
}
/**
* 声明路由
*
* @param exchange
* @return
*/
public boolean declareExchange(AbstractExchange exchange) {
amqpAdmin.declareExchange(exchange);
return true;
}
/**
* 声明路由
*
* @param name
* @param type
* @return
*/
public boolean declareExchange(String name, String type) {
throwEx4Blank(name, type);
type = type.toLowerCase().trim();
amqpAdmin.declareExchange(getExchangeByType(name, type));
return true;
}
/**
* 将队列绑定到Topic类型的路由
*
* @param routingKey
* @param queue
* @param exchange
* @return
* @throws Exception
*/
public boolean bindingTopic(String routingKey, String queue, String exchange) {
throwEx4Blank(routingKey, queue, exchange);
Binding binding = BindingBuilder.bind(new Queue(queue)).to(new TopicExchange(exchange)).with(routingKey);
amqpAdmin.declareBinding(binding);
return true;
}
/**
* 将队列绑定到Direct类型的路由
*
* @param routingKey
* @param queue
* @param exchange
* @return
* @throws Exception
*/
public boolean bindingDirect(String routingKey, String queue, String exchange) {
throwEx4Blank(routingKey, queue, exchange);
Binding binding = BindingBuilder.bind(new Queue(queue)).to(new DirectExchange(exchange)).with(routingKey);
amqpAdmin.declareBinding(binding);
return true;
}
/**
* 将队列绑定到Fanout类型的路由
*
* @param queue
* @param exchange
* @return
*/
public boolean bindingFanout(String queue, String exchange) {
throwEx4Blank(queue, exchange);
Binding binding = BindingBuilder.bind(new Queue(queue)).to(new FanoutExchange(exchange));
amqpAdmin.declareBinding(binding);
return true;
}
/**
* 将队列绑定到路由
*
* 允许的路由类型仅为:TOPIC、DIRECT、FANOUT
*
*
* 当路由类型为FANOUT时,routingKey可为空
*
*
* @param routingKey
* @param queue
* @param exchange
* @param exchangeType
* @return
* @throws Exception
*/
public boolean binding(String routingKey, String queue, String exchange, String exchangeType) {
exchangeType = exchangeType.toLowerCase().trim();
if (ExchangeTypes.TOPIC.equals(exchangeType)) {
return bindingTopic(routingKey, queue, exchange);
} else if (ExchangeTypes.DIRECT.equals(exchangeType)) {
return bindingDirect(routingKey, queue, exchange);
} else if (ExchangeTypes.FANOUT.equals(exchangeType)) {
return bindingFanout(queue, exchange);
} else {
String msg = "参数错误。exchangeType只能为%s、%s、%s中的一种。";
String error = String.format(msg, ExchangeTypes.TOPIC, ExchangeTypes.DIRECT, ExchangeTypes.FANOUT);
throw new IllegalArgumentException(error);
}
}
/**
* 将队列绑定到路由
*
* 允许的路由类型仅为:TOPIC、DIRECT、FANOUT
*
*
* 当路由类型为FANOUT时,routingKey可为空
*
*
* @param routingKey
* @param queue
* @param exchange
* @return
* @throws Exception
*/
public boolean binding(String routingKey, String queue, AbstractExchange exchange) {
String exchangeType = exchange.getType();
String exchangeName = exchange.getName();
return binding(routingKey, queue, exchangeName, exchangeType);
}
/**
* 将队列绑定到路由
*
* 允许的路由类型仅为:TOPIC、DIRECT、FANOUT
*
*
* 当路由类型为FANOUT时,routingKey可为空
*
*
* @param routingKey
* @param queue
* @param exchange
* @return
* @throws Exception
*/
public boolean binding(String routingKey, Queue queue, AbstractExchange exchange) {
String exchangeType = exchange.getType();
String exchangeName = exchange.getName();
String queueName = queue.getName();
return binding(routingKey, queueName, exchangeName, exchangeType);
}
private AbstractExchange getExchangeByType(String name, String type) {
AbstractExchange exchange = null;
if (ExchangeTypes.TOPIC.equals(type)) {
exchange = new TopicExchange(name);
} else if (ExchangeTypes.DIRECT.equals(type)) {
exchange = new DirectExchange(name);
} else if (ExchangeTypes.FANOUT.equals(type)) {
exchange = new FanoutExchange(name);
} else if (ExchangeTypes.HEADERS.equals(type)) {
exchange = new HeadersExchange(name);
}
return exchange;
}
private void throwEx4Blank(String... strings) {
for (String s : strings) {
if (StringUtils.isBlank(s)) {
throw new IllegalArgumentException("参数错误。所需参数不能为空。");
}
}
}
}
问:为什么不声明消费者?
答:1. 消费者 2. 生产者 3. @Bean声明的Queue、Exchange、Binding;三者在各自不同的服务上。
如果你没有声明任何消费者,且没有主动创建rabbitmq连接,那么…你利用注解声明的Queue、Exchange、Binding,其实是不会主动注册到rabbitmq上的。(写博客时所用spring boot amqp版本是2.0.0.RELEASE)
那么解决方案呢?显而易见:主动创建rabbitmq连接
@Autowired
private RabbitTemplate amqpTemplate;
private void explicitOpenMqConnect() {
//主动创建rabbitmq连接
amqpTemplate.getConnectionFactory().createConnection();
}