spring boot集成rabbitMQ及绑定死信队列

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;

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值