微服务之Springcloud 从零基础到入门——Stream篇

微服务之Springcloud 从零基础到入门——Stream篇

一. 背景简介

在微服务的开发过程中,服务与服务之间通信经常会使用到消息中间件,而如果确定使用了某个消息中间件比如RabbitMQ,那么该中间件和系统的耦合性就会十分的高,假如现在要把RabbitMQ替换为Kafka,那么产生的变动会特别的大,许多地方都需要进行配置。这时我们可以使用Spring Cloud Stream来整合我们的消息中间件,来降低系统和中间件的耦合性。作用就是屏蔽底层的消息中间件的差异,降低切换成本,统一消息的编程模型。对于消息系统,实际上最初的 SUN 公司是非常看中的,所以在 EJB 的时代里面专门提供有消息驱动 Bean(Message Driven Bean、MDB)利用消息驱动 Bean 可以进行消息的处理操作。利用消息驱动 bean 的模式可以简化用户的操作复杂度,直接传递一些各类的数据即可实现业务的处理操作。SpringCloudStream 就是实现了 MDB 功能。

二. Stream简介

应用程序通过 inputs 或者 outputs 来与 Spring Cloud Stream 中binder 交互,通过我们配置来 binding(绑定) ,而 Spring Cloud Stream 的 binder 负责与消息中间件交互。input对应于消息消费者,output对应于消息生产者。所以,我们只需要搞清楚如何与 Spring Cloud Stream 交互就可以方便使用消息驱动的方式。
在这里插入图片描述

Stream通过使用Spring Integration来连接消息代理中间件以实现消息事件驱动。Spring Cloud Stream 为一些供应商的消息中间件产品提供了个性化的自动化配置实现,引用了发布-订阅、消费组、分区的三个核心概念。目前仅支持RabbitMQ、Kafka。

组成说明
Middleware中间件,目前只支持RabbitMQ和Kafka
BinderBinder是应用与消息中间件之间的封装,目前实行了Kafka和RabbitMQ的Binder,通过Binder可以很方便的连接中间件,可以动态的改变消息类型(对应于Kafka的topic,RabbitMQ的exchange),这些都可以通过配置文件来实现
@Input注解标识输入通道,通过该输入通道接收到的消息进入应用程序
@Output注解标识输出通道,发布的消息将通过该通道离开应用程序
@StreamListener监听队列,用于消费者的队列的消息接收
@EnableBinding指信道channel和exchange绑定在一起
三. Stream消息驱动案例
  • 创建消息生产者
  1. 引入依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<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>
  1. 修改application.yml配置
server:
  port: 8506

spring:
  application:
    name: cloud-stream-provider
  cloud:
    stream:
      binders:
        defaultRabbit:  # 定义的绑定对象
          type: rabbit   # 绑定对象rabbitmq的类型
          environment:  # 绑定对象rabbitmq的环境配置
            spring:
              rabbitmq:
                host: 192.168.192.129
                port: 5672
                username: root
                password: root
      bindings:
        output:
          destination: MyExchange   # 约定的通道名称
          contentType: application/json  # 设置消息的类型为json,也可设置为文本类型
          binder: defaultRabbit    # 引用定义好的绑定对象
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
  instance:
    lease-renewal-interval-in-seconds: 2  # 设置心跳的间隔时间
    lease-expiration-duration-in-seconds: 5
    instance-id: 8506.com
    prefer-ip-address: true
  1. 定义消息发送接口
public interface SendService {
    String send();
}
  1. 定义消息发送实现类
@EnableBinding(Source.class)     //定义消息推送管道
public class SendServiceImpl implements SendService {
    @Resource
    private MessageChannel output;  //消息发送通道
    @Override
    public String send() {
        String uuid= UUID.randomUUID().toString();
        //发送消息
        output.send(MessageBuilder.withPayload(uuid).build());
        String msg="uuid="+uuid;
        return msg;
    }
}
  1. 定义测试消息发送的controller类
@RestController
public class SendController {

    @Resource
    private SendService sendService;
    
    @GetMapping(value = "send")
    public String send(){
        return sendService.send();
    }
}
  1. 定义主启动类
@SpringBootApplication
public class Stream8506Application {
    public static void main(String[] args) {
        SpringApplication.run(Stream8506Application.class,args);
    }
}
  1. 测试
    在浏览器输入http://localhost:8506/send,可以看到在控制台输出的发送消息,并且可以在RabbitMQ的管理界面看到Exhange中的Mychange。
    在这里插入图片描述
  • 创建消息消费者
  1. 引入依赖
    此处依赖同消息生产者
  2. 修改application.yml配置文件
server:
  port: 8606

spring:
  application:
    name: cloud-stream-consumer
  cloud:
    stream:
      binders:
        defaultRabbit:  # 定义的绑定对象
          type: rabbit   # 绑定对象的类型
          environment:  # 绑定对象的环境配置
            spring:
              rabbitmq:
                host: 192.168.192.129
                port: 5672
                username: root
                password: root
      bindings:
        input:
          destination: MyExchange   # 表示要使用的通道名称
          contentType: application/json  # 设置消息的类型为json,也可设置为文本类型
          binder: defaultRabbit    # 引用定义好的绑定对象
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
  instance:
    lease-renewal-interval-in-seconds: 2  # 设置心跳的间隔时间
    lease-expiration-duration-in-seconds: 5
    instance-id: 8606.com
    prefer-ip-address: true
  1. 定义消息的监听程序类
@EnableBinding(Sink.class)
@Component
public class ReceiveController {

    @Value("${server.port}")
    private String port;

    @StreamListener(Sink.INPUT)
    public void get(Message<String> message){
        System.out.println("消费者:"+message.getPayload()+",port="+port);
    }
}
  1. 主启动类
@SpringBootApplication
public class Stream8606Application {
    public static void main(String[] args) {
        SpringApplication.run(Stream8606Application.class,args);
    }
}
  1. 测试
    浏览器输入http://localhost:8506/send,消费者的控制台输出发送的消息
四. 分组消费
  1. 重复消费问题的产生
    若一个通道的消费者有两个的话,那么这两个消费者会获取到同一个消息并将其消费,那么就会产生问题。若在实际的微服务调用中,若发送的消息为订单的消息,并且被两个服务消费掉,那么会使得一个订单被消费两次,与实际情况不符。
  2. 解决方案
    分组消费,在stream中,在同一个组之下的消费者是竞争关系,消息只会被消费一次。那么可以将消费特定消息的消费者放到同一个组下,这样可以保证消息只会被其中的一个服务消费,与该消费者同在mygroup下的消费者会采用轮询的机制去消费消息。在消息的消费者中添加组的配置如下:
spring:
  cloud:
    stream:
      bindings:
        input:
          destination: MyExchange
          contentType: application/json  
          binder: defaultRabbit   
          group: mygroup     # 配置分组防止重复消费
五. 消息持久化
  1. 持久化的原因
    如果没有消息持久化,万一消息的消费方发生了宕机事件,那么当消费者再次启动的后也不能获取得到消息中间件得消息,造成了消息丢失。
  2. 如何做消息持久化
    消费者配置了分组就默认配置了消息持久化,即便消费者宕机,那么再次重启后还能消费消息中间件里边的消息。
六. 下一篇介绍

本文简要介绍了Stream的基本用法,接下来将对spring cloud alibaba的技术进行介绍。在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值