SpringMvc整合rabbitMQ

 

0.我的目錄結構

1.需要依賴的jar包 

<dependency>
    <groupId>com.rabbitmq</groupId>
    <artifactId>amqp-client</artifactId>
    <version>3.5.7</version>
</dependency>

<dependency>
    <groupId>org.springframework.amqp</groupId>
    <artifactId>spring-amqp</artifactId>
    <version>1.5.6.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-messaging</artifactId>
    <version>4.2.5.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.springframework.amqp</groupId>
    <artifactId>spring-rabbit</artifactId>
    <version>1.5.6.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    <version>1.1.2.RELEASE</version>
</dependency>

2.生產者配置文件 applicationContext-rabbitmq-send.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns:rabbit="http://www.springframework.org/schema/rabbit"
	xmlns:task="http://www.springframework.org/schema/task"
	xsi:schemaLocation="
	http://www.springframework.org/schema/rabbit
	http://www.springframework.org/schema/rabbit/spring-rabbit-1.4.xsd
    http://www.springframework.org/schema/task  
    http://www.springframework.org/schema/task/spring-task-4.1.xsd
    http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">
	<!--導入配置文件 -->
	<bean id="mysqlSource"
		  class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="location" value="classpath:jdbcConfig.properties"/>
	</bean>
	
	<!-- 定義RabbitMQ的連接工廠 -->
	<rabbit:connection-factory id="connectionFactory"
							   username="${rabbit.username}" password="${rabbit.password}" host="${rabbit.host}" port="${rabbit.port}"
							   virtual-host="${rabbit.virtual-host}"
							   publisher-confirms="true"
							   publisher-returns="true"
							   channel-cache-size="5"
							   />

	<!-- 定義Rabbit模板,指定連接工廠以及定義exchange
		rabbitTemplate   
	 -->
	<!--定義rabbit template用於數據的接收和發送 -->
	<rabbit:template id="rabbitTemplate"  connection-factory="connectionFactory"
					 exchange="${rabbit.direct-exchange}" confirm-callback="CallBackMQ"
					 mandatory="true"
					 return-callback="ReturnCall"/>

	<!-- MQ的管理,包括隊列、交換器等 -->
	<rabbit:admin connection-factory="connectionFactory" />
	
	<!--<rabbit:fanout-exchange name=""></rabbit:fanout-exchange>-->


	<!--定義queue -->
	<rabbit:queue name="queueZhoa111" auto-declare="true"/>

    <!-- 定義direct exchange,綁定queue -->
	<rabbit:direct-exchange name="exchangeZhoa111" auto-declare="true" durable="true">
		<rabbit:bindings>
			<rabbit:binding queue="queueZhoa111" key="zhoa"/>
		</rabbit:bindings>
	</rabbit:direct-exchange>

	<!--消息是否成功發送到Exchange 的回調-->
	<bean id="CallBackMQ" class="com.util.rabbitmq.CallBackMQ"></bean>
	<!--消息從Exchange路由到隊列的回調,注意這裏只有路由失敗的時候纔會調此方法-->
	<bean id="ReturnCall" class="com.util.rabbitmq.ReturnCall"></bean>
</beans>

3.消費者配置文件 applicationContext-rabbitmq-receive.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns:rabbit="http://www.springframework.org/schema/rabbit"
	xmlns:task="http://www.springframework.org/schema/task"
	xsi:schemaLocation="
	http://www.springframework.org/schema/rabbit
	http://www.springframework.org/schema/rabbit/spring-rabbit-1.4.xsd
    http://www.springframework.org/schema/task  
    http://www.springframework.org/schema/task/spring-task-4.1.xsd
    http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">

	<!--導入配置文件 -->
	<bean id="mysqlSource"
		  class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="location" value="classpath:jdbcConfig.properties"/>
	</bean>
	
	<!-- 定義RabbitMQ的連接工廠 -->
	<rabbit:connection-factory id="connectionFactory"
							   username="${rabbit.username}" password="${rabbit.password}" host="${rabbit.host}" port="${rabbit.port}"
							   virtual-host="${rabbit.virtual-host}"
							   publisher-confirms="true"
							   publisher-returns="true"
							   channel-cache-size="5"
							   />

	<!-- MQ的管理,包括隊列、交換器等 -->
	<rabbit:admin connection-factory="connectionFactory" />

	<!--定義消息隊列queue -->
	<rabbit:queue name="queueZhoa111" auto-declare="true"/>

    <!-- 定義交換機,並且完成隊列和交換機的綁定 -->
	<rabbit:direct-exchange name="exchangeZhoa111" auto-declare="true">
		<rabbit:bindings>
			<rabbit:binding queue="queueZhoa111" key="zhoa"/>
		</rabbit:bindings>
	</rabbit:direct-exchange>

	<!-- 定義監聽 -->
	<!--定義消費者監聽隊列 acknowledge設置消費者手動確認消息 原因是:rabbitmq默認是自動確認消息的,不管消費者有沒有消費成功
    只要消費者收到消息後就直接確認了,確認後rabbitmq就會將隊列中的消息刪除掉 如果消費者收到消息後實際沒有消費成功,就會導致消息丟失
        -->
	<rabbit:listener-container connection-factory="connectionFactory" acknowledge="manual">
		<!-- 監聽一個隊列,當隊列中有消息,就會自動觸發類.方法,傳遞消息就作爲方法的參數,根據方法聲明的參數強轉 -->
		<rabbit:listener ref="messageReceiver2"  queue-names="queueZhoa111"/>
	</rabbit:listener-container>

	<bean id="messageReceiver2" class="com.util.rabbitmq.CatHandler2"/>
</beans>

4.引入的配置文件配置 jdbcConfig.properties  (路徑錯誤的話自己修改)

rabbit.host=127.0.0.1
rabbit.port=5672
rabbit.username=zsq
rabbit.password=zsq
rabbit.virtual-host=/zhang
rabbit.direct-exchange=exchangeZhoa

5. applicationContext.xml 引入配置文件 ,添加線程池配置

<!--添加配置文件-->
	<import resource="classpath*:applicationContext-rabbitmq-receive.xml" />
	<import resource="classpath*:applicationContext-rabbitmq-send.xml" />

	<!--添加線程池-->
	<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
		<!-- 核心線程數 -->
		<property name="corePoolSize" value="10" />
		<!-- 最大線程數 -->
		<property name="maxPoolSize" value="20" />
		<!-- 隊列最大長度 -->
		<property name="queueCapacity" value="1800" />
		<!-- 線程池維護線程所允許的空閒時間,默認爲60s -->
		<property name="keepAliveSeconds" value="60" />
	</bean>
	<!-- 註解式 -->
	<task:annotation-driven />

6.springmvcConfig.xml 配置文件,添加掃包範圍

<context:component-scan base-package="com.util.rabbitmq"/>

7,核心代碼 

 controller控制層,調用發送消息工具類

@RestController
@RequestMapping("/test")
public class RabbitMQTest {

    @Autowired
    private RabbitAckServiceImpl rabbitAckService;

    @RequestMapping("/test2")
    public void sendMessage2() {
        for (int i = 1; i <=100 ; i++) {
            Map<String,Object> map=new HashMap<>();
            map.put("hh","zsq"+i);
            System.out.println("to send message:  "+ map);
            String json = JSON.toJSONString(map);
            boolean zhoa = rabbitAckService.sendMessage("exchangeZhoa111","zhoa", json);
            if(zhoa){
                System.out.println("發送成功1");
            }else {
                System.out.println("發送失敗");
            }
        }
    }
}

工具類發送消息 

@Service
public class RabbitAckServiceImpl {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * @param exchange      交換機
     * @param routingKey    路由key
     * @param message       消息
     */
    public boolean sendMessage(String exchange,String routingKey , String message) {
        boolean result=false;
        if(!checkParam(exchange,routingKey,message)){
            return false;
        }
        try {
            rabbitTemplate.convertAndSend(exchange,routingKey,message);
            result = true;
        }catch (AmqpException e){
            e.printStackTrace();
        }
        return result;
    }

    public boolean checkParam(String param1){
        if(StringUtils.isEmpty(param1)){
            return false;
        }else {
            return true;
        }
    }

    public boolean checkParam(String param1,String param2){
        if(StringUtils.isEmpty(param1)||StringUtils.isEmpty(param2)){
            return false;
        }else {
            return true;
        }
    }

    public boolean checkParam(String param1,String param2,String param3){
        if(StringUtils.isEmpty(param1)||StringUtils.isEmpty(param2)||StringUtils.isEmpty(param3)){
            return false;
        }else {
            return true;
        }
    }
}

 實現接口 , 消息發送到exchang交換機 ,  返回投遞結果

public class CallBackMQ implements RabbitTemplate.ConfirmCallback {

    int i=1;
    @Override   //消息投遞到exchange是否成功
    public void confirm(CorrelationData correlationData, boolean b, String s) {
        if (b) {
            //設置消息投遞成功
            System.out.println("消息投遞成功"+i);
            i++;
        } else {
            //消息投遞失敗
            System.out.println(s);
            System.out.println("消息投遞失敗");
        }
    }
}

 實現接口, 消息從Exchange交換機 發送到 對列Queue 失敗時回調執行此方法

public class ReturnCall implements RabbitTemplate.ReturnCallback {
    /**
     *只有消息從Exchange路由到Queue失敗纔會回調這個方法
     */
    @Override
    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
        System.out.println("消息從Exchage路由到Queue失敗");
    }
}

消費者消費 ,採用多線程異步消費 

//消費者 採用異步多線程消費
public class CatHandler2 implements ChannelAwareMessageListener {
    
    @Autowired
    private TaskExecutor taskExecutor;

    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        taskExecutor.execute(new Consume(message,channel));
    }
}

 線程類方法

public class Consume implements  Runnable{

    private Message message;
    private Channel channel;

    public  Consume(Message message, Channel channel){
        this.message=message;
        this.channel=channel;
    }


    @Override
    public void run() {
        boolean falg = false;
        try {
            // msg就是rabbitmq傳來的消息,需要的同學自己打印看一眼
            // 使用jackson解析
            Map map = JSON.parseObject(message.getBody(), Map.class);
            System.out.println(Thread.currentThread().getName()+"收到消息:我是可愛的小豬,我的名字是" + map.get("hh"));
            //Thread.sleep(2000);
            falg = true;
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            MessageProperties properties = message.getMessageProperties();
            long tag = properties.getDeliveryTag();
            //消費成功後將手動確認消息
            if (falg) {
                //消息確認,發送成功
                try {
                    channel.basicAck(tag, false);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } else {
                //如果發送失敗,消息會重新進入隊列,再次被這個消費者消費
                //消息發送失敗後,也可以將消息發送到別的隊列,讓其他消費者進行消費
                //第三個參數 true爲重新將消息放入隊列,如果設置爲false,則拋棄這條消息
                try {
                    channel.basicNack(tag, false, true);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

8.測試結果

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章