从零开始搭建支持客户端负载均衡的SpringCloud分布式系统(超详细)

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();
    }
}
  • 第三步:实现三种负载均衡算法。
  1. 随机算法,思路比较简单,首先通过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();
}
  1. 轮询算法,该算法通过让所有的服务实例每次执行一个请求,之后换另外一个服务实例即可,同时在调用的同时记录该实例的调用次数并存入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();
}
  1. 一致性哈希算法,该算法在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 程序啦!!如果想要代码的话,可以点击这里啦~~

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值