rabbitMQ的安装和搭建spring boot项目百度一下有很多,就不介绍了;
直接开始在spring boot项目中去集成 MQ:
这里版本号可以不填,默认使用的是spring boot的版本;
首先是pom.xml中添加:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
然后是application.properties文件:
#==========================================MQ=======================================
#rabbitMQ的配置
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
接下来是RabbitConfig配置类:
package com.wxy.mq.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.boot.autoconfigure.amqp.SimpleRabbitListenerContainerFactoryConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
/**
Broker:它提供一种传输服务,它的角色就是维护一条从生产者到消费者的路线,保证数据能按照指定的方式进行传输,
Exchange:消息交换机,它指定消息按什么规则,路由到哪个队列。
Queue:消息的载体,每个消息都会被投到一个或多个队列。
Binding:绑定,它的作用就是把exchange和queue按照路由规则绑定起来.
Routing Key:路由关键字,exchange根据这个关键字进行消息投递。
vhost:虚拟主机,一个broker里可以有多个vhost,用作不同用户的权限分离。
Producer:消息生产者,就是投递消息的程序.
Consumer:消息消费者,就是接受消息的程序.
Channel:消息通道,在客户端的每个连接里,可建立多个channel.
*/
/**
* MQ配置
* @author wangxinyang
* @date 2019年4月18日
* @version 1.0.0
*
*/
@Configuration
public class RabbitConfig {
private final Logger logger = LoggerFactory.getLogger(RabbitConfig.class);
@Value("${spring.rabbitmq.host}")
private String host;
@Value("${spring.rabbitmq.port}")
private int port;
@Value("${spring.rabbitmq.username}")
private String username;
@Value("${spring.rabbitmq.password}")
private String password;
public static final String EXCHANGE_A = "test-mq-exchange_A";
public static final String QUEUE_A = "TEST_QUEUE_A";
public static final String ROUTINGKEY_A = "test-routingKey_A";
@Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(host,port);
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost("/");
connectionFactory.setPublisherConfirms(true);
return connectionFactory;
}
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
//必须是prototype类型
public RabbitTemplate rabbitTemplate() {
RabbitTemplate template = new RabbitTemplate(connectionFactory());
return template;
}
/**
* 针对消费者配置
* 1. 设置交换机类型
* 2. 将队列绑定到交换机
FanoutExchange: 将消息分发到所有的绑定队列,无routingkey的概念
HeadersExchange :通过添加属性key-value匹配
DirectExchange:按照routingkey分发到指定队列
TopicExchange:多关键字匹配
*/
@Bean
public DirectExchange defaultExchange() {
return new DirectExchange(EXCHANGE_A);
}
@Bean
public DirectExchange dlxExchange() {
return new DirectExchange("dlx.exchange");
}
/**
* 为队列绑定死信队列
* @return
*/
@Bean
public Queue queueA() {
Map<String, Object> args = new HashMap<>(3);
//声明死信交换器
args.put("x-dead-letter-exchange", "dlx.exchange");
//声明死信路由键
args.put("x-dead-letter-routing-key", ROUTINGKEY_A );
//声明队列消息过期时间 30分钟
args.put("x-message-ttl", 1800000);
// args.put("x-message-ttl", 10000);
return new Queue(QUEUE_A, true, false, false, args);//队列持久
}
@Bean
public Queue dlqQueue() {
return new Queue("dlq.queue", true);//队列持久
}
@Bean
public Binding bindingDlQueue() {
return BindingBuilder.bind(dlqQueue()).to(dlxExchange()).with(ROUTINGKEY_A);
}
/**
* 绑定队列
* @return
*/
@Bean
public Binding binding() {
return BindingBuilder.bind(queueA()).to(defaultExchange()).with(RabbitConfig.ROUTINGKEY_A);
}
/**
* 消费者开启多线程
* @param configurer
* @param connectionFactory
* @return
*/
@Bean("customContainerFactory")
public SimpleRabbitListenerContainerFactory containerFactory(SimpleRabbitListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConcurrentConsumers(10); //设置线程数
factory.setMaxConcurrentConsumers(30); //最大线程数
//这里还可以添加一个ack处理,默认的是auto,自动处理消息确认;
factory.setAcknowledgeMode(AcknowledgeMode.AUTO);
configurer.configure(factory, connectionFactory);
return factory;
}
}
然后是生产者:
package com.rabbitmq.send;
import java.util.Map;
import java.util.UUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.rabbitmq.config.RabbitConfig;
/**
* MQ生产者
* @author wangxinyang
* @date 2019年4月18日
* @version 1.0.0
*
*/
@Component
public class MsgProducer implements RabbitTemplate.ConfirmCallback {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
//由于rabbitTemplate的scope属性设置为ConfigurableBeanFactory.SCOPE_PROTOTYPE,所以不能自动注入
private RabbitTemplate rabbitTemplate;
/**
* 构造方法注入rabbitTemplate
*/
@Autowired
public MsgProducer(RabbitTemplate rabbitTemplate) {
this.rabbitTemplate = rabbitTemplate;
rabbitTemplate.setConfirmCallback(this); //rabbitTemplate如果为单例的话,那回调就是最后设置的内容
}
/**
*
* @param map
*/
public void sendMsg(Map<String, Object> map) {
CorrelationData correlationId = new CorrelationData(UUID.randomUUID().toString());
//把消息放入ROUTINGKEY_A对应的队列当中去,对应的是队列A
rabbitTemplate.convertAndSend(RabbitConfig.EXCHANGE_A, RabbitConfig.ROUTINGKEY_A, map, correlationId);
}
/**
* 回调
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
logger.info(" 回调id:" + correlationData);
if (ack) {
logger.info("消息成功消费");
} else {
logger.info("消息消费失败:" + cause);
}
}
}
接下来是消费者:
这里的监听里面配置的:containerFactory="customContainerFactory",这是我们在RabbitConfig中编写使 消费者开启多线程的方式去消费;
消费者这里我们做了一个动作,去调用服务端接口处理业务逻辑;
package com.wxy.mq.consumer;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSONObject;
import com.eip.mq.config.RabbitConfig;
import com.eip.mq.util.HttpUtils;
/**
* MQ消费者
* @author wangxinyang
* @date 2019年4月18日
* @version 1.0.0
*
*/
@Component
@RabbitListener(queues = RabbitConfig.QUEUE_A,containerFactory="customContainerFactory")
public class MsgConsumer {
private final Logger logger = LoggerFactory.getLogger(MsgConsumer.class);
private final static String URL = "http://localhost:8080/demo/user/getUserInfo";
@RabbitHandler
public void process(Map<String, Object> map) {
logger.info("===================当前所属服务:mq===================start");
logger.info("接收处理队列A当中的消息: " + map + "\n 当前线程name:" + Thread.currentThread().getName() + "\n 当前线程id:" + Thread.currentThread().getId());
/*JSONObject jsonObject = new JSONObject();
jsonObject.putAll(map);
String resultStr = HttpUtils.doPost(URL, jsonObject);
logger.info("返回结果:" + resultStr);*/
//可用
String jsonString = JSON.toJSONString(map);
sendPost(jsonString);
logger.info("===================当前所属服务:mq===================end");
}
private void sendPost(String str){
DataOutputStream out1 = null;
HttpURLConnection conn1 = null;
try {
// 创建连接
URL url1 = new URL(TACT_SRV_ORDER_INFO_SERVICE_URL);
conn1 = (HttpURLConnection) url1.openConnection();
logger.info("创建链接成功:" + conn1.getURL());
conn1.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
conn1.setDoOutput(true);
conn1.setDoInput(true);
conn1.setRequestMethod("POST");
conn1.setUseCaches(false);
conn1.setInstanceFollowRedirects(true);
conn1.connect();
logger.info("获取链接成功。");
out1 = new DataOutputStream(conn1.getOutputStream());
out1.write(str.getBytes("UTF-8"));
logger.info("数据写入成功:" + str);
out1.flush();
// 读取响应
BufferedReader rd1 = new BufferedReader(new InputStreamReader(conn1.getInputStream(), "utf-8"));
String lines1;
StringBuffer sb1 = new StringBuffer("");
while ((lines1 = rd1.readLine()) != null) {
lines1 = new String(lines1.getBytes());
sb1.append(lines1);
}
logger.info("返回结果:" + sb1.toString());
} catch (Exception e) {
// TODO: handle exception
logger.error("发生错误,错误原因:" + e.getMessage());
logger.error("错误位置:" + e.getStackTrace());
}finally {
try {
out1.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
conn1.disconnect();
}
}
}
接下来编写控制层代码,去调用生产者生产消息:
package com.rabbitmq.controller;
import java.util.Date;
import java.util.HashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.rabbitmq.commons.R;
import com.rabbitmq.config.RabbitConfig;
import com.rabbitmq.send.MsgProducer;
/**
* test 往mq发送消息,两个方法,两种方式
* @author wangxinyang
* @date 2019年4月16日
* @version 1.0.0
*
*/
@RestController
@RequestMapping("/send")
public class SendController {
/** 日志对象 */
private static Logger logger = LoggerFactory.getLogger(SendController.class);
@Autowired
private AmqpTemplate amqpTemplate;
@Autowired
private MsgProducer msgProducer;
@RequestMapping("/send")
public R send(){
String content = "Date:" + System.currentTimeMillis();
amqpTemplate.convertAndSend(RabbitConfig.QUEUE_A, content);
return R.ok();
}
@PostMapping("/sendA")
public R sendA(@RequestBody HashMap<String, Object> params){
logger.info("传入的params:" + params);
String content = System.currentTimeMillis() + "";
/*for (int i = 0; i < 1000; i++) {
msgProducer.sendMsg("id:" + id + content + "----" + i);
}*/
msgProducer.sendMsg(params);
return R.ok();
}
}
服务端代码:
package com.wxy.controller;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.wxy.commons.R;
/**
* 服务端,接收mq消费者调用;
* @author wangxinyang
* @date 2019年4月18日
* @version 1.0.0
*
*/
@RestController
@RequestMapping("/demo/user")
public class UserController {
private static Logger logger = LoggerFactory.getLogger(UserController.class);
@PostMapping("/getUserInfo")
public R getUserInfo(@RequestBody Map<String, Object> params){
logger.info("接收MQ请求参数params:" + params);
return R.ok();
}
}
到这里就已经完成整个调用链了;
end;