springcloud學習-17 Spring Cloud Stream【周陽springcloud2020學習筆記】

Spring Cloud Stream 是一個用來爲微服務應用構建消息驅動能力的框架。
Spring Cloud Stream 爲一些供應商的消息中間件產品提供了個性化的自動化配置實現,並引入了發佈-訂閱、消費組、分區這三個核心概念。
通過使用 Spring Cloud Stream,可以有效簡化開發人員對消息中間件的使用複雜度,讓系統開發人員可以有更多的精力關注於核心業務邏輯的處理。
目前僅支持 RabbitMQ 和 Kafka 的自動化配置。

概括:屏蔽底層消息中間件的差異,降低切換版本,統一消息的編程模型

Spring Cloud Stream中文指導手冊:https://m.wang1314.com/doc/webapp/topic/20971999.html

一、設計思想
1.標準MQ
生產者/消費者之間靠消息媒介傳遞信息內容:Message
消息必須走特定的通道:MessageChannel
消息通道里的消息如何被消費呢,誰負責收發處理:消息通道MessageChannel的子接口SubscribableChannel,由MessageHandler消息處理器訂閱

2.通過定義綁定器Binder作爲中間層,實現了應用程序與消息中間件細節之間的隔離。
INPUT對應於消費者
OUTPUT對應於生產者

3.Stream中的消息通信方式遵循了發佈-訂閱模式:Topic主題進行廣播
在RabbitMQ就是Exchange
在kafka中就是Topic

二、Spring Cloud Stream標準流程套路
1.Binder:很方便的連接中間件,屏蔽差異
2.Channel:通道,是隊列Queue的一種抽象,在消息通訊系統中就是實現存儲和轉發的媒介,通過對Channel對隊列進行配置
3.Source和Sink:簡單的可理解爲參照對象是Spring Cloud Stream自身,從Stream發佈消息就是輸出,接收消息就是輸入

Middleware:中間件,目前只支持RabbitMQ和Kafka
Binder:BInder是應用與消息中間件之間的封裝,目前實行了Kafka和RabbitMQ的Binder。
通過Binder可以方便的連接中間件,可以動態改變消息類型(對應於Kafka的topic,RabbitMQ的exchange),這些都可以通過配置文件來實現。
@Input:註解標識輸入通道,通過該輸入通道接收到的消息進入應用程序
@Output:註解標識輸出通道,發佈的消息將通過該通道離開應用程序
@StreamListener:監聽隊列,用於消費者的隊列的消息接收
@EnableBinding:指信道channel和exchange綁定在一起

三、案例

1.rabbitMQ開啓

2.生產者模塊,進行發消息:stream-rabbitmq-provider8801

1)新建module:stream-rabbitmq-provider8801

2)pom

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>demo2020</artifactId>
        <groupId>cn.chen.demo</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>stream-rabbitmq-provider8801</artifactId>

    <dependencies>

        <!--stream-rabbit-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
        </dependency>

        <!-- eureka-client -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <!--公共模塊-->
        <dependency>
            <groupId>cn.chen.demo</groupId>
            <artifactId>api-common</artifactId>
            <version>${project.version}</version>
        </dependency>

        <!--基本配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

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

        <!-- devtools工具 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>

</project>

3)yml

server:
  port: 8801

spring:
  application:
    name: stream-provider
  cloud:
    stream:
      binders: # 在此處配置要綁定的rabbitmq的服務信息;
        defaultRabbit: # 表示定義的名稱,用於於binding整合
          type: rabbit # 消息組件類型
          environment: # 設置rabbitmq的相關的環境配置
            spring:
              rabbitmq:
                host: localhost
                port: 5672
                username: guest
                password: guest
      bindings: # 服務的整合處理
        output: # 這個名字是一個通道的名稱
          destination: studyExchange # 表示要使用的Exchange名稱定義
          content-type: application/json # 設置消息類型,本次爲json,文本則設置“text/plain”
          binder: defaultRabbit  # 設置要綁定的消息服務的具體設置

eureka:
  client: # 客戶端進行Eureka註冊的配置
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka
  instance:
    lease-renewal-interval-in-seconds: 2 # 設置心跳的時間間隔(默認是30秒)
    lease-expiration-duration-in-seconds: 5 # 如果現在超過了5秒的間隔(默認是90秒)
    instance-id: send-8801.com  # 在信息列表時顯示主機名稱
    prefer-ip-address: true     # 訪問的路徑變爲IP地址

4)主啓動

@SpringBootApplication
public class StreamMQProvider8801 {

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

5)業務類

  • 發送消息接口
public interface IMessageProvider {

	/**
	 * @title send
	 * @Description 發送消息
	 **/
	public void send();
}
  • 發送消息接口實現類
@Slf4j
@EnableBinding(Source.class) //定義消息的推送管道
public class MessageProviderImpl implements IMessageProvider{

	@Autowired
	private MessageChannel output; // 消息發送管道

	@Override
	public void send() {
		String serial = UUID.randomUUID().toString();
		output.send(MessageBuilder.withPayload(serial).build());
		log.info("隨機數:" + serial);
	}
}
  • controller
@RestController
@RequestMapping("/send")
public class SendMessageController {
	@Resource
	private IMessageProvider messageProvider;

	@GetMapping(value = "/sendMessage")
	public String sendMessage()
	{
		return messageProvider.send();
	}

}

3.消費者模塊,接收消息:stream-rabbitmq-consumer8802

1)新建module:stream-rabbitmq-consumer8802

2)pom

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>demo2020</artifactId>
        <groupId>cn.chen.demo</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>stream-rabbitmq-consumer8802</artifactId>

    <dependencies>

        <!--stream-rabbit-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
        </dependency>

        <!-- eureka-client -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <!--公共模塊-->
        <dependency>
            <groupId>cn.chen.demo</groupId>
            <artifactId>api-common</artifactId>
            <version>${project.version}</version>
        </dependency>

        <!--基本配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

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

        <!-- devtools工具 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>

</project>

3)yml

server:
  port: 8802

spring:
  application:
    name: stream-consumer
  cloud:
    stream:
      binders: # 在此處配置要綁定的rabbitmq的服務信息;
        defaultRabbit: # 表示定義的名稱,用於於binding整合
          type: rabbit # 消息組件類型
          environment: # 設置rabbitmq的相關的環境配置
            spring:
              rabbitmq:
                host: localhost
                port: 5672
                username: guest
                password: guest
      bindings: # 服務的整合處理
        input: # 這個名字是一個通道的名稱,輸入
          destination: studyExchange # 表示要使用的Exchange名稱定義
          content-type: application/json # 設置消息類型,本次爲json,文本則設置“text/plain”
          binder: defaultRabbit  # 設置要綁定的消息服務的具體設置

eureka:
  client: # 客戶端進行Eureka註冊的配置
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka
  instance:
    lease-renewal-interval-in-seconds: 2 # 設置心跳的時間間隔(默認是30秒)
    lease-expiration-duration-in-seconds: 5 # 如果現在超過了5秒的間隔(默認是90秒)
    instance-id: receiver-8802.com  # 在信息列表時顯示主機名稱
    prefer-ip-address: true     # 訪問的路徑變爲IP地址

4)主啓動

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

5)業務類

@Component
@EnableBinding(Sink.class)
@Slf4j
public class ReceiverMessageListenerController {

	@Value("${server.port}")
	private String serverPort;

	@StreamListener(Sink.INPUT)
	public void input(Message<String> message) {
		log.info("消費者1號,接受:"+message.getPayload()+"\t port:"+serverPort);
	}

}

4.消費者模塊,接收消息:stream-rabbitmq-consumer8803
同上copy一份修改即可

5.分組消費與持久化

1)啓動兩個消費者之後出現了問題:

  • 有重複消費問題
  • 消息持久化問題

2)分組:解決重複消費問題

  • 原理:微服務應用放置於同一個group中,就能夠保證消息只會被其中一個應用消費一次。不同的組是可以消費的,同一個組內會發生競爭關係,只有其中一個可以消費。

  • 8802、8803不同組情況:yml添加分組配置
    8802:consumer1
    8803:consumer2

server:
  port: 8802

spring:
  application:
    name: stream-consumer
  cloud:
    stream:
      binders: # 在此處配置要綁定的rabbitmq的服務信息;
        defaultRabbit: # 表示定義的名稱,用於於binding整合
          type: rabbit # 消息組件類型
          environment: # 設置rabbitmq的相關的環境配置
            spring:
              rabbitmq:
                host: localhost
                port: 5672
                username: guest
                password: guest
      bindings: # 服務的整合處理
        input: # 這個名字是一個通道的名稱,輸入
          destination: studyExchange # 表示要使用的Exchange名稱定義
          content-type: application/json # 設置消息類型,本次爲json,文本則設置“text/plain”
          binder: defaultRabbit  # 設置要綁定的消息服務的具體設置
          group: consumer1 # 消費者分組

eureka:
  client: # 客戶端進行Eureka註冊的配置
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka
  instance:
    lease-renewal-interval-in-seconds: 2 # 設置心跳的時間間隔(默認是30秒)
    lease-expiration-duration-in-seconds: 5 # 如果現在超過了5秒的間隔(默認是90秒)
    instance-id: receiver-8802.com  # 在信息列表時顯示主機名稱
    prefer-ip-address: true

結論:重複消費

  • 8802、8803同組情況:group: consumer
    8802/8803實現了輪詢分組,每次只有一個消費者 8801模塊發的消息只能被8802或8803其中一個接收到,這樣避免了重複消費
    結論:同一個組的多個微服務實例,每次只會有一個拿到

3)持久化

springcloud學習系列目錄

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