分布式调用系统的现状:
当前,随着互联网架构的扩张,分布式系统变得日趋复杂,越来越多的组件开始走向分布式化,如微服务、消息收发、分布式数据库、分布式缓存、分布式对象存储、跨域调用,这些组件共同构成了繁杂的分布式网络。
•当应用A发出某个请求时,其背后可能有数十个甚至更多的服务被调用,可谓是“牵一发而动全身”
•业界非常知名的分布式链路跟踪服务:
•阿里:鹰眼
•大众点评:CAT
•美团:OCTO
•京东: Hydra
•Twitter—OpenZipkin
•鹰眼系统简介:
•阿里中分布式调用跟踪是采用鹰眼(EagleEye)系统来实现的,鹰眼是基于日志的分布式调用跟踪系统,其理念脱胎于Google Dapper论文,其关键核心在于调用链,为每个请求生成全局唯一的ID(Traceld),通过它将不同系统的“孤立的”调用信息关联在一起,还原出更多有价值的数据。
•可以在业务异常日志的错误信息中找到Traceld(比如TraceId=ac18287913742691251746923),之后在鹰眼系统中只需要输入Traceld,就可以看到调用链中具体的情况,在调用链上更加直观地定位到问题,层层排查后确定问题的所在。
ZipKin是一个链路跟踪工具,可以用来监控微服务集群中调用链路的通畅情况
•首先,ZipKin是Spring Cloud 的分布式链路跟踪解决方案:
•比如存在两个子项目,并在一个项目中使用RestTemplate或者Feign等方法调用另外一个项目中的接口,这样就可以利用ZipKin进行跟踪服务!
•第一步:需要一个zipkin-server服务
•第二步:需要两个trace服务,然后进行调用跟踪
代码实现:
1.创建eureka-server:略(详见前文)
2.创建zipkin-server:独立于eureka,可以注册到eureka做负载和高可用;
pom:
<dependencies>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-server</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-storage-mysql</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-ui</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Edgware.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<finalName>spring-cloud-06-config-server</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.cc.springcloud.Application</mainClass>
</configuration>
</plugin>
</plugins>
</build>
--------------------------------------------------------------------------------------------
application.properties:
spring.application.name=zipkin-server
server.port=9500
spring.datasource.url=jdbc:mysql://localhost:3306/zipkin?characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.schema=classpath:/zipkin.sql
spring.datasource.initialize=true
spring.datasource.continue-on-error=true
##真实生产环境应该使用ES进行存储,myqsl无法承受高并发
zipkin.storage.type=mysql
--------------------------------------------------------------------------------------------
Application:
package com.cc.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import zipkin.server.internal.EnableZipkinServer;
@EnableZipkinServer //zipkin
@SpringBootApplication //springboot 核心配置
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
--------------------------------------------------------------------------------------------
此文件官网下载就可以;
3.创建3个trace,代码结构基本一致,最为服务依次调用测试:
pom:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 标示这个工程是一个服务的提供者,需要引入此jar -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- 动态刷新的一个模块jar -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- sleuth(抓取) && zipkin -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
<!-- kafka
<dependency>
<groupId>org.springframeword.cloud</groupId>
<artifactId>spring-cloud-starter-stream-kafka</artifactId>
</dependency>
<dependency>
<groupId>org.springframeword.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin-stream</artifactId>
</dependency>-->
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Edgware.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<finalName>spring-cloud-08-trace-1</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.cc.springcloud.Application</mainClass>
</configuration>
</plugin>
</plugins>
</build>
---------------------------------------------------------------------------------------------
application.properties:
spring.application.name=trace-1
##微服务方式尽量不要加server.context-path,只用名字加方法实现
#server.context-path=/provider
server.context-path=/
server.port=7001
##需要引入eureka注册中心的地址
##下面两条配置代表注册到注册中心后显示自己的IP地址,实际工作中尽量加上
eureka.instance.prefer-ip-address=true
eureka.instance.instance-id=${spring.application.name}:${spring.cloud.client.ipAddress}:${server.port}
##租期到期时间间隔
eureka.instance.lease-expiration-duration-in-seconds=30
##租期更新时间间隔
eureka.instance.lease-renewal-interval-in-seconds=10
##开启健康检查,必须要引入spring-boot-starter-actuator动态刷新jar
eureka.client.healthcheck.enabled=true
eureka.client.service-url.defaultZone=http://eureka1:8001/eureka
## 配置zipkin地址以及sleuth服务抓取日志的采样百分比
spring.zipkin.base-url=http://localhost:9500
## 1.0代表100%
spring.sleuth.sampler.percentage=1.0
##配置kafka与zookeeper
#spring.cloud.stream.kafka.zkNodes=192.168.1.11:2182,192.168.1.11:2183,192.168.1.11:2184
#spring.cloud.stream.kafka.zkNodes=192.168.1.11:9092
---------------------------------------------------------------------------------------------
Application:
package com.cc.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
@EnableDiscoveryClient //标示是一个具体的服务,需要向注册中心注册
@SpringBootApplication //springboot 核心配置
public class Application {
@Bean
@LoadBalanced //用于实现内部的服务负载均衡机制:service-id service-name
public RestTemplate restTemplate() { //springcloud项目基于http通讯,所以此处构建RestTemplate
HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
httpComponentsClientHttpRequestFactory.setConnectTimeout(10000);
httpComponentsClientHttpRequestFactory.setConnectionRequestTimeout(10000);
httpComponentsClientHttpRequestFactory.setReadTimeout(20000);
return new RestTemplate(httpComponentsClientHttpRequestFactory);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
---------------------------------------------------------------------------------------------
Trace1Controller:
package com.cc.springcloud.api;
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 Trace1Controller {
@Autowired
private RestTemplate restTemplate;
@RequestMapping(value="/trace1")
public String trace1() {
System.out.println("-----trace1-----");
return restTemplate.getForObject("http://trace-2/trace2", String.class);
}
}
---------------------------------------------------------------------------------------------
trace-2/Trace2Controller:
package com.cc.springcloud.api;
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 Trace2Controller {
@Autowired
private RestTemplate restTemplate;
@RequestMapping(value="/trace2")
public String trace1() {
System.out.println("-----trace2-----");
return restTemplate.getForObject("http://trace-3/trace3", String.class);
}
}
---------------------------------------------------------------------------------------------
trace-3/Trace3Controller:
package com.cc.springcloud.api;
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 Trace3Controller {
@Autowired
private RestTemplate restTemplate;
@RequestMapping(value="/trace3")
public String trace1() {
System.out.println("-----trace3-----");
//return restTemplate.getForObject("http://trace-2/trace2", String.class);
return "success!";
}
}
---------------------------------------------------------------------------------------------
启动测试:
zipkin-server启动后数据库表自动创建(前提先手动创建zipkin数据库);