背景
微服务架构下,一个请求可能会经过多个服务才会得到结果,如果在这个过程中出现了异常,就很难去定位问题。所以,必须要实现一个分布式链路跟踪的功能,直观的展示出完整的调用过程。
什么是Spring Cloud Sleuth?
Spring Cloud Sleuth是Spring Cloud提供的分布式系统服务链追踪组件,它大量借用了Google的Dapper,Twitter的Zipkin。学习Spring Cloud Sleuth,最好先对Zipkin有一些了解,对span、trace这些概念有相应的认识。
如何使用Spring Cloud Sleuth?
在这里,为了复习下之前学过的Spring Cloud相关的组件,会通过之前搭建的多个服务来学习。
microservice-provider:服务提供者
microservice-consumer:服务消费者
Eureka Server:作为注册中心,提供服务注册和服务发现的功能。
microservice-gateway:微服务网关,所有的调用,都是经过网关进行转发
microservice-zipkin-server:收集调用信息,提供界面进行查看
microservice-provider,microservice-consumer,microservice-gateway和microservice-zipkin-server都向Eureka Server注册;
microservice-consumer调用microservice-provider时,通过microservice-gateway进行调用。访问microservice-consumer时,通过microservice-gateway访问。
microservice-provider,microservice-consumer,microservice-gateway都向microservice-zipkin-server上报调用信息。
Eureka Server:
pom文件:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.3.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Brixton.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
配置文件application.properties:
server.port=8761
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
Eureka Server启动端口为8761。
启动类:
@SpringBootApplication
@EnableEurekaServer//声明这是一个Eureka server
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
EurekaServerApplication上添加注解@EnableEurekaServer,声明这是一个Eureka server。
microservice-zipkin-server:
Zipkin需要配置jdk1.8才可以运行。
pom文件:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-server</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-ui</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
配置文件application.yml:
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
server:
port: 9411
spring:
application:
name: microservice-zipkin-server
启动类ZipkinServerApplication,添加@EnableZipkinServer注解,声明这是一个Zipkin Server,@EnableEurekaClient注解声明这是一个Eureka Client,向Eureka Server注册:
@SpringBootApplication
@EnableEurekaClient
@EnableZipkinServer
public class ZipkinServerApplication {
public static void main(String[] args) {
SpringApplication.run(ZipkinServerApplication.class, args);
}
}
配置文件中,定义了服务的名称为microservice-zipkin-server,端口为9411,向地址为http://localhost:8761/eureka/ 的Eureka Server注册。
microservice-gateway:
网关服务的pom文件:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.3.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Brixton.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
配置文件application.properties:
server.port=9100
spring.application.name=microservice-gateway
spring.zipkin.base-url=http://localhost:9411
spring.sleuth.sampler.percentage=1.0
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
microservice-gateway配置了eureka.client.service-url.defaultZone向Eureka Server注册,配置了spring.zipkin.base-url,向Zipkin上报调用信息。配置spring.sleuth.sampler.percentage=1.0,说明采集率是100%,所有的数据都会上报给Zipkin,如果不配置,默认是10%。
上面的pom文件中,spring-cloud-starter-zipkin依赖了spring-cloud-starter-sleuth和spring-cloud-sleuth-zipkin,只需要在pom中添加spring-cloud-starter-zipkin,在配置文件中配置spring.zipkin.base-url,就可以为项目整合sleuth和zipkin。当有请求访问服务时,就会产生链路数据,自动向Zipkin上报。
启动类SpringCloudZuulApplication:
@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy // 声明这是一个zuul代理
public class SpringCloudZuulApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudZuulApplication.class, args);
}
}
microservice-provider
pom文件:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
配置文件application.properties:
server.port=8000
spring.application.name=microservice-provider
spring.zipkin.base-url=http://localhost:9411
spring.sleuth.sampler.percentage=1.0
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
启动类:
@EnableDiscoveryClient//声明是一个Eureka client
@SpringBootApplication
public class MicroServiceProviderApplication {
public static void main(String[] args) {
SpringApplication.run(MicroServiceProviderApplication.class, args);
}
}
microservice-provider中还有一个ProviderController来提供服务,为了简单起见,直接返回hello world字符串。
@RestController
public class ProviderController {
private final Logger logger = LoggerFactory.getLogger(ProviderController.class);
@Autowired
private DiscoveryClient discoveryClient;
@RequestMapping(value = "/provider", method = RequestMethod.GET)
public String provider() {
ServiceInstance serviceInstance = discoveryClient.getLocalServiceInstance();
logger.info("host:{}, service_id:{}", serviceInstance.getHost(), serviceInstance.getServiceId());
return "hello world";
}
}
microservice-consumer
pom文件:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.3.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.7</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Brixton.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
配置文件application.properties:
server.port=9000
spring.application.name=microservice-consumer
spring.zipkin.base-url=http://localhost:9411
spring.sleuth.sampler.percentage=1.0
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
启动类MicroServiceConsumeApplication:
@EnableDiscoveryClient
@SpringBootApplication
public class MicroServiceConsumeApplication {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(MicroServiceConsumeApplication.class, args);
}
}
microservice-consumer在启动时,初始化RestTemplate实例,由于调用时是经过microservice-gateway的,microservice-gateway默认集成了Ribbon和Hystrix,所以RestTemplate不需要再用@LoadBalanced修饰。
microservice-consumer中有个ConsumerController,暴露接口,调用microservice-provider提供的服务。
@RestController
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@RequestMapping(value = "/consumer", method = RequestMethod.GET)
public String consumer() {
return this.restTemplate.getForEntity("http://localhost:9100/microservice-provider/provider", String.class).getBody();
}
}
这些项目都创建好之后,启动,可以看到Eureka Server上,其它四个服务都注册上去了。
浏览器中输入http://localhost:9100/microservice-consumer/consumer,请求会经过microservice-gateway,转发到microservice-consumer的接口上,microservice-consumer再请求http://localhost:9100/microservice-provider/provider,还会经过microservice-gateway,转发到microservice-provider的接口上。最终会在页面上看到返回hello world。
Zipkin Server有提供的界面,可以直观的查看trace信息,浏览器输入http://localhost:9411,就可以看到刚才的调用已经生成了调用链,信息如下图所示。
点击一条详细记录,可以看到具体的调用信息:
从图中可以看出,这次调用涉及到三个服务,深度为5,包含5个span,右上角还有个Json按钮,可以查看json格式的数据。具体的含义可以学习Zipkin,就不再细说了。
参考资料:
1.《Spring Cloud与Docker微服务架构实战》 周立 著
2.《Spring Cloud微服务实战》 翟永超 著
3.《深入理解Spring Cloud与微服务构建》 方志朋 著