SpringCloud系列之SpringCloud Stream
SpringCloud Stream
SpringCloud Config
SpringCloud Gatewa
SpringCloud Hystrix
技术兴起的原因:为了解决系统中不同中间件的适配问题,出现了cloud stream,采用适配绑定的方式,自动给不同的MQ之间进行切换。
屏蔽底层消息中间件的差异,降低切换成本,统一消息的编程模型。

官方定义Spring Cloud Stream是一个构建消息驱动微服务的框架。
应用程序通过inputs(消费者)或者outputs(生产者)来与Spring Cloud Stream中binder对象交互。通过我们配置来绑定,而Spring Cloud Stream的binder对象负责与消息中间件交互。
Spring Cloud Stream为一些供应商的消息中间件产品提供了个性化的自动配置,引用了发布、订阅、消费、分区的三个核心概念。
官方版本目前仅仅支持RabbitMQ和Kafka。
MQ相关术语
Message:生产者/消费者之间靠消息媒介传递信息内容
MessageChannel:消息必须走特定的通道
消息通道的子接口SubscribableChannel,由MessageHandle消息处理器所订阅。
相关注解
- Middleware:中间件,目前只支持RabbitMQ和Kafka
- Binder:应用层和消息中间件之间的封装,实现了Kafka和RabbitMQ的Binder,通过Binder可以很方便的连接中间件,可以动态的改变消息类型,这些可以通过配置文件修改。
- Input:表示输入通道,消息进入该通道传到应用程序。
- Output:注解标识输出通道,发布的消息将通过该通道离开应用程序。
- StreamListener:监听队列,用于消费者的队列的消息接收。
- EnableBinding:将信道channel和exchange绑定在一起。
首先创建一个provider,服务提供者rabbitmq-provider8801
导入依赖
<dependencies>
<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>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</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-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
编写配置文件application.yml
server:
port: 8801
spring:
application:
name: cloud-stream-provider
cloud:
stream:
binders: # 在此处配置要绑定的rabbitmq的服务信息;
defaultRabbit: # 表示定义的名称,用于于binding整合
type: rabbit # 消息组件类型
environment: # 设置rabbitmq的相关的环境配置
spring:
rabbitmq:
host: 192.168.31.52 #rabbitmq服务启动所在机器的IP地址
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://localhost: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地址
编写一个发送数据的接口IMessageProvider
public interface IMessageProvider {
String sendMessage();
}
接口的实现类IMessageProviderImpl
@EnableBinding(Source.class) //定义消息的推送管道
public class IMessageProviderImpl implements IMessageProvider
{
@Resource
private MessageChannel output; // 消息发送管道
@Override
public String sendMessage()
{
String serial = UUID.randomUUID().toString();
output.send(MessageBuilder.withPayload(serial).build());
System.out.println("*****serial: "+serial);
return null;
}
}
controller层下的SendMessageController
@RestController
public class SendMessageController {
@Autowired
private IMessageProvider iMessageProvider;
@GetMapping(value = "/sendMessage")
public String send(){
return iMessageProvider.sendMessage();
}
}
启动Eureka7001,启动服务提供者8801.启动虚拟机上的RabbitMQ

记得把虚拟机防火墙关了。
[hadoop@centos7 bin]$ systemctl stop firewalld
[hadoop@centos7 bin]$ systemctl status firewalld
然后测试一下服务提供者是否正常运行。
发送请求:http://localhost:8801/sendMessage

控制台输出UUID。
然后再创建一个服务消费者,在MQ的另一端进行消费消息。
创建另一个模块,cloud-stream-rabbitmq-consumer8802
导入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</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-actuator</artifactId>
</dependency>
<!--基础配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
和上一个服务提供者的依赖一样。
写配置文件application.yml
server:
port: 8802
spring:
application:
name: cloud-stream-consumer
cloud:
stream:
binders: # 在此处配置要绑定的rabbitmq的服务信息;
defaultRabbit: # 表示定义的名称,用于于binding整合
type: rabbit # 消息组件类型
environment: # 设置rabbitmq的相关的环境配置
spring:
rabbitmq:
host: 192.168.31.52
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://localhost:7001/eureka
instance:
lease-renewal-interval-in-seconds: 2 # 设置心跳的时间间隔(默认是30秒)
lease-expiration-duration-in-seconds: 5 # 如果现在超过了5秒的间隔(默认是90秒)
instance-id: receive-8802.com # 在信息列表时显示主机名称
prefer-ip-address: true # 访问的路径变为IP地址
创建一个消费者的ReceiveMessageController
@Component
@EnableBinding(Sink.class)
public class ReceiveMessageController {
@Value("${server.port}")
private String serverPort;
@StreamListener(Sink.INPUT)
public void input(Message<String> message){
System.out.println("message = "+message.getPayload()+"\t"+"serverPort= "+serverPort);
}
}
如果消费者成功接收消息,则在控制台输出产生的UUID和端口号。
启动Eureka7001,启动服务提供者8801,启动服务消费者8802,还有MQ。
在Eureka中可以看到两个服务已经启动。

每次请求http://localhost:8801/sendMessage;消费者都能输出结果,输出的UUID与提供者的一致。


登录RabbitMQ的web管理,可以看到我们新建的exchange,并且可以查看消息队列中的请求次数的情况。


发送的消息除了可以是字符串类型还可以发送对象,在消费者接受数据的时候,会将实体转换成JSON字符串。
配置文件中,如果你使用的消息中间件是kafka,type: kafka;environment是设置消息中间件的配置信息,端口,主机地址,用户名,密码等,可以设置多个binder,适应不同的场景。
重复消费问题
默认情况下,每个消费者的分组名都是随机的,不同的,对于不同的组会引起重复消费的问题,例如:消息提供者只向消息队列中发送了一个消息,正常情况下,消费者A从队列中拿走之后,消费者B不能再获得相同的消息,但是由于AB是不同的组,所以A和B都会获取相同的消息,这就导致了资源被重复消费。
微服务应用放置到同一个group中,就能够保证消息只会被其中应用消费一次,不同的组是可以消费的,同一个组内会发生竞争关系,只有其中一个可以消费。
同一个应用的不同微服务,只用在配置文件中指定相同的group。


再次发送消息时,只有消费者其中一个能消费。避免了重复消费。
消息持久化
当两个消费者A和B,A设置了group属性值,B没有设置,这时,消费者全部宕机,但是消息生产者一直响MQ中生产消息,这时候重启A和B两者有什么区别呢?
正因为B没有这时分组,B再次启动后不会再向MQ中取数据,而A启动成功后可以正常消费消息队列中的消息。
因此设置了group的消费者,可以保证消息队列中的消息持久化,group对于消费者来讲很重要,既能避免重复消费,又能在消费者重启后依然可以消费消息队列中未消费的消息。
SpringCloud系列之SpringCloud Stream的更多相关文章
- SpringCloud系列:前言
准备写springcloud系列了,先吐槽下自己,之前准备把学到的东西写下来,都因为工作或自己太懒(主要还是懒),写了个开篇就GG了,这次springcloud一定会坚持写完.加油! 这里先说下我搭建 ...
- springCloud系列教程01:Eureka 注册中心集群搭建
springCloud系列教程包含如下内容: springCloud系列教程01:Eureka 注册中心集群搭建 springCloud系列教程02:ConfigServer 配置中心server搭建 ...
- SpringCloud系列——SSO 单点登录
前言 作为分布式项目,单点登录是必不可少的,文本基于之前的的博客(猛戳:SpringCloud系列——Zuul 动态路由,SpringBoot系列——Redis)记录Zuul配合Redis实现一个简单 ...
- SpringCloud系列——Bus 消息总线
前言 SpringCloud Bus使用轻量级消息代理将分布式系统的节点连接起来.然后可以使用此代理广播状态更改(例如配置更改)或其他管理指令.本文结合RabbitMQ+GitHub的Webhook实 ...
- SpringCloud系列——Config 配置中心
前言 Spring Cloud Config为分布式系统中的外部化配置提供了服务器端和客户端支持.有了配置服务器,您就有了一个中心位置来管理跨所有环境的应用程序的外部属性.本文记录实现一个配置中心.客 ...
- SpringCloud系列——Feign 服务调用
前言 前面我们已经实现了服务的注册与发现(请戳:SpringCloud系列——Eureka 服务注册与发现),并且在注册中心注册了一个服务myspringboot,本文记录多个服务之间使用Feign调 ...
- SpringCloud系列——Zuul 动态路由
前言 Zuul 是在Spring Cloud Netflix平台上提供动态路由,监控,弹性,安全等边缘服务的框架,是Netflix基于jvm的路由器和服务器端负载均衡器,相当于是设备和 Netflix ...
- SpringCloud系列——Ribbon 负载均衡
前言 Ribbon是一个客户端负载均衡器,它提供了对HTTP和TCP客户端的行为的大量控制.我们在上篇(猛戳:SpringCloud系列——Feign 服务调用)已经实现了多个服务之间的Feign调用 ...
- SpringCloud系列-整合Hystrix的两种方式
Hystrix [hɪst'rɪks],中文含义是豪猪,因其背上长满棘刺,从而拥有了自我保护的能力.本文所说的Hystrix是Netflix开源的一款容错框架,同样具有自我保护能力. 本文目录 一.H ...
随机推荐
- Codeforces Edu Round 57 A-D
A. Find Divisible 符合条件的区间一定可以选择\(l, l * 2\). 证明\(l * 2 <= r\) 假设存在一组解,\(x, x * d (l <= x <= ...
- Fabric v2.0中的隐私数据
文章来源于https://hyperledger-fabric.readthedocs.io/en/release-2.0/ 私有数据集在v1.4中提出,一直使用的是隐私数据集方式,即建立一个隐私数据 ...
- MVC错误页面相关说明
1.如果使用普通的纯静态页面,在httpErrors中配置的话,返回的status code是200,不会是对应的错误码404.只能使用,aspx或mvc页面,加入,这样就会返回的时候就会显示404错 ...
- 数据结构与算法——图(游戏中的自动寻路-A*算法)
在复杂的 3D 游戏环境中如何能使非玩家控制角色准确实现自动寻路功能成为了 3D 游戏开 发技术中一大研究热点.其中 A*算法得到了大量的运用,A*算法较之传统的路径规划算法,实时性更高.灵活性更强, ...
- Java中CAS原理分析(volatile和synchronized浅析)
CAS是什么? CAS英文解释是比较和交换,是cpu底层的源语,是解决共享变量原子性实现方案,它定义了三个变量,内存地址值对应V,期待值E和要修改的值U,如下图所示,这些变量都是在高速缓存中的,如果两 ...
- MobaXterm无法退格删除
MobaXterm退格删除出现^H,总是要取消输入重新敲语句,很麻烦 解决方法:打开MobaXterm-->settings-->Configuration,把"Backspac ...
- 使用VNC远程linux机器
一,在Windows机器上下载vnc客户端 vnc viewer是一款vnc远程客户端,可以很方便的从Windows机器远程安装了图形界面和vnc服务器的linux机器. 从官网下载https://w ...
- 访问控制列表ACL应用
ACL的应用的场景 应用在三层接口 • Nat地址转换 Nat(network address translation,地址转换)是将数据报报头中的ip地址转换为另一个ip地址的过程,主要用于实现内部 ...
- Unity 黑暗之光 笔记 第一章
第一章 设计游戏开始进入场景 1.设置相机视野同步 选中要调整的相机 GameObject - Align With View(快捷键 Ctrl + Shift + F)
- Elastic Search 学习之路(二)——inverted index(反向索引)
这是篇翻译文,图画的挺有意思. Elastic使用非常特殊的数据结构,称作反向索引.反向索引中,包括了一组document中出现的唯一的单词,和对应的单词,所出现的位置.反向索引是在ES中,docum ...