微服务之消息驱动Stream和请求链路跟踪Sleuth

B站尚硅谷P83~P94
代码Gitee地址
以上次博客的eureka集群作为注册中心来演示。

1. Stream

是一个构建消息驱动微服务的框架。应用程序通过inputs或者outputs来与Spring Cloud Stream 中binder对象交互;我们通过配置来绑定,而binder对象负责与消息中间件交互。我们只需要搞清楚如何与Spring Cloud Stream交互就可以方便的使用消息驱动的方式。
通过Spring Intergration来连接消息代理中间件以实现消息事件驱动。
为一些供应商产品提供了个性化的自动化配置实现,引用了发布-订阅、消费组、分区的三个核心概念。
支持RabbitMQKafka
总之它的作用是屏蔽底层消息中间件的差异,降低切换成本,统一消息的编程模型。

  • 截止到2022.6.14,仍然只支持RabbitMQ和Kafka

在这里插入图片描述

1.1 设计思想

  • 标准化的消息队列,生产者和消费者之间必须走特定的信道来传递信息,信道里面的消息只有被订阅了之后才会有消息处理器来接收。
    在这里插入图片描述

  • Stream设计方案:通过定义绑定器作为中间件,完美地实现了应用程序与消息中间件之间的隔离,通过向应用程序暴露统一的信道。使得应用程序不需要再考虑各种不同的消息中间件实现。在这里插入图片描述

    在这里插入图片描述

  • Binder

    1. input:消费者;
    2. output:生产者。

1.2 标准流程

在这里插入图片描述

  • Binder:连接中间件,屏蔽差异;
  • Channel:消息通讯系统中实现存储和转发的媒介,通过Channel对队列进行配置;
  • Soure/Sink:参照stream自身,发出就是输出source,接收就是输入sink

1.3 常用注解

  1. @Input:输入信道,通过输入信道进入应用程序;
  2. @Output:输出信道,通过输出信道离开应用程序。
  3. @StreamLinster:监听队列,消费者的队列和消息接收;
  4. @EnableBinding:指Channel和Exchange绑定在疫情。

1.4 生产者搭建

  • 新建module:cloud-stream-rabbitmq-provider8801

  • 依赖:

        <dependencies>
    
            <dependency>
                <groupId>org.example</groupId>
                <artifactId>cloud-api-commons</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
    
            <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>
    
            <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>
    
    
    
  • 配置文件:

    server:
      port: 8801
    
    spring:
      application:
        name: cloud-stream-provider
      cloud:
        stream:
          binders: # 在此处配置要绑定的rabbitmq的服务信息;
            defaultRabbit: # 表示定义的名称,用于于binding整合
              type: rabbit # 消息组件类型
              environment: # 设置rabbitmq的相关的环境配置
                spring:
                  rabbitmq:
                    host: localhost
                    port: 5672
                    username: guest
                    password: guest
          bindings: # 服务的整合处理
            output: # 指定作为生产者
              destination: studyExchange # 表示要使用的Exchange名称定义
              content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
              binder: defaultRabbit  # 设置要绑定的消息服务的具体设置
    
    eureka:
      client:
        service-url:
          defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7001.com:7001/eureka/, # 注入7002 和 7001
    
    
    
  • 主启动类:

    @SpringBootApplication
    public class StreamMQMain8801 {
        public static void main(String[] args) {
            SpringApplication.run(StreamMQMain8801.class,args);
        }
    }
    
  • 发送消息接口:

    public interface IMessageProvider {
        String send();
    }
    
  • 发送消息接口实现类,注意导包位置。

    import com.jm.cloud.service.IMessageProvider;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cloud.stream.annotation.EnableBinding;
    import org.springframework.cloud.stream.messaging.Source;
    import org.springframework.messaging.MessageChannel;
    import org.springframework.messaging.support.MessageBuilder;
    
    import java.util.UUID;
    
    
    /**
     * @Description
     * @date 2022/6/14 14:54
     */
    @Slf4j
    @EnableBinding(Source.class) // 定义消息推送管道
    public class MessageProviderImpl implements IMessageProvider {
    
        @Autowired
        private MessageChannel output; // 消息发送管道
    
        @Override
        public String send() {
            String serial = UUID.randomUUID().toString();
            output.send(MessageBuilder.withPayload(serial).build());
            log.info("****** serial:" + serial);
            return serial;
        }
    
    }
    
  • controller:

    @RestController
    @RequestMapping("/send")
    public class SendMessageController {
    
        @Autowired
        private IMessageProvider messageProvider;
    
        @GetMapping
        public String sendMessage(){
            return messageProvider.send();
        }
    
    }
    
  • 启动测试:可以看到新的交换机
    在这里插入图片描述

1.5 消费者搭建

  • 新建两个基本相同的modulecloud-stream-rabbitmq-consumer8802cloud-stream-rabbitmq-consumer8803

  • 依赖:

        <dependencies>
    
            <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.example</groupId>
                <artifactId>cloud-api-commons</artifactId>
                <version>1.0-SNAPSHOT</version>
            </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>
    
            <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>
    
  • 配置文件:

    server:
      port: 8803 # 8802
    
    spring:
      application:
        name: cloud-stream-consumer
      cloud:
        stream:
          binders: # 在此处配置要绑定的rabbitmq的服务信息;
            defaultRabbit: # 表示定义的名称,用于于binding整合
              type: rabbit # 消息组件类型
              environment: # 设置rabbitmq的相关的环境配置
                spring:
                  rabbitmq:
                    host: localhost
                    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://eureka7002.com:7002/eureka/,http://eureka7001.com:7001/eureka/, # 注入7002 和 7001
         
    
    
  • 启动类:8803与整个相同。

    @SpringBootApplication
    public class StreamMQ8802 {
        public static void main(String[] args) {
            SpringApplication.run(StreamMQ8802.class,args);
        }
    }
    
  • 业务类:注意导包位置:

    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.cloud.stream.annotation.EnableBinding;
    import org.springframework.cloud.stream.annotation.StreamListener;
    import org.springframework.cloud.stream.messaging.Sink;
    import org.springframework.messaging.Message;
    import org.springframework.stereotype.Component;
    
    /**
     * @Description
     * @date 2022/6/14 15:27
     */
    @Component
    @EnableBinding(Sink.class)
    public class ReceiveMessageListener {
    
        @Value("${server.port}")
        private String serverPort;
    
        @StreamListener(Sink.INPUT)
        public void input(Message<String > message){
            System.out.println(serverPort + "->"+message.getPayload());
        }
    }
    
    
  • 启动测试8802和8801测试:在8801发送消息时,8802可以接收到。
    在这里插入图片描述

1.6 重复消费

如下方场景下,一个订单被两个服务获取到,会造成数据错误。

在这里插入图片描述

  • 启动8803;
  • 8801进行发送消息:可以看到两个消费者都收到了当前消息。
    在这里插入图片描述
    在这里插入图片描述

1.7 group

1.7.1 解决重复消费

在Stream中可以通过分组来解决重复消费问题。同一个group中的多个消费者是竞争关系,一条消息只会被一个应用消费一次;不同组可以重复消费

  • 修改配置文件,使8802 和 8803 都处于A组

    server:
      port: 8802
    
    spring:
      application:
        name: cloud-stream-consumer
      cloud:
        stream:
          binders: # 在此处配置要绑定的rabbitmq的服务信息;
            defaultRabbit: # 表示定义的名称,用于于binding整合
              type: rabbit # 消息组件类型
              environment: # 设置rabbitmq的相关的环境配置
                spring:
                  rabbitmq:
                    host: localhost
                    port: 5672
                    username: guest
                    password: guest
          bindings: # 服务的整合处理
            input: # 这个名字是一个通道的名称
              destination: studyExchange # 表示要使用的Exchange名称定义
              content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
              binder: defaultRabbit  # 设置要绑定的消息服务的具体设置
              group: A # 指定组
    
    
    eureka:
      client: # 客户端进行Eureka注册的配置
        service-url:
          defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7001.com:7001/eureka/, # 注入7002 和 7001
    
    
  • 启动,发送两条消息:可以看出8803和8802是轮询消费的。
    在这里插入图片描述
    在这里插入图片描述

1.7.2 持久化

如果不添加group属性,当消费者端宕机时再恢复时,消息不会持久化,即不会收到宕机时发送的消息,会造成消息的丢失。
添加group属性就可以完美的解决消息的持久化来避免消息丢失的方案。

2. Sleuth

在微服务框架中,一个由客户端发起的请求在后端系统中会经过多个不同服务器节点调用来协同产生最后的请求结果,每个前段请求都会形成一个复杂的分布式服务调用链路,链路中任何一环出现高延时或错误都会引起整个服务最后的请求失败。
Sleuth就是Spring Cloud提供的完整的服务跟踪的解决方案。并且兼容支持zipkin。
在这里插入图片描述

2.1 安装zipkkin和使用

java -jar ‪D:\zipkin\zipkin-server-2.14.1-exec.jar

在这里插入图片描述

  • 访问:http://localhost:9411/zipkin/
    在这里插入图片描述

  • 调用原理:一条请求链路通过TreaceID唯一标识,Span表示发起的请求,各个span通过parentID关联。在这里插入图片描述

  • 依赖关系:
    在这里插入图片描述

2.2 使用sleuth监控

在8001服务提供者和80消费者端都进行修改:

  • 引入依赖:

            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-zipkin</artifactId>
            </dependency>
    
    
  • 修改配置文件:

    spring:
      application:
        name: cloud-payment-service
        # 主要是修改这部分对于zipkin和sleuth
      zipkin:
        base-url: http://localhost:9411
      sleuth:
        sampler:
          probability: 1 # 1 表示全部采样
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: org.gjt.mm.mysql.Driver
        url: jdbc:mysql://localhost:3306/jm-cloud?useUnicode=true&characterEncoding=utf-8&useSSL=false
        username: root
        password: root
    
    

2.3 访问测试

访问80端口的接口:http://localhost:80/consumer/payment/1

  • 可以在界面搜索到服务调用的信息,点击可以查看详情。
    在这里插入图片描述
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值