• 分布式链路监控与追踪系统Zipkin


    1.分布式链路监控与追踪产生背景
    2.SpringCloud Sleuth + Zipkin
    3.分布式服务追踪实现原理
    4.搭建Zipkin服务追踪系统
    5.搭建Zipkin集成RabbitMQ异步传输
    6.SpringCloud2.x新知识介绍

    分布式链路监控与追踪产生背景

    在微服务系统中,随着业务的发展,系统会变得越来越大,那么各个服务之间的调用关系也就变得越来越复杂。一个 HTTP 请求会调用多个不同的微服务来处理返回最后的结果,在这个调用过程中,可能会因为某个服务出现网络延迟过高或发送错误导致请求失败,这个时候,对请求调用的监控就显得尤为重要了。Spring Cloud Sleuth 提供了分布式服务链路监控的解决方案。下面介绍 Spring Cloud Sleuth 整合 Zipkin 的解决方案。

    Zipkin框架介绍

    Zipkin 是 Twitter 的一个开源项目,它基于 Google Dapper 实现的。我们可以使用它来收集各个服务器上请求链路的跟踪数据,并通过它提供的 REST API 接口来辅助查询跟踪数据以实现对分布式系统的监控程序,从而及时发现系统中出现的延迟过高问题。除了面向开发的 API 接口之外,它还提供了方便的 UI 组件来帮助我们直观地搜索跟踪信息和分析请求链路明细,比如可以查询某段时间内各用户请求的处理时间等。

    Zipkin 和 Config 结构类似,分为服务端 Server,客户端 Client,客户端就是各个微服务应用。

    微服务中,如果服务与服务之间的依赖关系非常复杂,如果某个服务出现一些问题,很难知道原因。

     Spring Cloud提供ZipKin组件

    SpringCloud Zipkin 与Sleuth
    Zipkin 是一个开放源代码分布式的跟踪系统,由Twitter公司开源,它致力于收集服务的定时数据,以解决微服务架构中的延迟问题,包括数据的收集、存储、查找和展现。
    每个服务向zipkin报告计时数据,例如用户每次请求服务的处理时间等,可方便的监测系统中存在的瓶颈。
    zipkin会根据调用关系通过Zipkin UI生成依赖关系图。

    Spring Cloud Sleuth为服务之间调用提供链路追踪。通过Sleuth可以很清楚的了解到一个服务请求经过了哪些服务,每个服务处理花费了多长。从而让我们可以很方便的理清各微服务间的调用关系。此外Sleuth可以帮助我们:
    耗时分析: 通过Sleuth可以很方便的了解到每个采样请求的耗时,从而分析出哪些服务调用比较耗时;
    可视化错误: 对于程序未捕捉的异常,可以通过集成Zipkin服务界面上看到;
    链路优化: 对于调用比较频繁的服务,可以针对这些服务实施一些优化措施。
    Spring Cloud Sleuth可以结合Zipkin,将信息发送到Zipkin,利用Zipkin的存储来存储信息,利用Zipkin Ui来展示数据。

      

     

    搭建Zipkin服务追踪系统

          

    Spring Boot 2.0 版本之后,官方已不推荐自己搭建定制了,而是直接提供了编译好的 jar 包。详情可以查看官网:https://zipkin.io/pages/quickstart.html
    注意:zipkin官网已经提供定制了,使用官方jar运行即可。

    启动方式:
    默认端口号启动zipkin服务
    java –jar zipkin.jar 默认端口号; 9411
    访问地址:http://192.168.18.220:9411

    指定端口号启动8080启动zipkin服务
    java -jar zipkin.jar --server.port=8080
    访问地址:http://192.168.18.220:8080

    指定访问rabbitmq 启动
    java -jar zipkin.jar --zipkin.collector.rabbitmq.addresses=127.0.0.1

     

    访问:http://192.168.8.159:9411/zipkin/

     默认的值是在内存中  需要设置持久化到内存中哦

     案例展示  order  ---> member ---> msg

    在Order服务、Member、Msg服务里面引入:

             <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-zipkin</artifactId>
            </dependency>

    pom:

    <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.0.1.RELEASE</version>
        </parent>
        <!-- 管理依赖 -->
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>Finchley.M7</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
        <dependencies>
            <!-- SpringBoot整合Web组件 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <!-- SpringBoot整合eureka客户端 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-zipkin</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-zipkin</artifactId>
            </dependency>
    
        </dependencies>
        <!-- 注意: 这里必须要添加, 否者各种依赖有问题 -->
        <repositories>
            <repository>
                <id>spring-milestones</id>
                <name>Spring Milestones</name>
                <url>https://repo.spring.io/libs-milestone</url>
                <snapshots>
                    <enabled>false</enabled>
                </snapshots>
            </repository>
        </repositories>

    yml配置      收集方式有抽样收集 有全部收集     收集到平台的ip+端口号

    ###会员项目的端口号
    server:
      port: 8000
    ###服务别名----服务注册到注册中心名称 
    spring:
      application:
        name: app-toov5-member
      zipkin: 
        base-url: http://127.0.0.1:9411/
      ###全部采集  
      sleuth:
        sampler:
          probability: 1.0
    
    eureka:
      client:
        service-url:
        ##### 当前会员服务注册到eureka服务地址
          defaultZone: http://localhost:8100/eureka
        ### 需要将我的服务注册到eureka上
        register-with-eureka: true
        ####需要检索服务
        fetch-registry: true

    底层原理:

       

    服务跟踪原理
    为了实现请求跟踪,当请求发送到分布式系统的入口端点时, 只需要服务跟踪框架为该请求创建一个唯的跟踪标识, 同时在分布式系统内部流转的时候,框架始终保持传递该唯一标识, 直到返回给请求方为止,这个唯一标识就是前 文中提到的Trace ID。通过Trace ID的记录,我们就能将所有请求过程的日志关联起来。
    为了统计各处理单元的时间延迟,当请求到达各个服务组件时,或是处理逻辑到达某个状态时,也通过一个唯一 标识来标记它的开始、 具体过程以及结束,该标识就是前文中提到的Span ID。对于每个Span来说,它必须有开始和结束两个节点,通过记录开始Span和结束Span的时间戳,就能统计出该Span的时间延迟,除了时间戳记录之外,它还可以包含一些其他元数据, 比如事件名称、请求信息等
    SpanId记录每一次请求, TraceID记录整个调用链全局ID

        TraceId 和 SpanId 在微服务中传递追踪

        TraceId记录每一次请求,耗时时间、接口调用关系

        TraceId和SpanId在微服务中传递追踪

      

        在微服务中,使用请求头传递TraceId和SpanId,一个TraceId由多个SpanId组合起来。获取到整个微服务调用依赖关系

        

    下一级的parentId就是上一级的spanId 形成一个链

    每次请求生成一个新的spanId

       

    在微服务中,使用请求头传递TraceId和SpanId,一个TraceId由多个SpanId组合起来,获取到整个微服务调用依赖关系。 

    案例如下:

     Eureka略

     Order:

       controller:

       

    import javax.servlet.http.HttpServletRequest;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    @RestController
    public class OrderControler {
    
        // RestTemplate 是有SpringBoot Web组件提供 默认整合ribbon负载均衡器
        // rest方式底层是采用httpclient技术
        @Autowired
        private RestTemplate restTemplate;
    
        /**
         * 在SpringCloud 中有两种方式调用 rest、fegin(SpringCloud)
         * 
         * @return
         */
    
        // 订单服务调用会员服务
        @RequestMapping("/getOrder")
        public String getOrder() {
            // 有两种方式,一种是采用服务别名方式调用,另一种是直接调用 使用别名去注册中心上获取对应的服务调用地址
            String url = "http://app-itmayiedu-member/getMember";
            String result = restTemplate.getForObject(url, String.class);
            System.out.println("订单服务调用会员服务result:" + result);
            return result;
        }
    
        @RequestMapping("/orderToMemberMsg")
        public String orderToMemberMsg(HttpServletRequest request) {
            System.out.println(
                    "TraceId:" + request.getHeader("X-B3-TraceId") + ",spanid:" + request.getHeader("X-B3-SpanId"));
            // 有两种方式,一种是采用服务别名方式调用,另一种是直接调用 使用别名去注册中心上获取对应的服务调用地址
            String url = "http://app-itmayiedu-member/memberAndMsg";
            String result = restTemplate.getForObject(url, String.class);
            System.out.println("订单服务调用会员服务result:" + result);
            return result;
        }
    
    }

    yml:

      

    ###订单服务的端口号
    server:
      port: 8001
    ###服务别名----服务注册到注册中心名称 
    spring:
      application:
        name: app-itmayiedu-order
      zipkin: 
        base-url: http://127.0.0.1:9411/
      ###全部采集  
      sleuth:
        sampler:
          probability: 1.0
    eureka:
      client:
        service-url:
        ##### 当前会员服务注册到eureka服务地址
          defaultZone: http://localhost:8100/eureka
        ### 需要将我的服务注册到eureka上
        register-with-eureka: true
        ####需要检索服务
        fetch-registry: true

    启动类:

      

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.loadbalancer.LoadBalanced;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    import org.springframework.context.annotation.Bean;
    import org.springframework.web.client.RestTemplate;
    
    @SpringBootApplication
    @EnableEurekaClient
    public class AppOrder {
        public static void main(String[] args) {
            SpringApplication.run(AppOrder.class, args);
    
            // 如果使用rest方式以别名方式进行调用依赖ribbon负载均衡器 @LoadBalanced
            // @LoadBalanced就能让这个RestTemplate在请求时拥有客户端负载均衡的能力
        }
    
        // 解决RestTemplate 找不到原因 应该把restTemplate注册SpringBoot容器中 @bean
        @Bean
        @LoadBalanced
        RestTemplate restTemplate() {
            return new RestTemplate();
        }
    
    }

    Member:

      

    import javax.servlet.http.HttpServletRequest;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    
    @RestController
    public class MemberApiController {
        @Value("${server.port}")
        private String serverPort;
        @Autowired
        private RestTemplate restTemplate;
    
        @RequestMapping("/getMember")
        public String getMember(HttpServletRequest request) {
    
            return "this is member,我是会员服务,springcloud2.0版本!端口号:" + serverPort
                    + request.getHeader("X-B3-TraceId") + ",spanid:" + request.getHeader("X-B3-SpanId");
    
        }
    
        @RequestMapping("/memberAndMsg")
        public String sndMsg() {
            String url = "http://app-itmayiedu-msg/sndMsg";
            String result = restTemplate.getForObject(url, String.class);
            System.out.println("会员服务调用消息服务result:" + result);
            return result;
        }
    
    }

    yml:

    ###会员项目的端口号
    server:
      port: 8000
    ###服务别名----服务注册到注册中心名称 
    spring:
      application:
        name: app-toov5-member
      zipkin: 
        base-url: http://127.0.0.1:9411/
      ###全部采集  
      sleuth:
        sampler:
          probability: 1.0
    
    eureka:
      client:
        service-url:
        ##### 当前会员服务注册到eureka服务地址
          defaultZone: http://localhost:8100/eureka
        ### 需要将我的服务注册到eureka上
        register-with-eureka: true
        ####需要检索服务
        fetch-registry: true

    启动类:

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.loadbalancer.LoadBalanced;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    import org.springframework.context.annotation.Bean;
    import org.springframework.web.client.RestTemplate;
    
    @SpringBootApplication
    @EnableEurekaClient
    public class AppMember {
    
        // @EnableEurekaClient 将当前服务注册到eureka上
        public static void main(String[] args) {
            SpringApplication.run(AppMember.class, args);
        }
    
        // 解决RestTemplate 找不到原因 应该把restTemplate注册SpringBoot容器中 @bean
        @Bean
        @LoadBalanced
        RestTemplate restTemplate() {
            return new RestTemplate();
        }
    
    }

    Msg

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @SpringBootApplication
    @RestController
    public class MsgController {
    
        @RequestMapping("/sndMsg")
        public String sndMsg() {
            try {
                Thread.sleep(5000);
            } catch (Exception e) {
                // TODO: handle exception
            }
            return "我是消息服务平台";
        }
    
        public static void main(String[] args) {
            SpringApplication.run(MsgController.class, args);
        }
    
    }

    启动类:

    ###订单服务的端口号
    server:
      port: 8003
    ###服务别名----服务注册到注册中心名称 
    spring:
      application:
        name: app-toov5-msg
      zipkin: 
        base-url: http://127.0.0.1:9411/
      ###全部采集  
      sleuth:
        sampler:
          probability: 1.0
    eureka:
      client:
        service-url:
        ##### 当前会员服务注册到eureka服务地址
          defaultZone: http://localhost:8100/eureka
        ### 需要将我的服务注册到eureka上
        register-with-eureka: true
        ####需要检索服务
        fetch-registry: true

     Eureka:

     访问: http://127.0.0.1:8001/orderToMemberMsg

     

    点击上面的实际三5.024s

      

  • 相关阅读:
    五种方法实现python3-随机生成10位包含数字和字母的密码
    用matplotlib中imshow()函数绘图
    MTNET 自用ios网络库开源
    移动时代软件测试团队该往哪里去?
    自己做的加速app测试流程的小工具,目前打算开放使用,想注册的朋友抓紧了,嘻嘻
    golang append
    Invalid Image Path
    xcode 插件之KSImageNamed-Xcode
    dissmiss a UISearchBar with an SearchBarController
    nginx server_name
  • 原文地址:https://www.cnblogs.com/toov5/p/10336578.html
Copyright © 2020-2023  润新知