SpringCloud——Stream(消息驱动)

本文介绍SpringCloud Stream框架,用于构建微服务应用的消息驱动能力。它通过SpringIntegration连接消息中间件,提供发布-订阅、消费组、消息分区等功能,实现消息事件驱动。文中详细介绍了SpringCloud Stream的四种集成风格及应用模型,并通过具体代码示例展示了如何创建消费者和生产者。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、Spring Cloud Stream简介

          Spring Cloud Stream 是一个用来为微服务应用构建消息驱动能力的框架。它可以基于Spring Boot 来创建独立的,可用于生产的Spring 应用程序。他通过使用Spring Integration来连接消息代理中间件以实现消息事件驱动。Spring Cloud Stream 为一些供应商的消息中间件产品提供了个性化的自动化配置实现,引用了发布-订阅、消费组、分区的三个核心概念。

1、四种集成风格:

  • 文件传输:两个系统生成文件,文件的有效负载就是由另一个系统处理的消息。该类风格的例子之一是针对文件轮询目录或FTP目录,并处理该文件。
  • 共享数据库:两个系统查询同一个数据库以获取要传递的数据。一个例子是你部署了两个EAR应用,它们的实体类(JPA、Hibernate等)共用同一个表。
  • 远程过程调用:两个系统都暴露另一个能调用的服务。该类例子有EJB服务,或SOAP和REST服务。
  • 消息:两个系统连接到一个公用的消息系统,互相交换数据,并利用消息调用行为。该风格的例子就是众所周知的中心辐射式的(hub-and-spoke)JMS架构。

2、应用模型

      Spring Cloud Stream由一个中间件中立的核组成。应用通过Spring Cloud Stream插入的input和output通道与外界交流。通道通过指定中间件的Binder实现与外部代理连接。业务开发者不再关注具体消息中间件,只需关注Binder对应用程序提供的抽象概念来使用消息中间件实现业务即可。

Spring Cloud Stream应用

3、绑定器

      通过定义绑定器作为中间层,实现了应用程序与消息中间件细节之间的隔离。通过向应用程序暴露统一的Channel通过,是的应用程序不需要再考虑各种不同的消息中间件的实现。当需要升级消息中间件,或者是更换其他消息中间件产品时,我们需要做的就是更换对应的Binder绑定器而不需要修改任何应用逻辑 。

4、发布-订阅

      应用间通信遵照发布-订阅模型,消息通过共享主题进行广播。如图所示,显示了交互的Spring Cloud Stream 应用的典型布局。

Spring Cloud Stream发布订阅模型

未处理的传感数据发布到raw-sensor-data的Topic进行广播,Averages 和IngestHDFS同时订阅了此消息,收到消息后触发自身的处理逻辑。

5、消费组

     由于发布-订阅模型使得共享主题的应用之间连接更简便,创建给定应用的不同实例来进行弹性扩张的能力也同样重要。如果存在多个应用实例,那么同一应用的额不同实例便会成为相互竞争的消费者,其中应该只有一个实例处理给定消息。 
  SpringCloud Stream通过消费者组的概念给这种情况进行建模。每一个单独的消费者可以使用spring.cloud.stream.bindings.input.group属性来指定一个组名字。如图中展示的消费者们,这一属性被设置为spring.cloud.stream.bindings.input.group=hdfsWrite或者spring.cloud.stream.bindings.input.group=average。  

      所有订阅给定目标的组都会收到发布消息的一个拷贝,但是每一个组内只有一个成员会收到该消息。默认情况下,如果没有指定组,Spring Cloud Stream 会将该应用指定给一个匿名的独立的单成员消费者组,后者与所有其他组都处于一个发布-订阅关系中。

Spring Cloud Stream消费者组

6、消息分区

      Spring Cloud Stream对给定应用的多个实例之间分隔数据予以支持。一个或者多个生产者应用实例给多个消费者应用实例发送消息并确保相同特征的数据被同一消费者实例处理。 
     Spring Cloud Stream对分割的进程实例实现进行了抽象。使得Spring Cloud Stream 为不具备分区功能的消息中间件(RabbitMQ)也增加了分区功能扩展。

Spring Cloud Stream Partitioning

 

二、代码实现

1、创建消费者(stream-sink)

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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.example.demo.stream</groupId>
	<artifactId>stream-sink</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>stream-sink</name>
	<description></description>

	<parent>
		<groupId>com.example.demo</groupId>
		<artifactId>springcloud-stream</artifactId>
		<version>0.0.1-SNAPSHOT</version>
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>

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

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

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

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


</project>

application.yml配置文件:

server:
  port: 8081
spring:
  application:
    name: stream-sink
  rabbitmq:
    host: 192.168.0.132
    port: 5672
    username: admin
    password: admin
  cloud:
    stream:
      binders:
        input:
          destination: output

消费者实例SinkReceiver.java文件:

package com.example.demo.stream.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.cloud.stream.messaging.Sink;

/**
 * 路径:com.example.demo.stream.service
 * 类名:
 * 功能:用来指定一个或多个定义了@Input或@Output注解的接口
 * 备注:消费者
 * 创建人:typ
 * 创建时间:2018/9/25 14:45
 * 修改人:
 * 修改备注:
 * 修改时间:
 */
@EnableBinding(value = {Sink.class})
public class SinkReceiver {

    private static final Logger log = LoggerFactory.getLogger(SinkReceiver.class);
    
    /**
     * 方法名:
     * 功能:将被修饰的方法注册到消息中间件上的数据流事件监听器中
     * 描述:
     * 创建人:typ
     * 创建时间:2018/9/25 16:36
     * 修改人:
     * 修改描述:
     * 修改时间:
     */
    @StreamListener(Sink.INPUT)
    public void receive(String str){
        log.info("Received:" + str);
    }
}

启动工程,浏览器访问:http://192.168.0.132:5672/#/queues

发送消息:

控制台数据信息:

2018-09-25 16:46:10.749  INFO 2564 --- [Ye2ZeeV2sRHjA-1] c.e.demo.stream.service.SinkReceiver     : Received:Hello World!

2、创建生产者(stream-source)

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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.example.demo.stream</groupId>
	<artifactId>stream-source</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>stream-source</name>

	<parent>
		<groupId>com.example.demo</groupId>
		<artifactId>springcloud-stream</artifactId>
		<version>0.0.1-SNAPSHOT</version>
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>

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

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

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

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


</project>

application.yml配置文件:

server:
  port: 8082
spring:
  application:
    name: stream-source
  rabbitmq:
    host: 192.168.0.132
    port: 5672
    username: admin
    password: admin
  cloud:
    stream:
      binders:
        input:
          destination: input

创建生产者实例Sender.java 文件:

package com.example.demo.stream.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.integration.annotation.InboundChannelAdapter;
import org.springframework.integration.annotation.Poller;

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

/**
 * 路径:com.example.demo.stream.service
 * 类名:
 * 功能:生产者
 * 备注:
 * 创建人:typ
 * 创建时间:2018/9/25 15:57
 * 修改人:
 * 修改备注:
 * 修改时间:
 */
@EnableBinding(Source.class)
public class Sender {

    private static final Logger log = LoggerFactory.getLogger(Sender.class);

    @InboundChannelAdapter(value = Source.OUTPUT,poller = @Poller(fixedDelay = "2000"))
    public String send(){
        String str = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        log.info("send message:"+str);
        return str;
    }
}

启动工程:

 

 

### 关于Spring Cloud Stream的使用案例和应用场景 #### 使用案例分析 在一个分布式微服务架构中,多个应用程序之间可能需要通过异步方式交换数据。例如,在电商系统中,订单创建成功后需通知库存管理系统减少相应商品数量。此时可以利用Spring Cloud Stream来构建事件驱动的应用程序。 具体来说,当第一个App(Service A)处理完成某些业务逻辑并生成一条消息时,会将其发送到指定的消息通道上;而另一个监听该通道的App(Service B),一旦接收到新消息就会触发相应的处理器函数来进行后续操作[^4]。 ```java @EnableBinding(Source.class) public class OrderProducer { @Autowired private Source source; public void sendOrderEvent(Order order){ MessageChannel messageChannel = this.source.output(); messageChannel.send(MessageBuilder.withPayload(order).build()); } } ``` 以上代码展示了如何定义一个生产者端的服务接口,并向外部传递带有负载信息的数据包给其他订阅此主题的服务实例。 对于消费者而言,则可以通过如下形式声明接收来自特定Topic上的所有更新: ```java @Component @RequiredArgsConstructor(onConstructor_=@Autowired) @Slf4j public class InventoryConsumer { @StreamListener(Sink.INPUT) public void receiveOrder(@Payload Order order) { log.info("Received an order event: " + order); // Process the received data, e.g., update inventory. } } ``` 这段Java片段说明了怎样设置好监听器以捕获由`Sink.INPUT`所代表的目标位置传来的任何变动请求,并执行预定的任务流程——这里是以日志记录为例简单示意了一下实际工作中可能会涉及到的操作之一。 #### 应用场景探讨 - **跨平台集成**:借助于Binder机制的支持,即使底层采用不同类型的MQ组件也不会影响高层设计模式的一致性和灵活性。这使得开发者能够更加专注于核心功能模块的设计而非基础设施层面的选择难题[^2]。 - **松散耦合**:由于引入了中间层作为中介桥梁连接起各个独立部署单元间的通讯路径,因此即便后期更换了新的传输协议栈也只需调整少量配置项即可顺利完成过渡工作而不必重写大量现有源码文件[^3]. - **实时数据分析**:随着物联网设备普及程度日益提高以及边缘计算技术的发展壮大,越来越多的企业渴望获取即时反馈以便快速响应市场变化趋势。基于流式编程模型之上建立起来的大规模并发处理框架正好满足此类需求特点[^1].
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值