相关源码: spring cloud demo

微服务的目的: 松耦合

事件驱动的优势:高度解耦

Spring Cloud Stream 的几个概念

Spring Cloud Stream is a framework for building message-driven microservice applications.

官方定义 Spring Cloud Stream 是一个构建消息驱动微服务的框架。

Spring Cloud Stream Application

应用程序通过 inputs 或者 outputs 来与 Spring Cloud Stream 中binder 交互,通过我们配置来 binding ,而 Spring Cloud Stream 的 binder 负责与中间件交互。所以,我们只需要搞清楚如何与 Spring Cloud Stream 交互就可以方便使用消息驱动的方式

Binder

Binder 是 Spring Cloud Stream 的一个抽象概念,是应用与消息中间件之间的粘合剂。目前 Spring Cloud Stream 实现了 Kafka 和 Rabbit MQ 的binder。

通过 binder ,可以很方便的连接中间件,可以动态的改变消息的
destinations(对应于 Kafka 的topic,Rabbit MQ 的 exchanges),这些都可以通过外部配置项来做到。

甚至可以任意的改变中间件的类型而不需要修改一行代码。

Publish-Subscribe

消息的发布(Publish)和订阅(Subscribe)是事件驱动的经典模式。Spring Cloud Stream 的数据交互也是基于这个思想。生产者把消息通过某个 topic 广播出去(Spring Cloud Stream 中的 destinations)。其他的微服务,通过订阅特定 topic 来获取广播出来的消息来触发业务的进行。

这种模式,极大的降低了生产者与消费者之间的耦合。即使有新的应用的引入,也不需要破坏当前系统的整体结构。

Consumer Groups

“Group”,如果使用过 Kafka 的童鞋并不会陌生。Spring Cloud Stream 的这个分组概念的意思基本和 Kafka 一致。

微服务中动态的缩放同一个应用的数量以此来达到更高的处理能力是非常必须的。对于这种情况,同一个事件防止被重复消费,只要把这些应用放置于同一个 “group” 中,就能够保证消息只会被其中一个应用消费一次。

Durability

消息事件的持久化是必不可少的。Spring Cloud Stream 可以动态的选择一个消息队列是持久化,还是 present。

Bindings

bindings 是我们通过配置把应用和spring cloud stream 的 binder 绑定在一起,之后我们只需要修改 binding 的配置来达到动态修改topic、exchange、type等一系列信息而不需要修改一行代码。

基于 RabbitMQ 使用

以下内容源码: spring cloud demo

消息接收

Spring Cloud Stream 基本用法,需要定义一个接口,如下是内置的一个接口。

public interface Sink {
String INPUT = "input"; @Input("input")
SubscribableChannel input();
}

注释 @Input 对应的方法,需要返回 SubscribableChannel ,并且参入一个参数值。

这就接口声明了一个 binding 命名为 “input” 。

其他内容通过配置指定:

spring:
cloud:
stream:
bindings:
input:
destination: mqTestDefault

destination:指定了消息获取的目的地,对应于MQ就是 exchange,这里的exchange就是 mqTestDefault

@SpringBootApplication
@EnableBinding(Sink.class)
public class Application { // 监听 binding 为 Sink.INPUT 的消息
@StreamListener(Sink.INPUT)
public void input(Message<String> message) {
System.out.println("一般监听收到:" + message.getPayload());
} public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}

定义一个 class (这里直接在启动类),并且添加注解@EnableBinding(Sink.class) ,其中 Sink 就是上述的接口。同时定义一个方法(此处是 input)标明注解为@StreamListener(Processor.INPUT) ,方法参数为 Message 。

启动后,默认是会创建一个临时队列,临时队列绑定的exchange为 “mqTestDefault”,routing key为 “#”。

所有发送 exchange 为“mqTestDefault” 的MQ消息都会被投递到这个临时队列,并且触发上述的方法。

以上代码就完成了最基本的消费者部分。

消息发送

消息的发送同消息的接受,都需要定义一个接口,不同的是接口方法的返回对象是MessageChannel,下面是 Spring Cloud Stream 内置的接口:

public interface Source {
String OUTPUT = "output"; @Output("output")
MessageChannel output();
}

这就接口声明了一个 binding 命名为 “output” ,不同于上述的 “input”,这个binding 声明了一个消息输出流,也就是消息的生产者。

spring:
cloud:
stream:
bindings:
output:
destination: mqTestDefault
contentType: text/plain

contentType:用于指定消息的类型。具体可以参考 spring cloud stream docs

destination:指定了消息发送的目的地,对应 RabbitMQ,会发送到 exchange 是 mqTestDefault 的所有消息队列中。

代码中调用:

@SpringBootApplication
@EnableBinding(Source.class)
public class Application implements CommandLineRunner { @Autowired
@Qualifier("output")
MessageChannel output; @Override
public void run(String... strings) throws Exception {
// 字符串类型发送MQ
System.out.println("字符串信息发送");
output.send(MessageBuilder.withPayload("大家好").build());
} public static void main(String[] args) {
SpringApplication.run(Application.class);
} }

通过注入MessageChannel的方式,发送消息。

通过注入Source 接口的方式,发送消息。 具体可以查看样例

以上代码就完成了最基本的生产者部分。

自定义消息发送接收

自定义接口

Spring Cloud Stream 内置了两种接口,分别定义了 binding 为 “input” 的输入流,和 “output” 的输出流,而在我们实际使用中,往往是需要定义各种输入输出流。使用方法也很简单。

interface OrderProcessor {

    String INPUT_ORDER = "inputOrder";
String OUTPUT_ORDER = "outputOrder"; @Input(INPUT_ORDER)
SubscribableChannel inputOrder(); @Output(OUTPUT_ORDER)
MessageChannel outputOrder();
}

一个接口中,可以定义无数个输入输出流,可以根据实际业务情况划分。上述的接口,定义了一个订单输入,和订单输出两个 binding。

使用时,需要在 @EnableBinding 注解中,添加自定义的接口。
使用 @StreamListener 做监听的时候,需要指定OrderProcessor.INPUT_ORDER

spring:
cloud:
stream:
defaultBinder: defaultRabbit
bindings:
inputOrder:
destination: mqTestOrder
outputOrder:
destination: mqTestOrder

如上配置,指定了 destination 为 mqTestOrder 的输入输出流。

分组与持久化

上述自定义的接口配置中,Spring Cloud Stream 会在 RabbitMQ 中创建一个临时的队列,程序关闭,对应的连接关闭的时候,该队列也会消失。而在实际使用中,我们需要一个持久化的队列,并且指定一个分组,用于保证应用服务的缩放。

只需要在消费者端的 binding 添加配置项 spring.cloud.stream.bindings.[channelName].group = XXX 。对应的队列就是持久化,并且名称为:mqTestOrder.XXX。

rabbitMQ routing key 绑定

用惯了 rabbitMQ 的童鞋,在使用的时候,发现 Spring Cloud Stream 的消息投递,默认是根据 destination + group 进行区分,所有的消息都投递到 routing key 为 “#‘’ 的消息队列里。

如果我们需要进一步根据 routing key 来进行区分消息投递的目的地,或者消息接受,需要进一步配,Spring Cloud Stream 也提供了相关配置:

spring:
cloud:
stream:
bindings:
inputProductAdd:
destination: mqTestProduct
group: addProductHandler # 拥有 group 默认会持久化队列
outputProductAdd:
destination: mqTestProduct
rabbit:
bindings:
inputProductAdd:
consumer:
bindingRoutingKey: addProduct.* # 用来绑定消费者的 routing key
outputProductAdd:
producer:
routing-key-expression: '''addProduct.*''' # 需要用这个来指定 RoutingKey

spring.cloud.stream.rabbit.bindings.[channelName].consumer.bindingRoutingKey
指定了生成的消息队列的routing key

spring.cloud.stream.rabbit.bindings.[channelName].producer.routing-key-expression 指定了生产者消息投递的routing key

DLX 队列

DLX 作用

DLX:Dead-Letter-Exchange(死信队列)。利用DLX, 当消息在一个队列中变成死信(dead message)之后,它能被重新publish到另一个Exchange,这个Exchange就是DLX。消息变成死信一向有一下几种情况:

消息被拒绝(basic.reject/ basic.nack)并且requeue=false
消息TTL过期(参考:RabbitMQ之TTL(Time-To-Live 过期时间)
队列达到最大长度

DLX也是一个正常的Exchange,和一般的Exchange没有区别,它能在任何的队列上被指定,实际上就是设置某个队列的属性,当这个队列中有死信时,RabbitMQ就会自动的将这个消息重新发布到设置的Exchange上去,进而被路由到另一个队列,可以监听这个队列中消息做相应的处理。

Spring Cloud Stream 中使用

spring.cloud.stream.rabbit.bindings.[channelName].consumer.autoBindDlq=true

spring.cloud.stream.rabbit.bindings.[channelName].consumer.republishToDlq=true

配置说明,可以参考 spring cloud stream rabbitmq consumer properties

结论

Spring Cloud Stream 最大的方便之处,莫过于抽象了事件驱动的一些概念,对于消息中间件的进一步封装,可以做到代码层面对中间件的无感知,甚至于动态的切换中间件,切换topic。使得微服务开发的高度解耦,服务可以关注更多自己的业务流程。

相关文档

spring cloud stream 文档

spring cloud stream 项目

spring cloud stream 样例

使用 Spring Cloud Stream 构建消息驱动微服务的更多相关文章

  1. Spring Cloud Alibaba学习笔记(12) - 使用Spring Cloud Stream 构建消息驱动微服务

    什么是Spring Cloud Stream 一个用于构建消息驱动的微服务的框架 应用程序通过 inputs 或者 outputs 来与 Spring Cloud Stream 中binder 交互, ...

  2. 「 从0到1学习微服务SpringCloud 」08 构建消息驱动微服务的框架 Spring Cloud Stream

    系列文章(更新ing): 「 从0到1学习微服务SpringCloud 」01 一起来学呀! 「 从0到1学习微服务SpringCloud 」02 Eureka服务注册与发现 「 从0到1学习微服务S ...

  3. SpringCloud之Spring Cloud Stream:消息驱动

    Spring Cloud Stream 是一个构建消息驱动微服务的框架,该框架在Spring Boot的基础上整合了Spring Integrationg来连接消息代理中间件(RabbitMQ, Ka ...

  4. 消息驱动微服务:Spring Cloud Stream

    最近在学习Spring Cloud的知识,现将消息驱动微服务:Spring Cloud Stream 的相关知识笔记整理如下.[采用 oneNote格式排版]

  5. 今天介绍一下自己的开源项目,一款以spring cloud alibaba为核心的微服务架构项目,为给企业与个人提供一个零开发基础的微服务架构。

    LaoCat-Spring-Cloud-Scaffold 一款以spring cloud alibab 为核心的微服务框架,主要目标为了提升自己的相关技术,也为了给企业与个人提供一个零开发基础的微服务 ...

  6. Spring Cloud Stream如何处理消息重复消费?

    最近收到好几个类似的问题:使用Spring Cloud Stream操作RabbitMQ或Kafka的时候,出现消息重复消费的问题.通过沟通与排查下来主要还是用户对消费组的认识不够.其实,在之前的博文 ...

  7. Spring cloud stream【消息分区】

      在上篇文章中我们给大家介绍了Stream的消息分组,可以实现消息的重复消费的问题,但在某些场景下分组还不能满足我们的需求,比如,同时有多条同一个用户的数据,发送过来,我们需要根据用户统计,但是消息 ...

  8. Spring cloud stream【消息分组】

      上篇文章我们简单的介绍了stream的使用,发现使用还是蛮方便的,但是在上个案例中,如果有多个消息接收者,那么消息生产者发送的消息会被多个消费者都接收到,这种情况在某些实际场景下是有很大问题的,比 ...

  9. spring cloud 入门,看一个微服务框架的「五脏六腑」

    Spring Cloud 是一个基于 Spring Boot 实现的微服务框架,它包含了实现微服务架构所需的各种组件. 注:Spring Boot 简单理解就是简化 Spring 项目的搭建.配置.组 ...

随机推荐

  1. [转帖]Stack的三种含义

    Stack的三种含义 http://www.ruanyifeng.com/blog/2013/11/stack.html 学习编程的时候,经常会看到stack这个词,它的中文名字叫做"栈&q ...

  2. druid 连接Oracle时出现的错误

    转博主https://blog.csdn.net/jiangyu1013/article/details/70237550#commentsedit mysql 更新 SQL 语句 无错误 批量 报错 ...

  3. java学习之—递归

    /** * 递归 * Create by Administrator * 2018/6/20 0020 * 上午 9:41 **/ public class TriangleApp { static ...

  4. scrapy暂停和重启,及url去重原理,telenet简单使用

    一.scrapy暂停与重启 1.要暂停,就要保留一些中间信息,以便重启读取中间信息并从当前位置继续爬取,则需要一个目录存放中间信息: scrapy crawl spider_name -s JOBDI ...

  5. DAY02、正式介绍python

    一.编程语言介绍(***) 1.1.机器语言:直接用计算机能理解的二进制指令编写程序,直接控制硬件 1.2.汇编语言:用英文标签取代二进制指令编写程序,本质也是直接控制硬件 1.3.高级语言:用人类能 ...

  6. SECCON 2014 CTF:Shuffle

    很简单的一道小题 dia看一下是ELF文件 运行之: St0CFC}4cNOeE1WOS !eoCE{ CC T2hNto 是一串乱七八糟的字符 ida看一下: 很简单的逻辑 v5和v6是随机生成的两 ...

  7. centos7优化启动项,关闭一些不必要开启的服务

    CentOS7已不再使用chkconfig 管理启动项 使用 systemctl list-unit-files 可以查看启动项 systemctl list-unit-files | grep en ...

  8. 洛谷 P1126 机器人搬重物

    题目描述 机器人移动学会(RMI)现在正尝试用机器人搬运物品.机器人的形状是一个直径 $1.6 米的球.在试验阶段,机器人被用于在一个储藏室中搬运货物.储藏室是一个 N×MN \times MN×M ...

  9. Centos虚拟环境工具virtualenvwrapper

    下载安装virtualenvwrapper pip3 install virtualenvwrapper !!!!注意安装virtualenvwrapper必须是在本地环境下!!! 设置Linux的用 ...

  10. Mysql 计划任务

    -- 设置 show variables like '%sche%'; ; -- Start存储过程 drop PROCEDURE if exists test; CREATE PROCEDURE t ...