springboot整合rabbitmq和ThreadPool實現異步調用

添加rabbitMq依賴

<dependency>
	<groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

配置rabbitMq 服務器

服務端和消費端都要配置

#配置rabbitMq 服務器
spring.rabbitmq.host= 10.10.240.xxx
spring.rabbitmq.port= 5672
spring.rabbitmq.username= chijiaqi
spring.rabbitmq.password= 123456

服務端配置類

package cn.edu.nfu.jw.conf;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.*;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;


/*
 * @Author : chijiaqi
 * @CreateTime : 2020/4/14
 * @Description :
*/
/**
 Broker:它提供一種傳輸服務,它的角色就是維護一條從生產者到消費者的路線,保證數據能按照指定的方式進行傳輸,
 Exchange:消息交換機,它指定消息按什麼規則,路由到哪個隊列。
 Queue:消息的載體,每個消息都會被投到一個或多個隊列。
 Binding:綁定,它的作用就是把exchange和queue按照路由規則綁定起來.
 Routing Key:路由關鍵字,exchange根據這個關鍵字進行消息投遞。
 vhost:虛擬主機,一個broker裏可以有多個vhost,用作不同用戶的權限分離。
 Producer:消息生產者,就是投遞消息的程序.
 Consumer:消息消費者,就是接受消息的程序.
 Channel:消息通道,在客戶端的每個連接裏,可建立多個channel.
 */
@Configuration
public class RabbitConfig {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @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 JW_EXCHANGE_DIRECT = "my-mq-exchange_A";


    public static final String JW_QUEUE_DIRECT = "JwDirectQueue";

    public static final String JW_ROUTINGKEY_DIRECT = "spring-boot-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(JW_EXCHANGE_DIRECT);
    }
    /**
     * 獲取隊列A
     * @return
     */
    @Bean
    public Queue queueA() {
        return new Queue(JW_QUEUE_DIRECT, true); //隊列持久
    }

    //一個交換機可以綁定多個消息隊列,也就是消息通過一個交換機,可以分發到不同的隊列當中去。

    @Bean
    public Binding binding() {
        return BindingBuilder.bind(queueA()).to(defaultExchange()).with(JW_ROUTINGKEY_DIRECT);
    }
}

服務端發送消息

@Autowired
RabbitTemplate rabbitTemplate;  //使用RabbitTemplate,這提供了接收/發送等等方法

rabbitTemplate.convertAndSend(RabbitConfig.JW_EXCHANGE_DIRECT, RabbitConfig.JW_ROUTINGKEY_DIRECT, JSON.toJSONString(jwlog));//jwlog爲消息實體類

消費端配置線程池

package cn.edu.nfu.jw.log.conf;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ThreadPoolExecutor;

/**
 * @Auther: cjq
 * @Date: 2020/4/15 11:33
 * @Description:
 * @version:V1.0
 * 用到線程池的時候還需要創建Executors,spring中有十分優秀的支持,
 * 就是註解@EnableAsync就可以使用多線程,@Async加在線程任務的方法上(需要異步執行的任務),
 * 定義一個線程任務,通過spring提供的ThreadPoolTaskExecutor就可以使用線程池
 *
 */
@Configuration
@EnableAsync
public class ThreadPoolConf {

    @Bean
    public ThreadPoolTaskExecutor logThreadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 設置核心線程數,默認爲1
        executor.setCorePoolSize(10);
        // 設置最大線程數,默認爲Integer.MAX_VALUE
        executor.setMaxPoolSize(50);
        // 設置隊列容量,一般需要設置值大於等於notifyScheduledMainExecutor.maxNum;默認爲Integer.MAX_VALUE
//        executor.setQueueCapacity(2);
        // 設置線程池維護線程所允許的空閒時間,默認爲60s
        executor.setKeepAliveSeconds(300);
        // 設置默認線程名稱
        executor.setThreadNamePrefix("jw-thread-");
        // 設置拒絕策略rejection-policy:當pool已經達到max size的時候,如何處理新任務 CALLER_RUNS:不在新線程中執行任務,而是有調用者所在的線程來執行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 等待所有任務結束後再關閉線程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        return executor;
    }

}

線程池業務實現類

日誌存儲服務接口類

package cn.edu.nfu.jw.log.service;


import cn.edu.nfu.jw.log.domain.Jwlog;


/**
 * <pre>
 * 日誌存儲服務接口類
 * </pre>
 * 
 * @author chijiaqi
 * @version 1.00.00
 *
 */
public interface ILogStoreService {

	/**
	 * 保存日誌
	 * 
	 * @param jwlog
	 */
	public abstract void saveSqlLogs(Jwlog jwlog);
}

日誌存儲服務實現類

package cn.edu.nfu.jw.log.service.imple;

import cn.edu.nfu.jw.log.dao.JwlogDao;
import cn.edu.nfu.jw.log.domain.Jwlog;
import cn.edu.nfu.jw.log.service.ILogStoreService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;


/**
 * @Auther: cjq
 * @Date: 2020/4/15 11:33
 * @Description:日誌存儲服務實現類
 * @version:V1.0
 */
@Service
public class LogStoreServiceImpl implements ILogStoreService {
    private static final Log log = LogFactory.getLog(LogStoreServiceImpl.class);

    @Resource
    protected ThreadPoolTaskExecutor logThreadPoolTaskExecutor;

    @Resource
    private JwlogDao<Jwlog> jwlogDao;

    /*@Async("logThreadPoolTaskExecutor")
    @Override
    public void saveSqlLogs(Jwlog jwlog) {
        try {
            jwlogDao.save(jwlog);
        } catch (Exception e) {
            log.error("保存日誌到數據庫異常,具體異常信息爲:" + e.getMessage(), e);
        }
    }*/

    @Override
    public void saveSqlLogs(Jwlog jwlog) {
        try {
            logThreadPoolTaskExecutor.execute(new SqlLogSaveServiceImpl(jwlog));
        } catch (Exception e) {
            log.error("保存日誌異常,異常信息爲:" + e.getMessage(), e);
        }
    }


    /**
     * @Auther: cjq
     * @Date: 2020/4/15 11:33
     * @Description:日誌保存服務類
     * @version:V1.0
     */
    class SqlLogSaveServiceImpl implements Runnable {
        private Jwlog jwlog;

        public SqlLogSaveServiceImpl(Jwlog jwlog) {
            super();
            this.jwlog = jwlog;
        }

        public void run() {
            try {
                jwlogDao.save(jwlog);
            } catch (Exception e) {
                log.error("保存日誌到數據庫異常,具體異常信息爲:" + e.getMessage(), e);
            }
        }
    }
}

消費端接收消息

package cn.edu.nfu.jw.log.listener;

import cn.edu.nfu.jw.conf.RabbitConfig;
import cn.edu.nfu.jw.exception.ServiceException;
import cn.edu.nfu.jw.log.controller.JwlogController;
import cn.edu.nfu.jw.log.domain.Jwlog;
import cn.edu.nfu.jw.log.service.ILogStoreService;
import com.alibaba.fastjson.JSON;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * 日誌消費
 */
@Component
@RabbitListener(queues = RabbitConfig.JW_QUEUE_DIRECT)//監聽的隊列名稱 JwDirectQueue
public class DirectReceiver {
    private static final Log log = LogFactory.getLog(JwlogController.class);
    @Resource
    private ILogStoreService logStoreService;
    @RabbitHandler
    public void process(String jsonStr) {
        log.info("DirectReceiver消費者收到消息  : " + jsonStr);
        try{
            Jwlog jwlog = JSON.parseObject(jsonStr,Jwlog.class);
            //通過線程池,開啓多線程來保存日誌
            logStoreService.saveSqlLogs(jwlog);
        }catch (Exception e){
            log.error("從MQ隊列裏面獲取信息保存異常,異常信息爲:"+e.getMessage(),e);
            throw new ServiceException(-1,"從MQ隊列裏面獲取信息保存異常,異常信息爲:"+e.getMessage());
        }
    }
 
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章