简介

随着近些年微服务在国内的盛行,消息驱动被提到的越来越多。主要原因是系统被拆分成多个模块后,一个业务往往需要在多个服务间相互调用,不管是采用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. 【Redis】跳跃表原理分析与基本代码实现(java)

    最近开始看Redis设计原理,碰到一个从未遇见的数据结构:跳跃表(skiplist).于是花时间学习了跳表的原理,并用java对其实现. 主要参考以下两本书: <Redis设计与实现>跳表 ...

  2. spring学习笔记(六)自定义spring-boot-starter(2)

    上篇文章我们主要讲了spring自动装配的原理,我们知道了springboot在启动的时候会自动去读.factories文件,在factories文件中,autoConfiguration对应的就是我 ...

  3. matlab 调用C程序进行simulink仿真

    文章目录 simulink仿真 创建C程序 编译C程序 运行结果 simulink仿真 simulink仿真中需要使用S-Function模块,可以实现调用C程序进行仿真,下面先建立一个简单的仿真: ...

  4. Android如何设置只有边框背景透明的背景呢?

    很简单,只需要新建一个 drawable 文件 <?xml version="1.0" encoding="utf-8"?> <shape x ...

  5. abp web.mvc项目中的菜单加载机制

    abp中的菜单加载机制 在abp中菜单的定义与我们传统写的框架不一样,它是在编写代码的时候配置,而我们一般写的通用权限管理系统中,是后期在后台界面中添加的.这一点有很大不同.abp关于菜单的定义及管理 ...

  6. Git上传文件、文件夹到github

    上传一个文件: $ git add test.txt 上传多个文件: $ git add test.txt demo.txt 同理: 上传一个文件夹: $ git add learngit 上传多个文 ...

  7. js判断数组(数组对象)中是否存在指定的值,如果存在就删除

    数组中是否存在指定值,存在就删除 var str = ["a", "b", "c"]; var index = str.indexOf(&q ...

  8. void 型指针的高阶用法,你掌握了吗?

    [导读] 要比较灵活的使用C语言实现一些高层级的框架时,需要掌握一些进阶编程技巧,这篇来谈谈void指针的一些妙用.测试环境采用 IAR for ARM 8.40.1 什么是void指针 void指针 ...

  9. 前端面试题-http和https区别

    说一下http和https https的SSL加密是在传输层实现的. (1)http和https的基本概念 http: 超文本传输协议,是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求 ...

  10. 这是一篇致力于解决Linux小白无法安装tunctl工具的文章

    计算机网络——搭建Linux下的实验环境并成功安装tunctl 各位如果是来解决安装tunctl的问题的,请直接到2.2部分的内容. 这个学期终于迎来了计算机网络这门课程,也终于能够进一步了解让我无数 ...