一、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对应用程序提供的抽象概念来使用消息中间件实现业务即可。
通过定义绑定器作为中间层,实现了应用程序与消息中间件细节之间的隔离。通过向应用程序暴露统一的Channel通过,是的应用程序不需要再考虑各种不同的消息中间件的实现。当需要升级消息中间件,或者是更换其他消息中间件产品时,我们需要做的就是更换对应的Binder绑定器而不需要修改任何应用逻辑 。
应用间通信遵照发布-订阅模型,消息通过共享主题进行广播。如图所示,显示了交互的Spring Cloud Stream 应用的典型布局。
未处理的传感数据发布到raw-sensor-data的Topic进行广播,Averages 和IngestHDFS同时订阅了此消息,收到消息后触发自身的处理逻辑。
由于发布-订阅模型使得共享主题的应用之间连接更简便,创建给定应用的不同实例来进行弹性扩张的能力也同样重要。如果存在多个应用实例,那么同一应用的额不同实例便会成为相互竞争的消费者,其中应该只有一个实例处理给定消息。
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对给定应用的多个实例之间分隔数据予以支持。一个或者多个生产者应用实例给多个消费者应用实例发送消息并确保相同特征的数据被同一消费者实例处理。
Spring Cloud Stream对分割的进程实例实现进行了抽象。使得Spring Cloud Stream 为不具备分区功能的消息中间件(RabbitMQ)也增加了分区功能扩展。
二、代码实现
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;
}
}
启动工程: