1.分布式链路监控与追踪产生背景
2.SpringCloud Sleuth + Zipkin
3.分布式服务追踪实现原理
4.搭建Zipkin服务追踪系统
5.搭建Zipkin集成RabbitMQ异步传输
6.SpringCloud2.x新知识介绍html
分布式链路监控与追踪产生背景
在微服务系统中,随着业务的发展,系统会变得愈来愈大,那么各个服务之间的调用关系也就变得愈来愈复杂。一个 HTTP 请求会调用多个不一样的微服务来处理返回最后的结果,在这个调用过程当中,可能会由于某个服务出现网络延迟太高或发送错误致使请求失败,这个时候,对请求调用的监控就显得尤其重要了。Spring Cloud Sleuth 提供了分布式服务链路监控的解决方案。下面介绍 Spring Cloud Sleuth 整合 Zipkin 的解决方案。java
Zipkin框架介绍
Zipkin 是 Twitter 的一个开源项目,它基于 Google Dapper 实现的。咱们可使用它来收集各个服务器上请求链路的跟踪数据,并经过它提供的 REST API 接口来辅助查询跟踪数据以实现对分布式系统的监控程序,从而及时发现系统中出现的延迟太高问题。除了面向开发的 API 接口以外,它还提供了方便的 UI 组件来帮助咱们直观地搜索跟踪信息和分析请求链路明细,好比能够查询某段时间内各用户请求的处理时间等。web
Zipkin 和 Config 结构相似,分为服务端 Server,客户端 Client,客户端就是各个微服务应用。spring
微服务中,若是服务与服务之间的依赖关系很是复杂,若是某个服务出现一些问题,很难知道缘由。服务器
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来展现数据。app
搭建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