本文采用directExchange,为保证针对同个用户的消息要有消费顺序,采用多队列,每条队列单线程消费进行配置
配置参数接收类
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* 配置类
*/
@Configuration
@ConfigurationProperties(prefix = "spring.rabbitmq")
public class RabbitMqProperties {
private String addresses;//地址
private String username;//用户名
private String password;//密码
private Boolean publisherConfirms;//是否发送校验
private String virtualHost;//虚拟地址
private Boolean defaultDurable;// 是否持久化
private Boolean autoDelete;//当所有消费客户端连接断开后,是否自动删除队列
public String getAddresses() {
return addresses;
}
public void setAddresses(String addresses) {
this.addresses = addresses;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Boolean getPublisherConfirms() {
return publisherConfirms;
}
public void setPublisherConfirms(Boolean publisherConfirms) {
this.publisherConfirms = publisherConfirms;
}
public String getVirtualHost() {
return virtualHost;
}
public void setVirtualHost(String virtualHost) {
this.virtualHost = virtualHost;
}
public Boolean getDefaultDurable() {
return defaultDurable;
}
public void setDefaultDurable(Boolean defaultDurable) {
this.defaultDurable = defaultDurable;
}
public Boolean getAutoDelete() {
return autoDelete;
}
public void setAutoDelete(Boolean autoDelete) {
this.autoDelete = autoDelete;
}
}
配置参数
#rabbitmq配置信息
spring.rabbitmq.addresses=127.0.0.1:5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.publisherConfirms=true
spring.rabbitmq.virtualHost=/rabbit
spring.rabbitmq.defaultDurable=true
spring.rabbitmq.autoDelete=false
rabbitmq.routingKey.count=3
rabbitmq.exchange=local.exchange
rabbitmq.routingKey=local.queue_
配置类
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
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.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
@Configuration
public class AmqpConfig {
private static Logger log = LoggerFactory.getLogger(AmqpConfig.class);
@Autowired
private RabbitMqProperties rabbitMqProperties;
@Bean
public ConnectionFactory connectionFactory(){
CachingConnectionFactory connectionFactory=new CachingConnectionFactory();
connectionFactory.setAddresses(rabbitMqProperties.getAddresses());
connectionFactory.setUsername(rabbitMqProperties.getUsername());
connectionFactory.setPassword(rabbitMqProperties.getPassword());
connectionFactory.setVirtualHost(rabbitMqProperties.getVirtualHost());
connectionFactory.setPublisherConfirms(rabbitMqProperties.getPublisherConfirms());
return connectionFactory;
}
// 这里作用域设置为原型模式 每次获取都会得到一个新的实例
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public RabbitTemplate rabbitTemplate(){
RabbitTemplate rabbitTemplate=new RabbitTemplate(connectionFactory());
//数据转换为json存入消息队列
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
//发布确认
rabbitTemplate.setConfirmCallback((CorrelationData correlationData, boolean ack, String cause) -> {
if (!ack){
log.error("发送到消息失败");
throw new RuntimeException("send error " + cause);
}
});
return rabbitTemplate;
}
/**
* rabbitAdmin代理类
* @return
*/
@Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory){
return new RabbitAdmin(connectionFactory);
}
}
发送消息util类
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.UnsupportedEncodingException;
/**
* 消息发送util
*/
@Component
public class SendMsgUtils {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private AmqpTemplate rabbitTemplate;
public void sendMsg(String exchange, String routingKey,String content){
MessageProperties messageProperties = new MessageProperties();
// 设置消息持久化
messageProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
try {
Message message = new Message(content.getBytes("utf-8"),messageProperties);
rabbitTemplate.send(exchange, routingKey, message);
} catch (UnsupportedEncodingException e) {
logger.info("发送消息失败 exchange{},routingKey:{},content",exchange,routingKey,content);
e.printStackTrace();
}
}
}
测试controller类
@RestController
@RequestMapping(path = "/test")
public class TestController {
@Value("${rabbitmq.exchange}")
private String AUDIENCE_EXCHANGE;// 交换器名
@Value("${rabbitmq.routingKey}")
private String ROUTING_KEY;// 路由键
@Value("${rabbitmq.routingKey.count}")
private Integer ROUTING_KEY_COUNT;// 路由键个数
@Autowired
private SendMsgComponent sendMsgComponent;
@PostMapping(path = "/sendMessage")
public void testSend(String userId,String message){
// 计算路由键
int routeKey = HashUtils.getHash(userId) % ROUTING_KEY_COUNT;
// 发送消息
sendMsgComponent.sendMsg(AUDIENCE_EXCHANGE,ROUTING_KEY+routeKey,message);
}
}
org.springframework.boot
spring-boot-starter-amqp
配置参数
#rabbitmq配置信息
spring.rabbitmq.addresses=127.0.0.1:5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.publisherConfirms=true
spring.rabbitmq.virtualHost=/rabbitpre
spring.rabbitmq.defaultDurable=true
spring.rabbitmq.autoDelete=false
# exchange
rabbitmq.exchange=local.exchange
# 3个队列消费
rabbitmq.queue_0=local.queue_0
rabbitmq.queue_1=local.queue_1
rabbitmq.queue_2=local.queue_2
#消费队列数
rabbitmq.queue.audience.count=3
xml配置
消息监听器
import com.alibaba.fastjson.JSON;
import com.rabbitmq.client.Channel;
import com.zbmy.rabbit.workbench.data.server.utils.HashUtils;
import org.elasticsearch.client.transport.TransportClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
@Component
public class MqListener implements ChannelAwareMessageListener {
private static final Logger LOGGER = LoggerFactory.getLogger(UpdateAudienceListener.class);
@Override
public void onMessage(Message message, Channel channel) throws Exception {
try {
byte[] body = message.getBody();
String messageStr = new String(body, "UTF-8");
doSomething(parseMessage(messageStr));
// 确认 false不批量确认
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
} catch (Exception e) {
LOGGER.error("处理消息失败:{}", e);
// 拒绝消息 false不重新放入队列
channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);
}
}
private Map parseMessage(String messageStr){
Map resultMap = new HashMap<>();
Map map = JSON.parseObject(messageStr);
for (Map.Entry entry : map.entrySet()) {
resultMap.put(entry.getKey(),entry.getValue());
}
return resultMap;
}
}
基础知识推荐这篇文章 https://www.jianshu.com/p/79ca08116d57
mq官方各版本文档地址 https://docs.spring.io/spring-amqp/docs/