第十二节:Stream(消息驱动)和Sleuth(分布式请求链路跟踪)

一、Stream(消息驱动)

消息驱动概述:

MQ(消息中间件):ActiveMQ、RabbitMQ、RocketMQ、kafkaMQ

解决问题:多种MQ适配(切换、维护、开发)

屏蔽底层消息中间件的差异,降低切换成本,统一消息的编程模型。

官方连接:https://spring.io/projects/spring-cloud-stream

  • 官方定义 Spring Cloud Stream 是一个构建消息驱动微服务的框架。
  • 应用程序通过 inputs 或者 outputs 来与 Spring Cloud Stream 中binder 交互。
  • 通过我们配置来 binding ,而 Spring Cloud Stream 的 binder 负责与消息中间件交互。所以,我们只需要搞清楚如何与 Spring Cloud Stream 交互就可以方便使用消息驱动的方式。
  • 通过使用Spring Integration来连接消息代理中间件以实现消息事件驱动。
  • Spring Cloud Stream 为一些供应商的消息中间件产品提供了个性化的自动化配置实现,引用了发布-订阅、消费组、分区的三个核心概念。
  • 目前仅支持RabbitMQKafka

中文操作手册:https://m.wang1314.com/doc/webapp/topic/20971999.html

第十二节:Stream(消息驱动)和Sleuth(分布式请求链路跟踪)_第1张图片

标准流程套路:Binder、Channel、Source和Sink

第十二节:Stream(消息驱动)和Sleuth(分布式请求链路跟踪)_第2张图片

编码API和常用注解:

第十二节:Stream(消息驱动)和Sleuth(分布式请求链路跟踪)_第3张图片

案例说明:

RabbitMQ环境OK

新建三个模块:

  • cloud-stream-rabbitmq-provider8801,作为生产者进行发消息模块
  • cloud-stream-rabbitmq-consumer8802,作为消息接收模块
  • cloud-stream-rabbitmq-consumer8803,作为消息接收模块

消息驱动生产者:

pom


        
            org.springframework.cloud
            spring-cloud-starter-stream-rabbit
        

yml

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://localhost:7001/eureka
  instance:
    lease-renewal-interval-in-seconds: 2 #心跳时间间隔
    lease-expiration-duration-in-seconds: 5 #如果现在超过了5秒的间隔
    instance-id: send-8801.com  #在信息列表显示主机名称
    prefer-ip-address: true  #访问路径变为IP地址

主启动类StreamMQMain8801

package com.jiao.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author jyl
 * @create 2021-3-15
 */

@SpringBootApplication
public class StreamMQMain8801 {
    public static void main(String[] args) {
        SpringApplication.run(StreamMQMain8801.class,args);
    }
}

业务类

  • 发送消息接口(IMessageProvider)
package com.jiao.springcloud.service;

public interface IMessageProvider {
    public String send();
}
  • 发送消息接口实现类(IMessageProviderImpl)
package com.jiao.springcloud.service.impl;

import com.jiao.springcloud.service.IMessageProvider;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.MessageChannel;

import javax.annotation.Resource;
import java.util.UUID;

@EnableBinding(Source.class)    //定义消息的推送管道
public class IMessageProviderImpl implements IMessageProvider {
    @Resource
    private MessageChannel output;  //消息发送管道

    public String send() {
        String serial = UUID.randomUUID().toString();
        output.send(MessageBuilder.withPayload(serial).build());
        System.out.println("*********serial:"+serial);
        return null;
    }
}
  • Controller(SendMessageController)
package com.jiao.springcloud.controller;

import com.jiao.springcloud.service.IMessageProvider;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class SendMessageController {
    @Resource
    private IMessageProvider messageProvider;

    @GetMapping(value = "/sendMessage")
    public String sendMessage(){
        return messageProvider.send();
    }
}

第十二节:Stream(消息驱动)和Sleuth(分布式请求链路跟踪)_第4张图片

测试(启动7001、启动RabbitMQ、启动8801)

http://localhost:8801/sendMessage

第十二节:Stream(消息驱动)和Sleuth(分布式请求链路跟踪)_第5张图片

第十二节:Stream(消息驱动)和Sleuth(分布式请求链路跟踪)_第6张图片

消息驱动消费者:

pom


        
            org.springframework.cloud
            spring-cloud-starter-stream-rabbit
        

yml

第十二节:Stream(消息驱动)和Sleuth(分布式请求链路跟踪)_第7张图片

主启动

package com.jiao.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author jyl
 * @create 2021-3-15
 */

@SpringBootApplication
public class StreamMQMain8802 {
    public static void main(String[] args) {
        SpringApplication.run(StreamMQMain8802.class,args);
    }
}

业务类

package com.jiao.springcloud.controller;

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;

@Component
@EnableBinding(Sink.class)
public class ReceiveMessageListenerController {
    @Value("${server.port}")
    private String serverPort;

    @StreamListener(Sink.INPUT)
    public void input(Message message){
        System.out.println("消费者1号,----->接收到的消息:"+message.getPayload()+"\t port:"+serverPort);
    }
}

测试(7001、RabbitMQ、8801、8802)

测试8801发送8802接收消息:http://localhost:8801/sendMessage

第十二节:Stream(消息驱动)和Sleuth(分布式请求链路跟踪)_第8张图片

第十二节:Stream(消息驱动)和Sleuth(分布式请求链路跟踪)_第9张图片

第十二节:Stream(消息驱动)和Sleuth(分布式请求链路跟踪)_第10张图片

第十二节:Stream(消息驱动)和Sleuth(分布式请求链路跟踪)_第11张图片

分组消费与持久化:

8803与8802一样,yml稍作修改。

启动(RabbitMQ、7001服务注册、8801消息生产、8802消息消费、8803消息消费)

第十二节:Stream(消息驱动)和Sleuth(分布式请求链路跟踪)_第12张图片

第十二节:Stream(消息驱动)和Sleuth(分布式请求链路跟踪)_第13张图片

运行后两个问题:(分组特性解决)

  • 有重复消费问题

8801请求sendMessage,8802和8803都消费了。

解决:分组和持久化属性group(不同组可以全面消费,同一组会发生竞争关系,其中只有一个可以消费。

第十二节:Stream(消息驱动)和Sleuth(分布式请求链路跟踪)_第14张图片

8802/8803变成不同的组,group两个不同。group:(jiaoA,jiaoB)

8802修改yml:

第十二节:Stream(消息驱动)和Sleuth(分布式请求链路跟踪)_第15张图片

8803修改yml:与上位置同

第十二节:Stream(消息驱动)和Sleuth(分布式请求链路跟踪)_第16张图片

消费了两次。

8802/8803实现轮训分组,每次只有一个消费者,8801模块的发的消息只能被8802或者8803其中一个接收到,这样避免了重复消费。

8802/8803变成不同的组,group两个不同。group:(jiaoA,jiaoA)

第十二节:Stream(消息驱动)和Sleuth(分布式请求链路跟踪)_第17张图片

结果:8801发送两次,8802消费一次,8803消费一次

  • 消息持久化问题
  1. 停止8802/8803并且去掉8802分组的group:jiaoA,
  2. 8801先发送4条消息到RabbitMQ
  3. 8802启动,消息丢失。8803启动,消息消费,没有丢失。

二、Sleuth(分布式请求链路跟踪)

概述:

链路中任何一环出现延时或者错误都会引起整个请求最后失败。(提供一套完整的服务跟踪的解决方案,兼容支持了zipkin。)

搭建链路监控步骤:

  • zipkin

H版后不用安装Zipkin Server,直接调用jar包。

下载地址:http://dl.bintray.com/openzipkin/maven/io/zipkin/java/zipkin-server/(zipkin-server-2.12.9-exec.jar)

在下载路径启动cmd窗口:输入 java -jar zipkin-server-2.12.9-exec.jar

后台启动:

第十二节:Stream(消息驱动)和Sleuth(分布式请求链路跟踪)_第18张图片

运行控制台:http://localhost:9411/zipkin

第十二节:Stream(消息驱动)和Sleuth(分布式请求链路跟踪)_第19张图片

Trace:类似于树结构的Span集合,表示一条调用链路,存在唯一标识。

span:表示调用链路来源,通俗讲span就是一次请求信息。

第十二节:Stream(消息驱动)和Sleuth(分布式请求链路跟踪)_第20张图片

  • 服务提供者(修改cloud-provider-payment8001)

pom


        
            org.springframework.cloud
            spring-cloud-starter-zipkin
        

yml

...
spring:
  application:
    name: cloud-payment-service
  zipkin:
    base-url: http://localhost:9411
  sleuth: 
    sampler:
      probability: 1  #采样率值介于0-1之间,1表示全部采集
...

业务类PaymentController

...
@GetMapping("/payment/zipkin")
    public String paymentZipkin(){
        return "I'm PaymentZipkin server fall back.";
    }
...
  • 服务消费者(调用方)(修改cloud-consumer-order80)

pom、yml与上面一样

业务类OrderController

...
@GetMapping("/consumer/payment/zipkin")
    public String paymentZipkin(){
        String result = restTemplate.getForObject("http://localhost:8001"+"payment/zipkin",String.class);
        return result;
    }
...
  • 依次启动eureka7001、8001、80(80调用8001几下测试:http://localhost/consumer/payment/zipkin)
  • 打开浏览器访问:

第十二节:Stream(消息驱动)和Sleuth(分布式请求链路跟踪)_第21张图片

 

你可能感兴趣的:(JavaCloud【尚硅谷】,java,rabbitmq,spring,boot,中间件)