第一篇:服务的注册与发现Eureka(Finchley版本
鉴于《史上最简单的Spring Cloud教程》很受读者欢迎,再次我特意升级了一下版本,目前支持的版本为Spring Boot版本2.0.3.RELEASE,Spring Cloud版本为Finchley.RELEASE。
Finchley版本的官方文档如下:
http://cloud.spring.io/spring-cloud-static/Finchley.RELEASE/single/spring-cloud.html
spring cloud 为开发人员提供了快速构建分布式系统的一些工具,包括配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等。它运行环境简单,可以在开发人员的电脑上跑。另外说明spring cloud是基于springboot的,所以需要开发中对springboot有一定的了解,如果不了解的话可以看这篇文章:2小时学会springboot。另外对于“微服务架构” 不了解的话,可以通过搜索引擎搜索“微服务架构”了解下。
在这里,我还是采用Eureka作为服务注册与发现的组件,至于Consul 之后会出文章详细介绍。
2.1 首先创建一个maven主工程。
首先创建一个主Maven工程,在其pom文件引入依赖,spring Boot版本为2.0.3.RELEASE,Spring Cloud版本为Finchley.RELEASE。这个pom文件作为父pom文件,起到依赖版本控制的作用,其他module工程继承该pom。这一系列文章全部采用这种模式,其他文章的pom跟这个pom一样。再次说明一下,以后不再重复引入。代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>
4.0.0
</modelVersion>
<groupId>
com.forezp
</groupId>
<artifactId>
sc-f-chapter1
</artifactId>
<version>
0.0.1-SNAPSHOT
</version>
<packaging>
pom
</packaging>
<name>
sc-f-chapter1
</name>
<description>
Demo project for Spring Boot
</description>
<parent>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-parent
</artifactId>
<version>
2.0.3.RELEASE
</version>
<relativePath/>
</parent>
<modules>
<module>
eureka-server
</module>
<module>
service-hi
</module>
</modules>
<properties>
<project.build.sourceEncoding>
UTF-8
</project.build.sourceEncoding>
<project.reporting.outputEncoding>
UTF-8
</project.reporting.outputEncoding>
<java.version>
1.8
</java.version>
<spring-cloud.version>
Finchley.RELEASE
</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-test
</artifactId>
<scope>
test
</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>
org.springframework.cloud
</groupId>
<artifactId>
spring-cloud-dependencies
</artifactId>
<version>
${spring-cloud.version}
</version>
<type>
pom
</type>
<scope>
import
</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-maven-plugin
</artifactId>
</plugin>
</plugins>
</build>
</project>
2.2 然后创建2个model工程:一个model工程作为服务注册中心,即Eureka Server,另一个作为Eureka Client。
下面以创建server为例子,详细说明创建过程:
右键工程->创建model-> 选择spring initialir 如下图:
下一步->选择cloud discovery->eureka server ,然后一直下一步就行了。
创建完后的工程,其pom.xml继承了父pom文件,并引入spring-cloud-starter-netflix-eureka-server的依赖,代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>
4.0.0
</modelVersion>
<groupId>
com.forezp
</groupId>
<artifactId>
eureka-server
</artifactId>
<version>
0.0.1-SNAPSHOT
</version>
<packaging>
jar
</packaging>
<name>
eureka-server
</name>
<description>
Demo project for Spring Boot
</description>
<parent>
<groupId>
com.forezp
</groupId>
<artifactId>
sc-f-chapter1
</artifactId>
<version>
0.0.1-SNAPSHOT
</version>
</parent>
<dependencies>
<dependency>
<groupId>
org.springframework.cloud
</groupId>
<artifactId>
spring-cloud-starter-netflix-eureka-server
</artifactId>
</dependency>
</dependencies>
</project>
2.3 启动一个服务注册中心,只需要一个注解@EnableEurekaServer,这个注解需要在springboot工程的启动application类上加:
@SpringBootApplication
@EnableEurekaServer
publicclass EurekaServerApplication {
public
static
void
main
(String[] args) {
SpringApplication.run( EurekaServerApplication.class, args );
}
}
**2.4 **eureka是一个高可用的组件,它没有后端缓存,每一个实例注册之后需要向注册中心发送心跳(因此可以在内存中完成),在默认情况下erureka server也是一个eureka client ,必须要指定一个 server。eureka server的配置文件appication.yml:
server:
port:
8761
eureka:
instance:
hostname: localhost
client
:
registerWithEureka:
false
fetchRegistry:
false
serviceUrl:
defaultZone: http:
//${eureka.instance.hostname}:${server.port}/eureka/
spring:
application:
name: eurka-
server
通过eureka.client.registerWithEureka:false和fetchRegistry:false来表明自己是一个eureka server.
2.5 eureka server 是有界面的,启动工程,打开浏览器访问:
http://localhost:8761 ,界面如下:
No
application available 没有服务被发现
……^_^
因为没有注册服务当然不可能有服务被发现了。
当client向server注册时,它会提供一些元数据,例如主机和端口,URL,主页等。Eureka server 从每个client实例接收心跳消息。 如果心跳超时,则通常将该实例从注册server中删除。
创建过程同server类似,创建完pom.xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>
4.0.0
</modelVersion>
<groupId>
com.forezp
</groupId>
<artifactId>
service-hi
</artifactId>
<version>
0.0.1-SNAPSHOT
</version>
<packaging>
jar
</packaging>
<name>
service-hi
</name>
<description>
Demo project for Spring Boot
</description>
<parent>
<groupId>
com.forezp
</groupId>
<artifactId>
sc-f-chapter1
</artifactId>
<version>
0.0.1-SNAPSHOT
</version>
</parent>
<dependencies>
<dependency>
<groupId>
org.springframework.cloud
</groupId>
<artifactId>
spring-cloud-starter-netflix-eureka-client
</artifactId>
</dependency>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-web
</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-maven-plugin
</artifactId>
</plugin>
</plugins>
</build>
</project>
通过注解@EnableEurekaClient 表明自己是一个eurekaclient.
@SpringBootApplication
@EnableEurekaClient
@RestController
publicclass ServiceHiApplication {
public
static
void
main
(String[] args) {
SpringApplication.run( ServiceHiApplication.class, args );
}
@Value
(
"${server.port}")
String port;
@RequestMapping
(
"/hi")
public
String
home(@
RequestParam(value =
"name", defaultValue =
"forezp") String name) {
return
"hi "
+ name +
" ,i am from port:"+ port;
}
}
仅仅@EnableEurekaClient是不够的,还需要在配置文件中注明自己的服务注册中心的地址,application.yml配置文件如下:
server:
port:
8762
spring:
application:
name: service-hi
eureka:
client
:
serviceUrl:
defaultZone: http:
//localhost:8761/eureka/
· 1
· 2
· 3
· 4
· 5
· 6
· 7
· 8
· 9
· 10
· 11
· 12
· 13
需要指明spring.application.name,这个很重要,这在以后的服务与服务之间相互调用一般都是根据这个name 。
启动工程,打开http://localhost:8761 ,即eureka server 的网址:
你会发现一个服务已经注册在服务中了,服务名为SERVICE-HI ,端口为7862
这时打开 http://localhost:8762/hi?name=forezp ,你会在浏览器上看到 :
hi forezp,i am from port:8762
在上一篇文章,讲了服务的注册和发现。在微服务架构中,业务都会被拆分成一个独立的服务,服务与服务的通讯是基于http restful的。Spring cloud有两种服务调用方式,一种是ribbon+restTemplate,另一种是feign。在这一篇文章首先讲解下基于ribbon+rest。
Ribbon is a client side load balancer which gives you a lot of control over the behaviour of HTTP and TCP clients. Feign already uses Ribbon, so if you are using @FeignClient then this section also applies.
—–摘自官网
ribbon是一个负载均衡客户端,可以很好的控制htt和tcp的一些行为。Feign默认集成了ribbon。
ribbon 已经默认实现了这些配置bean:
这一篇文章基于上一篇文章的工程,启动eureka-server 工程;启动service-hi工程,它的端口为8762;将service-hi的配置文件的端口改为8763,并启动,这时你会发现:service-hi在eureka-server注册了2个实例,这就相当于一个小的集群。
如何在idea下启动多个实例,请参照这篇文章:
https://blog.csdn.net/forezp/article/details/76408139
访问localhost:8761如图所示:
如何一个工程启动多个实例,请看这篇文章:https://blog.csdn.net/forezp/article/details/76408139
重新新建一个spring-boot工程,取名为:service-ribbon;
在它的pom.xml继承了父pom文件,并引入了以下依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>
4.0.0
</modelVersion>
<groupId>
com.forezp
</groupId>
<artifactId>
service-ribbon
</artifactId>
<version>
0.0.1-SNAPSHOT
</version>
<packaging>
jar
</packaging>
<name>
service-ribbon
</name>
<description>
Demo project for Spring Boot
</description>
<parent>
<groupId>
com.forezp
</groupId>
<artifactId>
sc-f-chapter2
</artifactId>
<version>
0.0.1-SNAPSHOT
</version>
</parent>
<dependencies>
<dependency>
<groupId>
org.springframework.cloud
</groupId>
<artifactId>
spring-cloud-starter-netflix-eureka-client
</artifactId>
</dependency>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-web
</artifactId>
</dependency>
<dependency>
<groupId>
org.springframework.cloud
</groupId>
<artifactId>
spring-cloud-starter-netflix-ribbon
</artifactId>
</dependency>
</dependencies>
</project>
在工程的配置文件指定服务的注册中心地址为http://localhost:8761/eureka/,程序名称为 service-ribbon,程序端口为8764。配置文件application.yml如下:
eureka:
client
:
serviceUrl:
defaultZone: http:
//localhost:8761/eureka/
server:
port:
8764
spring:
application:
name: service-ribbon
在工程的启动类中,通过@EnableDiscoveryClient向服务中心注册;并且向程序的ioc注入一个bean: restTemplate;并通过@LoadBalanced注解表明这个restRemplate开启负载均衡的功能。
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
publicclass ServiceRibbonApplication {
public
static
void
main
(String[] args) {
SpringApplication.run( ServiceRibbonApplication.class, args );
}
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return
new
RestTemplate();
}
}
写一个测试类HelloService,通过之前注入ioc容器的restTemplate来消费service-hi服务的“/hi”接口,在这里我们直接用的程序名替代了具体的url地址,在ribbon中它会根据服务名来选择具体的服务实例,根据服务实例在请求的时候会用具体的url替换掉服务名,代码如下:
@Service
publicclass HelloService {
@Autowired
RestTemplate restTemplate;
public
String
hiService(String name) {
return
restTemplate.getForObject(
"http://SERVICE-HI/hi?name="+name,String.class);
}
}
写一个controller,在controller中用调用HelloService 的方法,代码如下:
@RestController
publicclass HelloControler {
@Autowired
HelloService helloService;
@GetMapping
(value =
"/hi")
public
String
hi(@RequestParam String name) {
return
helloService.hiService( name );
}
}
在浏览器上多次访问http://localhost:8764/hi?name=forezp,浏览器交替显示:
hi forezp,i am from port:8762
hi forezp,i am from port:8763
这说明当我们通过调用restTemplate.getForObject(“http://SERVICE-HI/hi?name=“+name,String.class)方法时,已经做了负载均衡,访问了不同的端口的服务实例。
上一篇文章,讲述了如何通过RestTemplate+Ribbon去消费服务,这篇文章主要讲述如何通过Feign去消费服务。
Feign是一个声明式的伪Http客户端,它使得写Http客户端变得更简单。使用Feign,只需要创建一个接口并注解。它具有可插拔的注解特性,可使用Feign 注解和JAX-RS注解。Feign支持可插拔的编码器和解码器。Feign默认集成了Ribbon,并和Eureka结合,默认实现了负载均衡的效果。
简而言之:
继续用上一节的工程, 启动eureka-server,端口为8761; 启动service-hi 两次,端口分别为8762 、8773.
新建一个spring-boot工程,取名为serice-feign,在它的pom文件引入Feign的起步依赖spring-cloud-starter-feign、Eureka的起步依赖spring-cloud-starter-netflix-eureka-client、Web的起步依赖spring-boot-starter-web,代码如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>
4.0.0
</modelVersion>
<groupId>
com.forezp
</groupId>
<artifactId>
service-feign
</artifactId>
<version>
0.0.1-SNAPSHOT
</version>
<packaging>
jar
</packaging>
<name>
service-feign
</name>
<description>
Demo project for Spring Boot
</description>
<parent>
<groupId>
com.forezp
</groupId>
<artifactId>
sc-f-chapter3
</artifactId>
<version>
0.0.1-SNAPSHOT
</version>
</parent>
<dependencies>
<dependency>
<groupId>
org.springframework.cloud
</groupId>
<artifactId>
spring-cloud-starter-netflix-eureka-client
</artifactId>
</dependency>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-web
</artifactId>
</dependency>
<dependency>
<groupId>
org.springframework.cloud
</groupId>
<artifactId>
spring-cloud-starter-openfeign
</artifactId>
</dependency>
</dependencies>
</project>
在工程的配置文件application.yml文件,指定程序名为service-feign,端口号为8765,服务注册地址为http://localhost:8761/eureka/ ,代码如下:
eureka:
client
:
serviceUrl:
defaultZone: http:
//localhost:8761/eureka/
server:
port:
8765
spring:
application:
name: service-feign
在程序的启动类ServiceFeignApplication ,加上@EnableFeignClients注解开启Feign的功能:
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableFeignClients
publicclass ServiceFeignApplication {
public
static
void
main
(String[] args) {
SpringApplication.run( ServiceFeignApplication.class, args );
}
}
· 1
· 2
· 3
· 4
· 5
· 6
· 7
· 8
· 9
· 10
· 11
· 12
· 13
定义一个feign接口,通过@ FeignClient(“服务名”),来指定调用哪个服务。比如在代码中调用了service-hi服务的“/hi”接口,代码如下:
@FeignClient(
value=
"service-hi")
publicinterface
SchedualServiceHi {
@RequestMapping(
value=
"/hi",method = RequestMethod.GET)
String sayHiFromClientOne(@RequestParam(
value=
"name") String name);
}
在Web层的controller层,对外暴露一个”/hi”的API接口,通过上面定义的Feign客户端SchedualServiceHi 来消费服务。代码如下:
@RestController
publicclass HiController {
//编译器报错,无视。 因为这个Bean是在程序启动的时候注入的,编译器感知不到,所以报错。
@Autowired
SchedualServiceHi schedualServiceHi;
@GetMapping
(value =
"/hi")
public
String
sayHi(@RequestParam String name) {
return
schedualServiceHi.sayHiFromClientOne( name );
}
}
启动程序,多次访问http://localhost:8765/hi?name=forezp,浏览器交替显示:
hi forezp,i am from port:8762
hi forezp,i am from port:8763
在微服务架构中,根据业务来拆分成一个个的服务,服务与服务之间可以相互调用(RPC),在Spring Cloud可以用RestTemplate+Ribbon和Feign来调用。为了保证其高可用,单个服务通常会集群部署。由于网络原因或者自身的原因,服务并不能保证100%可用,如果单个服务出现问题,调用这个服务就会出现线程阻塞,此时若有大量的请求涌入,Servlet容器的线程资源会被消耗完毕,导致服务瘫痪。服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的“雪崩”效应。
为了解决这个问题,业界提出了断路器模型。
Netflix has created a library called Hystrix that implements the circuit breaker pattern. In a microservice architecture it is common to have multiple layers of service calls.
. —-摘自官网
Netflix开源了Hystrix组件,实现了断路器模式,SpringCloud对这一组件进行了整合。 在微服务架构中,一个请求需要调用多个服务是非常常见的,如下图:
较底层的服务如果出现故障,会导致连锁故障。当对特定的服务的调用的不可用达到一个阀值(Hystric 是5秒20次) 断路器将会被打开。
断路打开后,可用避免连锁故障,fallback方法可以直接返回一个固定值。
这篇文章基于上一篇文章的工程,首先启动上一篇文章的工程,启动eureka-server 工程;启动service-hi工程,它的端口为8762。
改造serice-ribbon 工程的代码,首先在pox.xml文件中加入spring-cloud-starter-netflix-hystrix的起步依赖:
<dependency>
<groupId>
org.springframework.cloud
</groupId>
<artifactId>
spring-cloud-starter-netflix-hystrix
</artifactId>
</dependency>
在程序的启动类ServiceRibbonApplication 加@EnableHystrix注解开启Hystrix:
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableHystrix
publicclass ServiceRibbonApplication {
public
static
void
main
(String[] args) {
SpringApplication.run( ServiceRibbonApplication.class, args );
}
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return
new
RestTemplate();
}
}
改造HelloService类,在hiService方法上加上@HystrixCommand注解。该注解对该方法创建了熔断器的功能,并指定了fallbackMethod熔断方法,熔断方法直接返回了一个字符串,字符串为”hi,”+name+”,sorry,error!”,代码如下:
@Service
publicclass HelloService {
@Autowired
RestTemplate restTemplate;
@HystrixCommand
(fallbackMethod =
"hiError")
public
String
hiService(String name) {
return
restTemplate.getForObject(
"http://SERVICE-HI/hi?name="+name,String.class);
}
public
String
hiError(String name) {
return
"hi,"
+name+
",sorry,error!";
}
}
·
启动:service-ribbon 工程,当我们访问http://localhost:8764/hi?name=forezp,浏览器显示:
hi forezp,i am from port:8762
此时关闭 service-hi 工程,当我们再访问http://localhost:8764/hi?name=forezp,浏览器会显示:
hi ,forezp,orry,error!
这就说明当 service-hi 工程不可用的时候,service-ribbon调用 service-hi的API接口时,会执行快速失败,直接返回一组字符串,而不是等待响应超时,这很好的控制了容器的线程阻塞。
Feign是自带断路器的,在D版本的Spring Cloud之后,它没有默认打开。需要在配置文件中配置打开它,在配置文件加以下代码:
feign.hystrix.enabled=true
基于service-feign工程进行改造,只需要在FeignClient的SchedualServiceHi接口的注解中加上fallback的指定类就行了:
@FeignClient(
value=
"service-hi",fallback = SchedualServiceHiHystric.class)
publicinterface
SchedualServiceHi {
@RequestMapping(
value=
"/hi",method = RequestMethod.GET)
String sayHiFromClientOne(@RequestParam(
value=
"name") String name);
}
SchedualServiceHiHystric需要实现SchedualServiceHi 接口,并注入到Ioc容器中,代码如下:
@Component
publicclass SchedualServiceHiHystric implements SchedualServiceHi {
@Override
public
String
sayHiFromClientOne(String name) {
return
"sorry "
+name;
}
}
启动四servcie-feign工程,浏览器打开http://localhost:8765/hi?name=forezp,注意此时service-hi工程没有启动,网页显示:
sorry forezp
打开service-hi工程,再次访问,浏览器显示:
hi forezp,i am from port:8762
这证明断路器起到作用了。
在微服务架构中,需要几个基础的服务治理组件,包括服务注册与发现、服务消费、负载均衡、断路器、智能路由、配置管理等,由这几个基础组件相互协作,共同组建了一个简单的微服务系统。一个简答的微服务系统如下图:
注意:A服务和B服务是可以相互调用的,作图的时候忘记了。并且配置服务也是注册到服务注册中心的。
在Spring Cloud微服务系统中,一种常见的负载均衡方式是,客户端的请求首先经过负载均衡(zuul、Ngnix),再到达服务网关(zuul集群),然后再到具体的服。,服务统一注册到高可用的服务注册中心集群,服务的所有的配置文件由配置服务管理(下一篇文章讲述),配置服务的配置文件放在git仓库,方便开发人员随时改配置。
Zuul的主要功能是路由转发和过滤器。路由功能是微服务的一部分,比如/api/user转发到到user服务,/api/shop转发到到shop服务。zuul默认和Ribbon结合实现了负载均衡的功能。
zuul有以下功能:
继续使用上一节的工程。在原有的工程上,创建一个新的工程。
其pom.xml文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>
4.0.0
</modelVersion>
<groupId>
com.forezp
</groupId>
<artifactId>
service-zuul
</artifactId>
<version>
0.0.1-SNAPSHOT
</version>
<packaging>
jar
</packaging>
<name>
service-zuul
</name>
<description>
Demo project for Spring Boot
</description>
<parent>
<groupId>
com.forezp
</groupId>
<artifactId>
sc-f-chapter5
</artifactId>
<version>
0.0.1-SNAPSHOT
</version>
</parent>
<dependencies>
<dependency>
<groupId>
org.springframework.cloud
</groupId>
<artifactId>
spring-cloud-starter-netflix-eureka-client
</artifactId>
</dependency>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-web
</artifactId>
</dependency>
<dependency>
<groupId>
org.springframework.cloud
</groupId>
<artifactId>
spring-cloud-starter-netflix-zuul
</artifactId>
</dependency>
</dependencies>
</project>
在其入口applicaton类加上注解@EnableZuulProxy,开启zuul的功能:
@SpringBootApplication
@EnableZuulProxy
@EnableEurekaClient
@EnableDiscoveryClient
publicclass ServiceZuulApplication {
public
static
void
main
(String[] args) {
SpringApplication.run( ServiceZuulApplication.class, args );
}
}
加上配置文件application.yml加上以下的配置代码:
eureka:
client:
serviceUrl:
defaultZone: http://localhost:
8761/eureka/
server:
port:
8769
spring:
application:
name: service-zuul
zuul:
routes:
api-a:
path: /api-a
/**
serviceId: service-ribbon
api-b:
path: /api-b/**
serviceId: service-feign
首先指定服务注册中心的地址为http://localhost:8761/eureka/,服务的端口为8769,服务名为service-zuul;以/api-a/ 开头的请求都转发给service-ribbon服务;以/api-b/开头的请求都转发给service-feign服务;
依次运行这五个工程;打开浏览器访问:http://localhost:8769/api-a/hi?name=forezp ;浏览器显示:
hi forezp,i am from port:8762
打开浏览器访问:http://localhost:8769/api-b/hi?name=forezp ;浏览器显示:
hi forezp,i am from port:8762
这说明zuul起到了路由的作用
zuul不仅只是路由,并且还能过滤,做一些安全验证。继续改造工程;
@Component
publicclass MyFilter extends ZuulFilter {
private
static
Logger log = LoggerFactory.getLogger(MyFilter.class);
@Override
public
String
filterType() {
return
"pre"
;
}
@Override
public
int
filterOrder
() {
return
0
;
}
@Override
public
boolean
shouldFilter
() {
return
true
;
}
@Override
public
Object
run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
log.info(String.format(
"%s >>> %s", request.getMethod(), request.getRequestURL().toString()));
Object accessToken = request.getParameter(
"token");
if
(accessToken ==
null) {
log.warn(
"token is empty");
ctx.setSendZuulResponse(
false);
ctx.setResponseStatusCode(
401);
try
{
ctx.getResponse().getWriter().write(
"token is empty");
}
catch(Exception e){}
return
null
;
}
log.info(
"ok");
return
null
;
}
}
这时访问:http://localhost:8769/api-a/hi?name=forezp ;网页显示:
token is empty
访问 http://localhost:8769/api-a/hi?name=forezp&token=22 ;
网页显示:
hi forezp,i am from port:8762
在上一篇文章讲述zuul的时候,已经提到过,使用配置服务来保存各个服务的配置文件。它就是Spring Cloud Config。
在分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件。在Spring Cloud中,有分布式配置中心组件spring cloud config ,它支持配置服务放在配置服务的内存中(即本地),也支持放在远程Git仓库中。在spring cloud config 组件中,分两个角色,一是config server,二是config client。
父maven工程省略,父pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>
4.0.0
</modelVersion>
<groupId>
com.forezp
</groupId>
<artifactId>
sc-f-chapter6
</artifactId>
<version>
0.0.1-SNAPSHOT
</version>
<packaging>
pom
</packaging>
<modules>
<module>
config-server
</module>
<module>
config-client
</module>
</modules>
<name>
sc-f-chapter6
</name>
<description>
Demo project for Spring Boot
</description>
<parent>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-parent
</artifactId>
<version>
2.0.3.RELEASE
</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>
UTF-8
</project.build.sourceEncoding>
<project.reporting.outputEncoding>
UTF-8
</project.reporting.outputEncoding>
<java.version>
1.8
</java.version>
<spring-cloud.version>
Finchley.RELEASE
</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-test
</artifactId>
<scope>
test
</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>
org.springframework.cloud
</groupId>
<artifactId>
spring-cloud-dependencies
</artifactId>
<version>
${spring-cloud.version}
</version>
<type>
pom
</type>
<scope>
import
</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-maven-plugin
</artifactId>
</plugin>
</plugins>
</build>
</project>
创建一个spring-boot项目,取名为config-server,其pom.xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>
4.0.0
</modelVersion>
<groupId>
com.forezp
</groupId>
<artifactId>
config-server
</artifactId>
<version>
0.0.1-SNAPSHOT
</version>
<packaging>
jar
</packaging>
<name>
config-server
</name>
<description>
Demo project for Spring Boot
</description>
<parent>
<groupId>
com.forezp
</groupId>
<artifactId>
sc-f-chapter6
</artifactId>
<version>
0.0.1-SNAPSHOT
</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-config-server
</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-maven-plugin
</artifactId>
</plugin>
</plugins>
</build>
</project>
在程序的入口Application类加上@EnableConfigServer注解开启配置服务器的功能,代码如下:
@SpringBootApplication
@EnableConfigServer
publicclass ConfigServerApplication {
public
static
void
main
(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
需要在程序的配置文件application.properties文件配置以下:
spring
.application.name=config-server
server
.port=
8888
spring
.cloud.config.server.git.uri=https://github
.com/forezp/SpringcloudConfig/
spring
.cloud.config.server.git.searchPaths=respo
spring
.cloud.config.label=master
spring
.cloud.config.server.git.username=
spring
.cloud.config.server.git.password=
如果Git仓库为公开仓库,可以不填写用户名和密码,如果是私有仓库需要填写,本例子是公开仓库,放心使用。
远程仓库https://github.com/forezp/SpringcloudConfig/ 中有个文件config-client-dev.properties文件中有一个属性:
foo = foo version 3
启动程序:访问http://localhost:8888/foo/dev
{"
name":
"foo","
profiles":
["dev"],"
label":
"master",
"
version":
"792ffc77c03f4b138d28e89b576900ac5e01a44b","
state":
null,"
propertySources":
[]}
证明配置服务中心可以从远程程序获取配置信息。
http请求地址和资源文件映射如下:
重新创建一个springboot项目,取名为config-client,其pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>
4.0.0
</modelVersion>
<groupId>
com.forezp
</groupId>
<artifactId>
config-client
</artifactId>
<version>
0.0.1-SNAPSHOT
</version>
<packaging>
jar
</packaging>
<name>
config-client
</name>
<description>
Demo project for Spring Boot
</description>
<parent>
<groupId>
com.forezp
</groupId>
<artifactId>
sc-f-chapter6
</artifactId>
<version>
0.0.1-SNAPSHOT
</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-config
</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-maven-plugin
</artifactId>
</plugin>
</plugins>
</build>
</project>
其配置文件bootstrap.properties:
spring
.application.name=config-client
spring
.cloud.config.label=master
spring
.cloud.config.profile=dev
spring
.cloud.config.uri= http://localhost:
8888/
server
.port=
8881
程序的入口类,写一个API接口“/hi”,返回从配置中心读取的foo变量的值,代码如下:
@SpringBootApplication
@RestController
publicclass ConfigClientApplication {
public
static
void
main
(String[] args) {
SpringApplication.run(ConfigClientApplication.class, args);
}
@Value
(
"${foo}")
String foo;
@RequestMapping
(value =
"/hi")
public
String
hi(){
return
foo;
}
}
打开网址访问:http://localhost:8881/hi,网页显示:
foo version 3
这就说明,config-client从config-server获取了foo的属性,而config-server是从git仓库读取的,如图:
一篇文章讲述了一个服务如何从配置中心读取文件,配置中心如何从远程git读取配置文件,当服务实例很多时,都从配置中心读取文件,这时可以考虑将配置中心做成一个微服务,将其集群化,从而达到高可用,架构图如下:
继续使用上一篇文章的工程,创建一个eureka-server工程,用作服务注册中心。
在其pom.xml文件引入Eureka的起步依赖spring-cloud-starter-netflix- eureka-server,代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>
4.0.0
</modelVersion>
<groupId>
com.forezp
</groupId>
<artifactId>
config-server
</artifactId>
<version>
0.0.1-SNAPSHOT
</version>
<packaging>
jar
</packaging>
<name>
config-server
</name>
<description>
Demo project for Spring Boot
</description>
<parent>
<groupId>
com.forezp
</groupId>
<artifactId>
sc-f-chapter7
</artifactId>
<version>
0.0.1-SNAPSHOT
</version>
</parent>
<dependencies>
<dependency>
<groupId>
org.springframework.cloud
</groupId>
<artifactId>
spring-cloud-starter-netflix-eureka-client
</artifactId>
</dependency>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-web
</artifactId>
</dependency>
<dependency>
<groupId>
org.springframework.cloud
</groupId>
<artifactId>
spring-cloud-config-server
</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-maven-plugin
</artifactId>
</plugin>
</plugins>
</build>
</project>
在配置文件application.yml上,指定服务端口为8889,加上作为服务注册中心的基本配置,代码如下:
server:
port:
8889
eureka:
instance:
hostname: localhost
client
:
registerWithEureka:
false
fetchRegistry:
false
serviceUrl:
defaultZone: http:
//${eureka.instance.hostname}:${server.port}/eureka/
入口类:
@EnableEurekaServer
@SpringBootApplication
publicclass EurekaServerApplication {
public
static
void
main
(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
在其pom.xml文件加上EurekaClient的起步依赖spring-cloud-starter-netflix-eureka-client,代码如下:
<dependencies>
<dependency>
<groupId>
org.springframework.cloud
</groupId>
<artifactId>
spring-cloud-starter-netflix-eureka-client
</artifactId>
</dependency>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-web
</artifactId>
</dependency>
<dependency>
<groupId>
org.springframework.cloud
</groupId>
<artifactId>
spring-cloud-config-server
</artifactId>
</dependency>
</dependencies>
配置文件application.yml,指定服务注册地址为http://localhost:8889/eureka/,其他配置同上一篇文章,完整的配置如下:
spring
.application.name=config-server
server
.port=
8888
spring
.cloud.config.server.git.uri=https://github
.com/forezp/SpringcloudConfig/
spring
.cloud.config.server.git.searchPaths=respo
spring
.cloud.config.label=master
spring
.cloud.config.server.git.username= your username
spring
.cloud.config.server.git.password= your password
eureka
.client.serviceUrl.defaultZone=http://localhost:
8889/eureka/
最后需要在程序的启动类Application加上@EnableEureka的注解。
将其注册微到服务注册中心,作为Eureka客户端,需要pom文件加上起步依赖spring-cloud-starter-netflix-eureka-client,代码如下:
<dependencies>
<dependency>
<groupId>
org.springframework.cloud
</groupId>
<artifactId>
spring-cloud-starter-config
</artifactId>
</dependency>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-web
</artifactId>
</dependency>
<dependency>
<groupId>
org.springframework.cloud
</groupId>
<artifactId>
spring-cloud-starter-netflix-eureka-client
</artifactId>
</dependency>
</dependencies>
配置文件bootstrap.properties,注意是bootstrap。加上服务注册地址为http://localhost:8889/eureka/
spring
.application.name=config-client
spring
.cloud.config.label=master
spring
.cloud.config.profile=dev
#spring.cloud.config.uri= http://localhost:8888/
eureka
.client.serviceUrl.defaultZone=http://localhost:
8889/eureka/
spring
.cloud.config.discovery.enabled=true
spring
.cloud.config.discovery.serviceId=config-server
server
.port=
8881
这时发现,在读取配置文件不再写ip地址,而是服务名,这时如果配置服务部署多份,通过负载均衡,从而高可用。
依次启动eureka-servr,config-server,config-client
访问网址:http://localhost:8889/
访问http://localhost:8881/hi,浏览器显示:
foo version 3
pring Cloud Bus 将分布式的节点用轻量的消息代理连接起来。它可以用于广播配置文件的更改或者服务之间的通讯,也可以用于监控。本文要讲述的是用Spring Cloud Bus实现通知微服务架构的配置文件的更改。
本文还是基于上一篇文章来实现。按照官方文档,我们只需要在配置文件中配置 spring-cloud-starter-bus-amqp ;这就是说我们需要装rabbitMq,点击rabbitmq下载。至于怎么使用 rabbitmq,搜索引擎下。
在pom文件加上起步依赖spring-cloud-starter-bus-amqp,完整的配置文件如下:
<dependencies>
<dependency>
<groupId>
org.springframework.cloud
</groupId>
<artifactId>
spring-cloud-starter-config
</artifactId>
</dependency>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-web
</artifactId>
</dependency>
<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-bus-amqp
</artifactId>
</dependency>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-actuator
</artifactId>
</dependency>
在配置文件application.properties中加上RabbitMq的配置,包括RabbitMq的地址、端口,用户名、密码。并需要加上spring.cloud.bus的三个配置,具体如下:
spring
.rabbitmq.host=localhost
spring
.rabbitmq.port=
5672
spring
.rabbitmq.username=guest
spring
.rabbitmq.password=guest
spring
.cloud.bus.enabled=true
spring
.cloud.bus.trace.enabled=true
management
.endpoints.web.exposure.include=bus-refresh
ConfigClientApplication启动类代码如下:
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@RestController
@RefreshScope
publicclass ConfigClientApplication {
/**
* http://localhost:8881/actuator/bus-refresh
*/
public
static
void
main
(String[] args) {
SpringApplication.run(ConfigClientApplication.class, args);
}
@Value
(
"${foo}")
String foo;
@RequestMapping
(value =
"/hi")
public
String
hi(){
return
foo;
}
}
依次启动eureka-server、confg-cserver,启动两个config-client,端口为:8881、8882。
访问http://localhost:8881/hi 或者http://localhost:8882/hi 浏览器显示:
foo version 3
这时我们去代码仓库将foo的值改为“foo version 4”,即改变配置文件foo的值。如果是传统的做法,需要重启服务,才能达到配置文件的更新。此时,我们只需要发送post请求:http://localhost:8881/actuator/bus-refresh,你会发现config-client会重新读取配置文件。如果是spring boot 2.0以下的版本,则调用http://localhost:8881/bus/refresh刷新。
这时我们再访问http://localhost:8881/hi 或者http://localhost:8882/hi 浏览器显示:
foo version 4
另外,/actuator/bus-refresh接口可以指定服务,即使用”destination”参数,比如 “/actuator/bus-refresh?destination=customers:**” 即刷新服务名为customers的所有服务。
此时的架构图:
当git文件更改的时候,通过pc端用post 向端口为8882的config-client发送请求/bus/refresh/;此时8882端口会发送一个消息,由消息总线向其他服务传递,从而使整个微服务集群都达到更新配置文件。
这篇文章主要讲述服务追踪组件zipkin,Spring Cloud Sleuth集成了zipkin组件。
Add sleuth to the classpath of a Spring Boot application (see below for Maven and Gradle examples), and you will see the correlation data being collected in logs, as long as you are logging requests.
—— 摘自官网
Spring Cloud Sleuth 主要功能就是在分布式系统中提供追踪解决方案,并且兼容支持了 zipkin,你只需要在pom文件中引入相应的依赖即可。
微服务架构上通过业务来划分服务的,通过REST调用,对外暴露的一个接口,可能需要很多个服务协同才能完成这个接口功能,如果链路上任何一个服务出现问题或者网络超时,都会形成导致接口调用失败。随着业务的不断扩张,服务之间互相调用会越来越复杂。
随着服务的越来越多,对调用链的分析会越来越复杂。它们之间的调用关系也许如下:
将Span和Trace在一个系统中使用Zipkin注解的过程图形化:
基本知识讲解完毕,下面我们来实战,本文的案例主要有三个工程组成:一个server-zipkin,它的主要作用使用ZipkinServer 的功能,收集调用数据,并展示;一个service-hi,对外暴露hi接口;一个service-miya,对外暴露miya接口;这两个service可以相互调用;并且只有调用了,server-zipkin才会收集数据的,这就是为什么叫服务追踪了。
在spring Cloud为F版本的时候,已经不需要自己构建Zipkin Server了,只需要下载jar即可,下载地址:
https://dl.bintray.com/openzipkin/maven/io/zipkin/java/zipkin-server/
也可以在这里下载:
链接: https://pan.baidu.com/s/1w614Z8gJXHtqLUB6dKWOpQ 密码: 26pf
下载完成jar 包之后,需要运行jar,如下:
java -jar zipkin-server-2.10.1-exec.jar
访问浏览器localhost:9494
在其pom引入起步依赖spring-cloud-starter-zipkin,代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>
4.0.0
</modelVersion>
<groupId>
com.forezp
</groupId>
<artifactId>
service-zipkin
</artifactId>
<version>
0.0.1-SNAPSHOT
</version>
<packaging>
jar
</packaging>
<name>
service-hi
</name>
<description>
Demo project for Spring Boot
</description>
<parent>
<groupId>
com.forezp
</groupId>
<artifactId>
sc-f-chapter9
</artifactId>
<version>
0.0.1-SNAPSHOT
</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-zipkin
</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-maven-plugin
</artifactId>
</plugin>
</plugins>
</build>
</project>
在其配置文件application.yml指定zipkin server的地址,头通过配置“spring.zipkin.base-url”指定:
server.port=8988
spring.zipkin.base-url=http://localhost:9411
spring.application.name=service-hi
通过引入spring-cloud-starter-zipkin依赖和设置spring.zipkin.base-url就可以了。
对外暴露接口:
package
com.forezp;
import brave
.sampler.Sampler;
import org
.springframework.beans.factory.annotation.Autowired;
import org
.springframework.boot.SpringApplication;
import org
.springframework.boot.autoconfigure.SpringBootApplication;
import org
.springframework.context.annotation.Bean;
import org
.springframework.web.bind.annotation.RequestMapping;
import org
.springframework.web.bind.annotation.RestController;
import org
.springframework.web.client.RestTemplate;
import java
.util.logging.Level;
import java
.util.logging.Logger;
@SpringBootApplication
@RestController
public class ServiceHiApplication {
public static void main(String[] args) {
SpringApplication
.run(ServiceHiApplication
.class, args)
;
}
private static final Logger LOG = Logger
.getLogger(ServiceHiApplication
.class.getName())
;
@Autowired
private RestTemplate restTemplate
;
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate()
;
}
@RequestMapping(
"/hi")
public String callHome(){
LOG
.log(Level
.INFO,
"calling trace service-hi ")
;
return restTemplate
.getForObject(
"http://localhost:8989/miya", String
.class)
;
}
@RequestMapping(
"/info")
public String info(){
LOG
.log(Level
.INFO,
"calling trace service-hi ")
;
return
"i‘m service-hi";
}
@Bean
public Sampler defaultSampler() {
return Sampler
.ALWAYS_SAMPLE
;
}
}
创建过程痛service-hi,引入相同的依赖,配置下spring.zipkin.base-url。
对外暴露接口:
package
com.forezp;
import brave
.sampler.Sampler;
import org
.springframework.beans.factory.annotation.Autowired;
import org
.springframework.boot.SpringApplication;
import org
.springframework.boot.autoconfigure.SpringBootApplication;
import org
.springframework.context.annotation.Bean;
import org
.springframework.web.bind.annotation.RequestMapping;
import org
.springframework.web.bind.annotation.RestController;
import org
.springframework.web.client.RestTemplate;
import java
.util.logging.Level;
import java
.util.logging.Logger;
@SpringBootApplication
@RestController
public class ServiceMiyaApplication {
public static void main(String[] args) {
SpringApplication
.run(ServiceMiyaApplication
.class, args)
;
}
private static final Logger LOG = Logger
.getLogger(ServiceMiyaApplication
.class.getName())
;
@RequestMapping(
"/hi")
public String home(){
LOG
.log(Level
.INFO,
"hi is being called")
;
return
"hi i‘m miya!";
}
@RequestMapping(
"/miya")
public String info(){
LOG
.log(Level
.INFO,
"info is being called")
;
return restTemplate
.getForObject(
"http://localhost:8988/info",String
.class)
;
}
@Autowired
private RestTemplate restTemplate
;
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate()
;
}
@Bean
public Sampler defaultSampler() {
return Sampler
.ALWAYS_SAMPLE
;
}
}
· 2
依次启动上面的工程,打开浏览器访问:http://localhost:9411/,会出现以下界面:
访问:http://localhost:8989/miya,浏览器出现:
i’m service-hi
再打开http://localhost:9411/的界面,点击Dependencies,可以发现服务的依赖关系:
点击find traces,可以看到具体服务相互调用的数据:
原文:https://www.cnblogs.com/cnwuhao/p/10586818.html