RabbitMQ -springboot整合rabbitmq

OkidoGreen發表於2020-11-01

課程:https://www.cnblogs.com/guchunchao/p/13173406.html

閱讀目錄

 


匯入pom.xml

複製程式碼

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.everjiankang</groupId>
    <artifactId>rabbitmqDemo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>rabbitmqDemo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        
        <!--  匯入配置檔案處理器,配置檔案進行繫結就會有提示 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.71</version>
        </dependency>
        
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
        </dependency>
        
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

複製程式碼

回到頂部

配置生產者

第一步:配置application.properties

複製程式碼

spring.rabbitmq.addresses=127.0.0.1:5672
spring.rabbitmq.username=xiaochao
spring.rabbitmq.password=root
spring.rabbitmq.virtual-host=/
spring.rabbitmq.connection-timeout=15000

#生產端配置
#開啟傳送確認,此配置在Springboot2.3.0版本中已經@Deprecated了,預設就是
# spring.rabbitmq.publisher-confirms=true
#
spring.rabbitmq.publisher-confirm-type=simple
#開啟傳送失敗退回
spring.rabbitmq.publisher-returns=true
#開啟執行return回撥
spring.rabbitmq.template.mandatory=true

複製程式碼

 

第二步:開發啟動類Application.java

複製程式碼

package com.dwz.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

複製程式碼

 

第三步:加入實體類order

複製程式碼

package com.everjiankang.dependency.model;

import java.io.Serializable;

public class Order implements Serializable {
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    
    private String id;
    private String name;
    
    public Order() {
    }

    public Order(String id, String name) {
        super();
        this.id = id;
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    } 
}

複製程式碼

 

第四步:開發訊息傳送方法

  (需要先手動建立交換器、佇列、繫結關係。或者通過程式碼Build)

複製程式碼

package com.everjiankang.dependency.springboot.producer;

import java.util.Map;

import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.core.RabbitTemplate.ConfirmCallback;
import org.springframework.amqp.rabbit.core.RabbitTemplate.ReturnCallback;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Component;

import com.everjiankang.dependency.model.Order;

@Component
public class RabbitmqSender {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    /**
     * publisher-confirms,實現一個監聽器用於監聽broker端給我們返回的確認請求:
     * RabbitTemplate.ConfirmCallback
     * 
     * publisher-returns,保證訊息對broker端是可達的,如果出現路由鍵不可達的情況,
     *     則使用監聽器對不可達的訊息進行後續的處理,保證訊息的路由成功:
     * RabbitTemplate.ReturnCallback
     * 
     * 注意:在傳送訊息的時候對template配置mandatory=true保證ReturnCallback監聽有效
     * 生產端還可以配置其他屬性,比如傳送重試,超時時間、次數、間隔等。
     */
    final ConfirmCallback confirmCallback = new RabbitTemplate.ConfirmCallback() {
        @Override
        public void confirm(CorrelationData correlationData, boolean ack, String cause) {
            System.err.println("confirm correlationData:" + correlationData);
            System.err.println("confirm ack:" + ack);
            if(!ack) {
                System.err.println("異常處理。。。");
            }
        }
    };
    
    final ReturnCallback returnCallback = new RabbitTemplate.ReturnCallback() {
        
        @Override
        public void returnedMessage(org.springframework.amqp.core.Message message, int replyCode, String replyText,
                String exchange, String routingKey) {
            System.err.println("return exchange:" + exchange);
            System.err.println("return routingKey:" + routingKey);
            System.err.println("return replyCode:" + replyCode);
            System.err.println("return replyText:" + replyText);
        }
    };
    
    public void send(Object message, Map<String, Object> properties) throws Exception {
        MessageHeaders mhs = new MessageHeaders(properties);
        Message msg = MessageBuilder.createMessage(message, mhs);
        rabbitTemplate.setConfirmCallback(confirmCallback);
        rabbitTemplate.setReturnCallback(returnCallback);
        CorrelationData correlationData = new CorrelationData();
        //實際訊息的唯一id
        correlationData.setId("dwz123456");//id + 時間戳 (必須是全域性唯一的)
        rabbitTemplate.convertAndSend("exchange-1", "spring.abc", msg, correlationData);
    }
    
    public void sendOrder(Order order) throws Exception {
        rabbitTemplate.setConfirmCallback(confirmCallback);
        rabbitTemplate.setReturnCallback(returnCallback);
        CorrelationData correlationData = new CorrelationData();
        correlationData.setId("zheaven123456");//id + 時間戳 (必須是全域性唯一的)
        rabbitTemplate.convertAndSend("exchange-2", "springboot.def", order, correlationData);
    }
}

複製程式碼

 

第五步:測試傳送訊息

複製程式碼

package com.dwz.springboot;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import com.dwz.springboot.entity.Order;
import com.dwz.springboot.producer.RabbitSender;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootTest {
    @Autowired
    private RabbitSender send;
    
    private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
    
    @Test
    public void testSender1() throws Exception {
        Map<String, Object> properties = new HashMap<String, Object>();
        properties.put("number", "12345");
        properties.put("send_time", simpleDateFormat.format(new Date()));
        send.send("Hello Rabbirmq For springboot!", properties);
        
    }
    
    @Test
    public void testSender2() throws Exception {
        Order order = new Order("001", "第一個訂單");
        send.sendOrder(order);
    }
}

複製程式碼

 

回到頂部

配置消費者

第一步:配置application.properties

複製程式碼

複製程式碼

spring.rabbitmq.addresses=127.0.0.1:5672
spring.rabbitmq.username=xiaochao
spring.rabbitmq.password=root
spring.rabbitmq.virtual-host=/vhost_dwz
spring.rabbitmq.connection-timeout=15000

#消費端配置
#配置手工確認模式,用於ACK的手工處理,這樣我們可以保證訊息的可靠性送達,或者在消費端消費失敗的時候可以做到重回佇列、根據業務記錄日誌等處理
spring.rabbitmq.listener.simple.acknowledge-mode=manual
#可以設定消費端的監聽個數和最大個數,用於控制消費端的併發情況
spring.rabbitmq.listener.simple.concurrency=5
spring.rabbitmq.listener.simple.max-concurrency=10

#消費端binding配置 ,以訂單為例
spring.rabbitmq.listener.order.queue.name=queue-2
spring.rabbitmq.listener.order.queue.durable=true
spring.rabbitmq.listener.order.exchange.name=exchange-2
spring.rabbitmq.listener.order.exchange.durable=true
spring.rabbitmq.listener.order.exchange.type=topic
spring.rabbitmq.listener.order.exchange.ignoreDeclarationExceptions=true
spring.rabbitmq.listener.order.key=springboot.*

複製程式碼

複製程式碼

第二步:開發啟動類Application.java

複製程式碼

package com.dwz.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
    
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

複製程式碼

 

第三步:新增主配置類

複製程式碼

package com.dwz.springboot;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan({"com.dwz.springboot.*"})
public class MainConfig {

}  

複製程式碼

 

第四步:新增訊息處理方法

複製程式碼

package com.dwz.springboot.consumer;

import java.util.Map;

import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.Message;
import org.springframework.messaging.handler.annotation.Headers;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Component;

import com.dwz.springboot.entity.Order;
import com.rabbitmq.client.Channel;

@Component
public class RabbitReceiver {
    //此配置可以直接建立Exchange、Queue、Binding
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(
                        name = "queue-1",
                        durable = "true"
                    ),
            exchange = @Exchange(
                        name = "exchange-1",
                        durable = "true",
                        type = "topic",
                        ignoreDeclarationExceptions = "true"
                    ),
            key = "springboot.*"
            )
    )
    @RabbitHandler
    public void onMessage(Message message, Channel  channel) throws Exception {
        System.err.println("------------------------------------");
        System.err.println("消費端Payload:" + message.getPayload());
        Long deliverytag = (Long)message.getHeaders().get(AmqpHeaders.DELIVERY_TAG);
        //手工ACK
        channel.basicAck(deliverytag, false);
    }
    
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(
                        name = "${spring.rabbitmq.listener.order.queue.name}",
                        durable = "${spring.rabbitmq.listener.order.queue.durable}"
                    ),
            exchange = @Exchange(
                        name = "${spring.rabbitmq.listener.order.exchange.name}",
                        durable = "${spring.rabbitmq.listener.order.exchange.durable}",
                        type = "${spring.rabbitmq.listener.order.exchange.type}",
                        ignoreDeclarationExceptions = "${spring.rabbitmq.listener.order.exchange.ignoreDeclarationExceptions}"
                    ),
            key = "${spring.rabbitmq.listener.order.key}"
            )
    )

    @RabbitHandler
    //將Message用@Payload和@Headers拆分
    //@Payload表示body裡面的資訊,@Headers表示properties的資訊
    public void onOrderMessage(@Payload Order order, Channel  channel,
            @Headers Map<String, Object> headers) throws Exception {
        System.err.println("------------------------------------");
        System.err.println("消費端Order:" + order.getId());
        Long deliverytag = (Long)headers.get(AmqpHeaders.DELIVERY_TAG);
        //手工ACK
        channel.basicAck(deliverytag, false);
    }
}

複製程式碼

 

 參考blog:

https://www.cnblogs.com/zheaven/p/11915480.html

 參考視訊:

https://www.bilibili.com/video/BV1Fz411B77A?p=34

相關文章