1、在pom.xml文件中添加以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2、在 application.properties文件中添加配置信息:
spring.rabbitmq.username=XXX
spring.rabbitmq.password=XXX
spring.rabbitmq.virtualHost=XXX
spring.rabbitmq.addresses=ip:port,ip:port
或者从第三方配置信息中读取:
/**
*
*/
import com.sohu.sns.manager.util.JsonMapper;
import org.apache.commons.lang3.StringUtils;
import java.util.Map;
/**
* mq连接配置
*/
public class AmqpInit {
private static final JsonMapper jsonMapper = JsonMapper.nonEmptyMapper();
public static void init() {
try {
String mqConfig = ZooKeeperInit.getData("/mq.properties");
if (StringUtils.isEmpty(mqConfig)) {
throw new Exception("mq.properties is not found in zk.");
} else {
Map<String,Object> configMap = jsonMapper.fromJson(mqConfig,Map.class);
//初始化ConnectionFactory需要参数
System.setProperty("spring.rabbitmq.username", configMap.get("user_name").toString());
System.setProperty("spring.rabbitmq.password", configMap.get("pwd").toString());
System.setProperty("spring.rabbitmq.virtualHost", configMap.get("virtual_host").toString());
System.setProperty("spring.rabbitmq.addresses", configMap.get("hosts").toString());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
才用此种配置时在服务启动时需要初始化信息。
更多配置参考配置类信息:
@ConfigurationProperties(
prefix = "spring.rabbitmq"
)
public class RabbitProperties {
private String host = "localhost";
private int port = 5672;
private String username;
private String password;
private final RabbitProperties.Ssl ssl = new RabbitProperties.Ssl();
private String virtualHost;
private String addresses;
private Integer requestedHeartbeat;
}
3、添加mq配置信息:
import org.springframework.amqp.rabbit.core.RabbitMessagingTemplate;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
@Configuration
public class AmqpConfig {
@Bean
public RabbitMessagingTemplate rabbitMessagingTemplate(RabbitTemplate rabbitTemplate) {
RabbitMessagingTemplate rabbitMessagingTemplate = new RabbitMessagingTemplate();
rabbitMessagingTemplate.setMessageConverter(jackson2Converter());
rabbitMessagingTemplate.setRabbitTemplate(rabbitTemplate);
return rabbitMessagingTemplate;
}
@Bean
public MappingJackson2MessageConverter jackson2Converter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
return converter;
}
}
由于我在项目中需要往多个实例中发送消息,所以采用Fanout Exchange 类型,在生产中发送消息无需进行其他配置,发送消息代码如下:
@Service
public class SendMessageServiceImpl implements SendMessageService{
private static final JsonMapper jsonMapper = JsonMapper.nonEmptyMapper();
private static Logger log = LoggerFactory.getLogger(SendMessageServiceImpl.class);
@Autowired
private RabbitTemplate rabbitMessagingTemplate;
@Override
public void sendMessageToMQ(String modelType, String operateType, Object data) {
Map<String,Object> mqDataMap = Maps.newConcurrentMap();
mqDataMap.put("modelType",modelType);
mqDataMap.put("operateType",operateType);
mqDataMap.put("data",data);
String exchangeName = System.getProperties().get("spring.rabbitmq.exchange_name").toString();
String message = jsonMapper.toJson(mqDataMap);
log.info("send message to mq..........."+message);
rabbitMessagingTemplate.convertAndSend(exchangeName,null,message);
}
}
只需要把消息发送到指定名称的交换机中即可。
4、消费者配置:
添加mq服务器配置信息,同生产者配置。
添加消费配置:
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Created by zhangnan on 2016/12/22.
*/
@Configuration
public class AmqpConfig{
@Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
return new RabbitAdmin(connectionFactory);
}
@Bean
public Queue mqQueue(RabbitAdmin rabbitAdmin) {
String queueName = System.getProperties().get("spring.rabbitmq.queue").toString();
Queue queue = new Queue(queueName,true);
rabbitAdmin.declareQueue(queue);
return queue;
}
@Bean
public FanoutExchange mqExchange(RabbitAdmin rabbitAdmin) {
String exchangeName = System.getProperties().get("spring.rabbitmq.exchange_name").toString();
//将消息分发到所有的绑定队列,无routingkey的概念
FanoutExchange exchange = new FanoutExchange(exchangeName);
rabbitAdmin.declareExchange(exchange);
return exchange;
}
@Bean
public Binding mqBinding(RabbitAdmin rabbitAdmin) {
return BindingBuilder.bind(mqQueue(rabbitAdmin)).to(mqExchange(rabbitAdmin));
}
}
声明交换路由和队列,并且把队列和交换路由进行绑定。
消费消息:
@Service
public class ReceiverMessageServiceImpl {
private static final JsonMapper jsonMapper = JsonMapper.nonEmptyMapper();
private static Logger log = LoggerFactory.getLogger(ReceiverMessageServiceImpl.class);
@RabbitListener(queues = "${spring.rabbitmq.queue}")
@RabbitHandler
public void receiveQueue(String message) {
log.info("receiver message from mq........"+message);
}
}
由于在初始化配置信息时:spring.rabbitmq.queue采用的是一下配置:
System.setProperty("spring.rabbitmq.queue", configMap.get("queue_name").toString()+ InetAddress.getLocalHost());
所以可以实现,每个实例的声明的队列是不一样的,实现消息的广播发送到每个队列中。接收消息也非常简单,只需要添加两个注解,监控指定的队列就可以了,再有消息到达时,就可以消费信息。
5、遇到的问题:
在发送消息时,我刚开始采用的监听的模式,在需要发送消息的地方抛出监听事件,在监听中发送消息mq队列中,在实际测试后发现消息会重复发送,后来废弃这种方式,采用现有的在Service中直接发送消息,重复发送消息的问题解决。
@Component
public class EventListener implements ApplicationListener{
private static Logger log = LoggerFactory.getLogger(EventListener.class);
@Override
public void onApplicationEvent(ApplicationEvent applicationEvent) {
}
}