1. 搭建目标
设计并完成一个支持客户端负载均衡的简单分布式系统,系统说明如下:
(1)系统由一个客户端进程和多个服务端进程(至少3个)组成;
(2)客户端实现随机、轮询和一致性hash等负载均衡算法;
(3)服务端提供一个REST风格的HTTP接口用于客户端调用。 客户端提供一个REST风格的HTTP接口用于启动调用,该接口可以指定负载均衡策略以及调用次数;
(4)设计并实现一种监控形式,可实时查看服务端不同实例被调用次数;
(5)代码中需要考虑可能出现的异常及合理的异常处理。
2. 具体设计
2.1 概述
在本项目中,针对需要实现的功能,系统设计的架构如下图所示:
系统中主要由三部分组成:EurekaServer,EurekaClient和ServiceProvider。其中EurekaServer为服务注册中心,使用的是SpringCloud中的Netflix Eureka组件,通过此组件可以将ServicePovider创建的服务实例在启动的时候会向配置的注册中心注册自己的信息,如图类似于<name, address>的结构,Eureka客户端向服务端注册自己的服务信息,同时将服务端的服务信息缓存到本地。之后客户端会和服务端周期性的进行心跳交互,以更新服务租约和服务信息。由于本系统需要通过客户端参数动态改变负载均衡策略,因此没有使用Ribbon来实现,而是在EurekaClient中可以通过DiscoveryClient手动实现客户端负载均衡,在DisoveryClient的Eureka实现中可以获取到服务的不同实例,进而调用不同负载均衡算法即可,其中包括随机算法、轮询算法以及一致性哈希算法,之后基于某种负载均衡算法,请求其中一个服务提供者的实例,进而客户端可以通过RestTemplate进行HTTP接口调用。
2.2 模块设计
由于本课题中主要由三个模块组成,其中项目的目录结构如下图所示:
- htsc-eurekaclient 中提供了eureka的客户端,在这个客户端中,提供了外部调用的接口,同时在此模块中实现了三种负载均衡的算法。
- htsc-eurekaserver担任服务注册中心的角色,新创建的服务可以在eureka server中注册自己的服务信息,同时也为eureka client提供全部的服务注册信息。
- htsc-serviceprovider 为服务提供方,启动服务后会在eureka server中对创建的服务实例进行注册,eureka client可以调用该模块提供的服务。
- zipkin-server-2.10.1-exec.jar 官网提供的分布式链路跟踪工具,可以通过在服务中引入zipkin,进而对模块之间调用进行监控。
2.3 所用技术
在本课题中,主要用到以下技术来实现不同的功能:
- 环境:Java 1.8 + Maven3.6.2 + SpringBoot 2.3.2 + SpringCloud Hoxton.SR6;
- SpringCloud Netflix Eureka实现服务注册与发现;
- SpringCloud DiscoveryClient实现客户端随机、轮询和一致性哈希的负载均衡算法;
- Spring Cloud Sleuth和Zipkin实现分布式链路跟踪;
- 自定义接口实现服务实例的调用次数展示。
3. 实现步骤
3.1 创建项目目录结构
第一步: 首先创建一个空的项目,之后在空项目中创建各个模块,空项目创建完毕之后,需要添加模块,如下所示添加eurekaserver模块,在该模块中可以通过Spring Initializr来创建,同时引入Eureka Server 依赖,具体过程如下所示:
该模块创建完毕之后,以类似的形式创建其他两个模块,不同的是在其他两个模块中需要引入的依赖是Eureka Client。创建完毕之后需要引入zipkin-server-2.10.1-exec.jar,这样整个项目的目录结构将如前所述示意图一样。
3.2 配置eurekaserver模块
- 第一步:配置pom.xml文件,在该文件中需要整合 eureka 服务端,整合监控信息,同时在WebUI界面中需要添加登录认证功能,具体如下所示:
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.com.htsc</groupId>
<artifactId>htsc-eurekaserver</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>htsc-eurekaserver</name>
<description>Demo project for Spring Boot</description>
<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>Hoxton.SR6</spring-cloud.version>
</properties>
<dependencies>
<!-- 整合 eureka 服务端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!-- 整合监控信息 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--添加登录验证功能-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- springboot test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</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>
- 第二步:修改application.yml文件,在这里需要配置eureka server服务端的端口号(本次设置的端口号是8761),通过eureka.client.registerWithEureka:false以及fetchRegistry:false 来表明自己是一个eureka server,同时也配置自定义的用户名和密码,通过此用户名和密码来登录eureka提供的UI界面,增加安全性(本系统用户名和密码分别为:admin:admin122),具体代码如下:
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
# 安全认证的配置
security:
basic:
enabled: true
user:
name: admin
password: admin122
- 第三步:为执行代码入口HtscEurekaserverApplication添加@EnableEurekaServer注解,开启EurekaServer,具体代码如下:
@EnableEurekaServer
@SpringBootApplication
public class HtscEurekaserverApplication {
public static void main(String[] args) {
SpringApplication.run(HtscEurekaserverApplication.class, args);
}
}
- 第四步:由于默认情况下,当Spring Security位于类路径上时,它将要求在每次向应用程序发送请求时都发送一个有效的CSRF令牌。Eureka客户机通常不会拥有一个有效的跨站点请求伪造令牌(CSRF),您需要禁用/ Eureka /**端点的这个请求,因此编写WebSecurityConfig类来忽略这些请求,具体代码如下:
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().ignoringAntMatchers("/eureka/**");
super.configure(http);
}
}
- 第五步:启动该应用,在浏览器中输入http://localhost:8761/,首先会跳转到如下所示登录页面:
输入配置的用户名和密码,即可登录到Eureka查看页面,如下图所示可以看到,此时没有应用启动,因此提示No instances available。
3.3 实现htsc-serviceprovider模块
在该模块中,我们提供了客户端调用的接口,通过调用该接口可以查看被调用的端口号,等信息,通过配置不同的端口,启动不同的进程进而形成服务集群,为后面的负载均衡提供服务。
- 第一步:引入相关依赖,其中包括Eureka客户端依赖,SpringBoot web依赖,链路监控依赖,SpringBoot actuator监控信息完善依赖,具体文件如下所示:
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.com.htsc</groupId>
<artifactId>htsc-serviceprovider</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>htsc-serviceprovider</name>
<description>Service Provider</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR6</spring-cloud.version>
</properties>
<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>
<!-- actuator监控信息完善 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 实时监控 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
<!--devtools热部署-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-devtools</artifactId>-->
<!-- <optional>true</optional>-->
<!-- <scope>true</scope>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</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>
<finalName>${project.artifactId}</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
**第二步:**配置application.yml文件,需要配置服务端口号,在本系统中通过更改端口来启动不同进程,实现集群效果;其次是配置注册中心的信息,当服务启动之后可以向注册中心进行注册。同时也需要配置Sleuth进行分布式链路跟踪。具体代码如下所示:
# 服务启动端口号
server:
port: 8002
spring:
application:
name: service1
# 分布式链路跟踪
sleuth:
web:
enabled: true
sampler:
probability: 1.0
zipkin:
base-url: http://localhost:9411
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
# 显示服务相关信息
info:
app.name: service1
company.name: www.htsc.com.cn
build.artifactId: $project.artifactId$
build.version: $project.version$
- 第三步:为HtscServiceProviderApplication添加注解@EnableEurekaClient,使其成为Eureka客户端。具体代码为:
@EnableEurekaClient
@SpringBootApplication
public class HtscServiceproviderApplication {
public static void main(String[] args) {
SpringApplication.run(HtscServiceproviderApplication.class, args);
}
}
- 第四步:编写服务的接口,实现调用过程中显示被调用的服务所属端口,返回的是ServiceInfo对象,其中包含了服务的名称以及端口号。具体代码如下:
@RestController
public class ServiceController {
@Value("${server.port}")
private String serverPort;
@Value("${spring.application.name}")
private String serverHost;
@RequestMapping("/getService")
public ServiceInfo getService() {
ServiceInfo serviceInfo = new ServiceInfo();
serviceInfo.setHost(serverHost);
serviceInfo.setPort(serverPort);
return serviceInfo;
}
}
当启动多个服务的时候,登录EurekaServer端即可在页面看到服务信息如下所示:
通过Postman进行接口调用时,响应如下:
3.4 实现htsc-eurekaclient模块
在该模块中,客户端提供了调用自身的REST风格接口,在接口中会可以通过请求的参数指定响应的负载均衡策略,实现客户端的负载均衡,其中实现的算法有:随机算法、轮询算法以及一致性哈希算法。同时参数中也包含了调用次数。另外在客户端中也实现了实时监控的功能,通过每次调用对请求进行记录,进而统计各个服务实例被调用的次数。
第一步:添加所需依赖,配置pom文件,其中包含整合web依赖,分布式链路跟踪,eureka客户端依赖,具体代码如下:
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.com.htsc</groupId>
<artifactId>htsc-eurekaclient</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>htsc-eurekaclient</name>
<description>Eureka client</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR6</spring-cloud.version>
</properties>
<dependencies>
<!-- spring-cloud-starter-netflix-eureka-client 依赖中已经包含ribbon-->
<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-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.8.RELEASE</version>
<scope>compile</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>
- 第二步:为启动类添加@EnableEurekaClient注解,同时将RestTemplate类注解为Bean,由于需要自己修改算法,因此原来通过Ribbon来实现的负载均衡就不用了,具体代码如下:
@EnableEurekaClient
@SpringBootApplication
public class HtscEurekaclientApplication {
public static void main(String[] args) {
SpringApplication.run(HtscEurekaclientApplication.class, args);
}
// 解决RestTemplate 找不到原因 应该把restTemplate注册SpringBoot容器中 @bean
@Bean
// 由于需要自己修改算法,因此原来通过Ribbon来实现的负载均衡就不用了
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
- 第三步:实现三种负载均衡算法。
- 随机算法,思路比较简单,首先通过DiscoveryClient获取到服务实例列表,之后从所有服务实例中随机选取其中一个返回并调用即可,同时在调用的同时记录该实例的调用次数并存入ConcurrentHashMap中,为后续实现查看个实例调用次数做准备,具体实现在getInstanceByRandomRule()方法中。具体代码如下所示:
private String getInstanceByRandomRule() {
// 获取所有的实例列表
List<ServiceInstance> instances = null;
instances = discoveryClient.getInstances("service");
if (instances == null || instances.size() <= 0) {
return null;
}
// 获取服务器集群个数
int instanceSize = instances.size();
// 获取0, instanceSize之间的随机数,即调用实例的下标
int serviceIndex = random.nextInt(instanceSize);
// 该实例原来的调用次数
int callAccount = requestMap.get(serviceIndex) == null? 0: requestMap.get(serviceIndex);
requestMap.put(serviceIndex, ++callAccount);
requestCount.incrementAndGet();
return instances.get(serviceIndex).getUri().toString();
}
- 轮询算法,该算法通过让所有的服务实例每次执行一个请求,之后换另外一个服务实例即可,同时在调用的同时记录该实例的调用次数并存入ConcurrentHashMap中,为后续实现查看个实例调用次数做准备,具体实现在getInstanceByRoundRule()方法中。
private String getInstanceByRoundRule() {
// 获取所有的实例列表
List<ServiceInstance> instances = discoveryClient.getInstances("service");
if (instances == null || instances.size() <= 0) {
return null;
}
// 获取服务器集群个数
int instanceSize = instances.size();
// 获取要调用实例的下标
int serviceIndex = requestCount.get() % instanceSize;
// 该实例原来的调用次数
int callAccount = requestMap.get(serviceIndex) == null? 0: requestMap.get(serviceIndex);
requestMap.put(serviceIndex, ++callAccount);
requestCount.incrementAndGet();
return instances.get(serviceIndex).getUri().toString();
}
- 一致性哈希算法,该算法在Ribbon中没有被实现,但是其是一种应对故障的较为好的一种算法,本系统中实现的是没有虚拟节点的一致性哈希算法,同时在调用的同时记录该实例的调用次数并存入ConcurrentHashMap中,为后续实现查看个实例调用次数做准备,具体实现可以通过方法getInstanceByConsistenceHashRule()来实现。
private String getInstanceByConsistentHashRule() {
// 获取所有的实例列表
List<ServiceInstance> instances = discoveryClient.getInstances("service");
if (instances == null || instances.size() <= 0) {
return null;
}
// 获取服务器集群个数
int instanceSize = instances.size();
// 服务器集合
String[] servers = new String[instanceSize];
//key表示服务器的hash值,value表示服务器
SortedMap<Integer, String> sortedMap = new TreeMap<Integer, String>();
for(int i=0; i< instanceSize; i++){
servers[i] = instances.get(i).getUri().toString();
sortedMap.put(ConsistenceUtils.getHash(servers[i]), servers[i]);
}
List<ServiceInstance> thisInstances = discoveryClient.getInstances("eurakaclient");
String thisInstance = thisInstances.get(0).getUri().toString();
int hash = ConsistenceUtils.getHash(thisInstance);
//得到大于该Hash值的所有Map
SortedMap<Integer, String> subMap = sortedMap.tailMap(hash);
if(subMap.isEmpty()){
//如果没有比该key的hash值大的,则从第一个node开始
Integer i = sortedMap.firstKey();
//返回对应的服务器
return sortedMap.get(i);
}else{
//第一个Key就是顺时针过去离node最近的那个结点
Integer i = subMap.firstKey();
//返回对应的服务器
return subMap.get(i);
}
}
- 第四步:实现调用客户端的服务接口,接口的说明如下所示:
具体实现过程为:首先判断出入的参数时是否合法,如果不合法则抛出异常,如果合法则根据time判断调用的次数,然后在每次调用的时候判断是需要使用那种负载均衡算法,之后调用特定的负载均衡算法,实现代码如下所示:
@RequestMapping("/clientApi/{time}/{loadbalance}")
public List<ServiceInfo> clientApi(@PathVariable("time") int time, @PathVariable("loadbalance") int loadbalance){
List<ServiceInfo> list = new ArrayList<>();
// 判断输入的参数是否合法
if(time <= 0){
throw new RuntimeException("输入的调用次数应该大于等于1!!");
}
if(loadbalance <= 0 || loadbalance > 3){
throw new RuntimeException("负载均衡参数不存在!!");
}
StringBuffer stringBuffer = new StringBuffer();
// 循环执行请求
for(int i=0 ; i < time ; i++) {
String instancesUrl = null;
ServiceInfo serviceInfo = new ServiceInfo();
if(loadbalance == 1){
// 获取对应服务器远程调用地址
String instanceAddr = getInstanceByRandomRule();
if(instanceAddr == null ){
throw new RuntimeException("未找到服务实例!!");
}
serviceInfo.setHost(instanceAddr.split(":")[1]);
serviceInfo.setPort(instanceAddr.split(":")[2]);
instancesUrl = instanceAddr + "/getService";
System.out.println("RandomRule instancesUrl:" + instancesUrl);
}else if(loadbalance == 2){
String instanceAddr = getInstanceByRoundRule();
if(instanceAddr == null ){
throw new RuntimeException("未找到服务实例!!");
}
serviceInfo.setHost(instanceAddr.split(":")[1]);
serviceInfo.setPort(instanceAddr.split(":")[2]);
instancesUrl = instanceAddr + "/getService";
System.out.println("RoundRule instancesUrl:" + instancesUrl);
}else if(loadbalance == 3){
String instanceAddr = getInstanceByConsistentHashRule();
if(instanceAddr == null ){
throw new RuntimeException("未找到服务实例!!");
}
serviceInfo.setHost(instanceAddr.split(":")[1]);
serviceInfo.setPort(instanceAddr.split(":")[2]);
instancesUrl = instanceAddr + "/getService";
System.out.println("ConsistentHashRule instancesUrl:" + instancesUrl);
}
// 2.可以直接使用httpclient技术实现远程调用
list.add(serviceInfo);
}
return list;
}
- 第五步:实现服务调用次数的查看接口,接口的介绍如下:
具体实现过程为:由于在调用的时候就已经记录响应的请求数目,因此在此接口中将遍历Map中的所有实例,并将每个实例的调用次数用CallTimeInfo实体类进行包装,实现代码如下所示:
// 实例调用次数查看接口
@RequestMapping("/instancesInfo")
public List<CallTimeInfo> requestInfo() {
List<CallTimeInfo> list = new ArrayList<>();
if(requestMap == null || requestMap.entrySet() == null || requestMap.size() == 0){
return list;
}
for (Map.Entry<String, Integer> entry: requestMap.entrySet()) {
CallTimeInfo callTimeInfo = new CallTimeInfo();
callTimeInfo.setInstanceName(entry.getKey());
callTimeInfo.setTime(entry.getValue());
list.add(callTimeInfo);
}
return list;
}
3.5 功能展示
1、开启zipkin-sever,此模块官方推荐使用jar包,因此直接运行jar包即可;
2、启动EurekaServer,并进行管理页面的登录;
3、启动4个ServiceProvider服务,端口号分别是8001,8002,8003,8004;
4、启动EurekaClient,端口号为8000;
5、调用客户端提供的接口,根据随机负载均衡策略得到的结果展示如下,可以看出调用的接口端口号是随机的:
6、调用实例调用次数查看接口,总次数为10次,结果如下所示:
7、调用客户端提供的接口,根据轮询负载均衡策略得到的结果展示如下,可以看出调用的接口端口号是符合轮询规则的:
8、调用实例调用次数查看接口,此时的总次数应为20,结果如下所示:
9、调用客户端提供的接口,根据一致性哈希负载均衡策略得到的结果展示如下:
10、调用实例调用次数查看接口,此时的总次数应为30,结果如下所示:
11、通过Zipkin web页面可以在①出设置想要查看时间范围内的调用情况,本次展示的是2天内的调用情况,在②处可以设置最大展示多少item, 本次设置的是1000,在③处可以查看调用的实际情况,例如在这次请求中总共调用了10次service服务,11次eurekaclient服务,如下所示:
12、当点击之后,可以看到调用的具体时延等信息,本次请求关联了2个服务,深度为2等,同时也展示了调用不同服务的时延信息,具体如下所示:
13、由于在微服务架构中经常会出现服务之间的相互调用,在依赖分析中可以查看服务之间的依赖关系,同时也可以对某个服务的调用总次数进行查看如下图所示:
4. 小结
自此算是弄好了SpringCloud的 hello word 程序啦!!如果想要代码的话,可以点击这里啦~~