简介

随着近些年微服务在国内的盛行,消息驱动被提到的越来越多。主要原因是系统被拆分成多个模块后,一个业务往往需要在多个服务间相互调用,不管是采用HTTP还是RPC都是同步的,不可避免快等慢的情况发生,系统性能上很容易遇到瓶颈。在这样的背景下,将业务中实时性要求不是特别高且非主干的部分放到消息队列中是很好的选择,达到了异步解耦的效果。

目前消息队列有很多优秀的中间件,目前使用较多的主要有 RabbitMQ,Kafka,RocketMQ 等,这些中间件各有优势,有的对 AMQP(应用层标准高级消息队列协议)支持完善,有的提供了更高的可靠性,有的对大数据支持良好,同时各种消息中间件概念不统一,使得选择和使用一款合适的消息中间件成为难题。Spring跳出来给出了解决方案:Spring Cloud Stream,使用它可以很方便高效的操作消息中间件,程序员只要关心业务代码即可,目前官方支持 RabbitMQ,Kafka两大主流MQ,RocketMQ 则自己提供了相应支持。

首先看一下Spring Cloud Stream做了什么,如下图所示,框架目前官方把消息中间件抽象成了 Binder,业务代码通过进出管道连接 Binder,各消息中间件的差异性统一交给了框架处理,程序员只需要了解框架的抽象出来的一些统一概念即可

  • Binder(绑定器):RabbitMQ,Kafka等中间件服务的封装
  • Channel(管道):也就是图中的 inputs 和 outputs 所指区域,是应用程序和 Binder 的桥梁
  • Gourp(消费组):由于微服务会部署多实例,为了保证只被服务的一个实例消费,可以通过配置,把实例都绑到同一个消费组
  • Partitioning (消息分区):如果某一类消息只想指定给服务的固定实例消费,可以使用分区实现

Spring Cloud Stream将业务代码和消息中间件解耦,带来的好处可以从下图很直观的感受到,很简洁的代码,我们便能从RabbitMQ中接受消息然后经过业务处理再向Kafka发送一条消息,只需要更改相关配置就能快速改变系统行为。

细心的读者可能会好奇,上图的代码只是注入了一个简单的 Function 而已,实际上,Spring Cloud Stream3.0后集成了Spring Cloud Function框架 ,提倡函数式的风格,弃用先前版本基于注解的开发方式。Spring Cloud Function是 Serverless 和 Faas 的产物,强调面向函数编程,一份代码各云平台运行,和Spring Cloud Stream一样也是解决了基础设施的差异性问题,通过强大的自动装配机制,可以根据配置自动暴露 HTTP 服务或者消息服务,并且同时支持命令式和响应式编程模式,可以说是很强大了。下面通过一个简单的例子来理解下上图的代码和框架的使用把。

简单案例

模拟一个简单的下单,收到订单之后处理完,返回成功,然后发送消息给库存模块,库存模块再发送消息给报表模块

项目地址

springcloud-stream

项目结构

项目依赖

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

表单

@Data
public class OrderForm {
private String productName;
}

消息管道注册

@Configuration
@Slf4j
public class MessageQueueConfig { @Bean
public Function<OrderForm, OrderForm> inventory() {
return orderForm -> {
log.info("Inventory Received Message: " + orderForm);
return orderForm;
};
} @Bean
public Consumer<OrderForm> report() {
return orderForm -> {
log.info("Report Received Message: " + orderForm);
};
}
}

Controller

@Slf4j
@RestController
public class OrderController { @Autowired
private BeanFactoryChannelResolver resolver; @PostMapping("order")
public String order(@RequestBody OrderForm orderForm) {
log.info("Received Request " + orderForm);
resolver.resolveDestination("inventory-in-0").send(new GenericMessage<>(orderForm));
return "success";
}
}

配置

框架会按照中间件默认端口去连接,这里自定义了一个名为myLocalRabbit的类型是RabbitMQ的Binder配置,bindings下面 inventory-in-0 是通道名,接受inventory主题(对应RabbitMQ的ExChange)的消息,然后处理完通过 inventory-out-0 通道发送消息到 report 主题, report-in-0通道负责接受report主题的消息。

注:通道名=注册的 function 方法名 + in或者out + 参数位置(详见注释)

spring:
cloud:
stream:
# 配置消息中间件信息
binders:
myLocalRabbit:
type: rabbit
environment:
spring:
rabbitmq:
host: localhost
port: 31003
username: guest
password: guest
virtual-host: /
# 重点,如何绑定通道,这里有个约定,开头是函数名,in表示消费消息,out表示生产消息,最后的数字是函数接受的参数的位置,destination后面为订阅的主题
# 比如Function<Tuple2<Flux<String>, Flux<Integer>>, Flux<String>> gather()
# gather函数接受的第一个String参数对应 gather-in-0,第二个Integer参数对应 gather-in-1,输出对应 gather-out-0
bindings:
inventory-in-0:
destination: inventory
inventory-out-0:
destination: report
report-in-0:
destination: report
# 注册声明的三个函数
function:
definition: inventory;report

测试

POST http://localhost:8080/order
Content-Type: application/json {
"productName": "999"
}

结果

POST http://localhost:8080/order

HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8
Content-Length: 7
Date: Sat, 30 May 2020 15:27:56 GMT
Keep-Alive: timeout=60
Connection: keep-alive success Response code: 200; Time: 56ms; Content length: 7 bytes

后台日志

可以看到消息成功发送到了库存和报表服务

2020-05-30 23:27:56.956  INFO 8760 --- [nio-8080-exec-1] c.e.springcloudstream.OrderController    : Received Request OrderForm(productName=999)
2020-05-30 23:27:56.956 INFO 8760 --- [nio-8080-exec-1] o.s.i.h.s.MessagingMethodInvokerHelper : Overriding default instance of MessageHandlerMethodFactory with provided one.
2020-05-30 23:27:56.957 INFO 8760 --- [nio-8080-exec-1] c.e.s.MessageQueueConfig : Inventory Received Message: OrderForm(productName=999)
2020-05-30 23:27:56.958 INFO 8760 --- [nio-8080-exec-1] o.s.a.r.c.CachingConnectionFactory : Attempting to connect to: [localhost:31003]
2020-05-30 23:27:56.964 INFO 8760 --- [nio-8080-exec-1] o.s.a.r.c.CachingConnectionFactory : Created new connection: rabbitConnectionFactory.publisher#6131841e:0/SimpleConnection@192fe472 [delegate=amqp://guest@127.0.0.1:31003/, localPort= 2672]
2020-05-30 23:27:56.965 INFO 8760 --- [nio-8080-exec-1] o.s.amqp.rabbit.core.RabbitAdmin : Auto-declaring a non-durable, auto-delete, or exclusive Queue (inventory.anonymous.wtaFwHlNRkql5IUh2JCNAA) durable:false, auto-delete:true, exclusive:true. It will be redeclared if the broker stops and is restarted while the connection factory is alive, but all messages will be lost.
2020-05-30 23:27:56.965 INFO 8760 --- [nio-8080-exec-1] o.s.amqp.rabbit.core.RabbitAdmin : Auto-declaring a non-durable, auto-delete, or exclusive Queue (report.anonymous.SJgpJKiJQf2tudszgf623w) durable:false, auto-delete:true, exclusive:true. It will be redeclared if the broker stops and is restarted while the connection factory is alive, but all messages will be lost.
2020-05-30 23:27:56.979 INFO 8760 --- [f2tudszgf623w-1] o.s.i.h.s.MessagingMethodInvokerHelper : Overriding default instance of MessageHandlerMethodFactory with provided one.
2020-05-30 23:27:56.980 INFO 8760 --- [f2tudszgf623w-1] c.e.s.MessageQueueConfig : Report Received Message: OrderForm(productName=999)

Spring Cloud Stream微服务消息框架的更多相关文章

  1. spring Boot+spring Cloud实现微服务详细教程第二篇

    上一篇文章已经说明了一下,关于spring boot创建maven项目的简单步骤,相信很多熟悉Maven+Eclipse作为开发常用工具的朋友们都一目了然,这篇文章主要讲解一下,构建spring bo ...

  2. 【微服务】使用spring cloud搭建微服务框架,整理学习资料

    写在前面 使用spring cloud搭建微服务框架,是我最近最主要的工作之一,一开始我使用bubbo加zookeeper制作了一个基于dubbo的微服务框架,然后被架构师否了,架构师曰:此物过时.随 ...

  3. Spring Cloud构建微服务架构(三)消息总线

     注:此文不适合0基础学习者直接阅读,请先完整的将作者关于微服务的博文全部阅读一遍,如果还有疑问,可以再来阅读此文,地址:http://blog.csdn.net/sosfnima/article/d ...

  4. 干货|基于 Spring Cloud 的微服务落地

    转自 微服务架构模式的核心在于如何识别服务的边界,设计出合理的微服务.但如果要将微服务架构运用到生产项目上,并且能够发挥该架构模式的重要作用,则需要微服务框架的支持. 在Java生态圈,目前使用较多的 ...

  5. 基于Spring Cloud的微服务落地

    微服务架构模式的核心在于如何识别服务的边界,设计出合理的微服务.但如果要将微服务架构运用到生产项目上,并且能够发挥该架构模式的重要作用,则需要微服务框架的支持. 在Java生态圈,目前使用较多的微服务 ...

  6. Spring Cloud与微服务构建:Spring Cloud简介

    Spring Cloud简介 微服务因该具备的功能 微服务可以拆分为"微"和"服务"二字."微"即小的意思,那到底多小才算"微&q ...

  7. 基于Spring Boot和Spring Cloud实现微服务架构学习

    转载自:http://blog.csdn.net/enweitech/article/details/52582918 看了几周Spring相关框架的书籍和官方demo,是时候开始总结下这中间的学习感 ...

  8. 基于Spring Boot和Spring Cloud实现微服务架构学习--转

    原文地址:http://blog.csdn.net/enweitech/article/details/52582918 看了几周spring相关框架的书籍和官方demo,是时候开始总结下这中间的学习 ...

  9. 基于Spring Boot和Spring Cloud实现微服务架构

    官网的技术导读真的描述的很详细,虽然对于我们看英文很费劲,但如果英文不是很差,请选择沉下心去读,你一定能收获好多.我的学习是先从Spring boot开始的,然后接触到微服务架构,当然,这一切最大的启 ...

随机推荐

  1. Jmeter简单压测之服务器监控

    此篇为最近工作需要到内容,故现在做一个总结. 最近家里电脑坏了,等待会公司空闲在编写. 文章构思中,敬请期待.......

  2. LeetCode 56,57,60,连刷三题不费劲

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是LeetCode专题的第34篇文章,刚好接下来的题目比较简单,很多和之前的做法类似.所以我们今天出一个合集,一口气做完接下来的57.5 ...

  3. zabbix分布式安装全过程

    项目规划 软件 版本 IP zabbix-server 3.4.15 10.1.10.128 zabbix-proxy 3.4.15 10.1.10.129 zabbix-agent 3.4.15 1 ...

  4. D. Distance in Tree(树型Dp计数)

    \(其实思路都能想到一点,就是去重这里特别麻烦,没有好的思路.\) \(设dp[i][j]为以i为根深度为j的节点数量\) \(dp[parent][j]=\sum{dp[son][j-1]}\) \ ...

  5. HMM-前向后向算法(附python实现)

    基本要素 状态 \(N\)个 状态序列 \(S = s_1,s_2,...\) 观测序列 \(O=O_1,O_2,...\) \(\lambda(A,B,\pi)\) 状态转移概率 \(A = \{a ...

  6. elasticsearch kibana的安装部署与简单使用(二)

    介绍一下elasticsearch和kibana的简单使用 es其实我理解为一个数据库,一个数据库无非就是增删改查, Delete  PUT GET POST 这些接口关键字完美对应 比如,我想查一张 ...

  7. [hdu1023]递推

    http://acm.hdu.edu.cn/showproblem.php?pid=1023 如果把栈里面的元素个数表示成状态,每一步(共2 * n步)的状态构成的状态序列的种数就是答案,令dp[i] ...

  8. about VennsBlog.

    此博客主要将用于记录自己学习路上的一些点滴及心得 同时,也希望各位提出指正 相互交流,共同进步 感谢相遇

  9. 黑马程序员_毕向东_Java基础视频教程——switch语句练习(随笔)

    switch(练习) /* if和 switch 语句很像. 具体什么场景下使用什么语句呢? 如果判断的具体数值不多且符合byte.short.int.char.String类型,虽然两个语句都可以使 ...

  10. CentOS 7.1 图形化安装

    1.在命令行下输入下面的命令来安装 Gnome 包 sudo  yum groupinstall "GNOME Desktop" "Graphical Administr ...