《深入理解 Spring Cloud 与微服务构建》第十四章 服务链路追踪 Spring Cloud Sleuth

文章目录

  • 《深入理解 Spring Cloud 与微服务构建》第十四章 服务链路追踪 Spring Cloud Sleuth
  • 一、为什么需要 Spring Cloud Sleuth
  • 二、基本术语
  • 三、案例讲解
    • 1.启动 Zipkin Server
    • 2.构建服务提供者
    • 3.构建服务消费者
    • 4.项目演示
  • 四、在链路数据中添加自定义数据
  • 五、使用 RabbitMQ 传输链路数据
  • 六、在 MySQL 数据库中存储链路数据
  • 七、在 ElasticSearch 中存储链路数据

一、为什么需要 Spring Cloud Sleuth

微服务架构是一个分布式架构,微服务系统按业务划分服务单元,一个微服务系统往往有很多个服务单元。由于服务单元数量众多,业务的复杂性较高,如果出现了错误和异常,很难去定位。主要体现在一个请求可能需要调用很多个服务,而内部服务的调用复杂性决定了问题难以定位。所以在微服务架构中,必须实现分布式链路追踪,去跟进一个请求到底有哪些服务参与,参与的顺序又是怎样的,从而达到每个请求的步骤清晰可见,出现问题能够快速定位的目的

Google 开源了 Dapper 链路追踪组件,并在 2010 年发表了论文《Dapper,a Large-Scale Distributed Systems Tracing Infrastructure》,这篇论文是业内实现链路追踪的标杆和理论基础,具有很高的参考价值

目前,常见的链路追踪组件有 Google 的 Dapper、Twitter 的 Zipkin,以及阿里的 Eagleeye(鹰眼)等,它们都是非常优秀的链路追踪开源组件

本章主要讲述如何在 Spring Cloud Sleuth 中集成 Zipkin。在 Spring Cloud Sleuth 中集成 Zipkin 非常简单,只需要引入相应的依赖并做相关的配置即可

二、基本术语

Spring Cloud Sleuth 采用了 Google 的开源项目 Dapper 的专业术语

Span
基本工作单元,发送一个远程调度任务就会产生一个 Span,Span 是用一个 64 位 ID 唯一标识的,Trace 是用另一个 64 位 ID 唯一标识的。Span 还包含了其它的信息,例如摘要、时间戳时间、Span 的 ID 以及进程 ID

Trace
由一系列 Span 组成的,呈树状结构。请求一个微服务系统的 API 接口,这个API 接口需要调用多个微服务单元,调用每个微服务单元都会产生一个新的 Span,所有由这个请求产生的 Span 组成了这个 Trace

Annotation
用于记录一个事件,一些核心注解用于定义一个请求的开始和结束,这些注解如下:

  • cs-Client Sent:客户端发送一个请求,这个注解描述了 Span 的开始
  • sr-Server Received:服务端获得请求并准备开始处理它,如果将其 sr 减去 cs 时间戳,便可得到网络传输的时间
  • ss-Server Sent:服务端发送响应,该注解表明请求处理的完成(当请求返回客户端),用 ss 的时间戳减去 sr 时间戳,便可以得到服务器请求的时间
  • cr-Client Received:客户端接收响应,此时 Span 结束,用 cr 的时间戳减去 cs 时间戳,便可以得到整个请求所消耗的时间

Spring Cloud Sleuth 提供了一套完整的链路解决方案,它可以结合 Zipkin,将链路数据发送到 Zipkin,并利用 Zipkin 来存储链路信息,也可以利用 Zipkin UI 来展示数据

那么什么是 Zipkin 呢?

Zipkin 是 Twitter 的一个开源项目,它基于 Google 的 Dapper 实现,被业界广泛使用。Zipkin 致力于收集分布式系统的链路数据,提供了数据持久化策略,也提供面向开发者的 API 接口,用于查询数据,还提供了 UI 组件帮助我们查看具体的链路信息

Zipkin 提供了可插拔式的数据存储方式,目前支持的数据存储由 In-Memory、MySQL、Cassandra 和 ElasticSearch

Zipkin 的架构如图所示:

它主要由 4 个核心组件构成:

  • Collector:链路数据收集器,主要用于处理从链路客户端发送过来的链路数据,将这些数据转换为 Zipkin 内部处理的 Span 格式,以支持后续的存储、分析和展示等功能
  • Storage:存储组件,用来存储接收到的链路数据,默认会将这些数据存储在内存中,同时支持多种存储策略,比如将链路数据存储在 M有SQL、Cassandra 和 ElasticSearch 中
  • RESTful API:API 组件,它是面向开发者的,提供外部访问 API 接口,可以通过这些 API 接口来自定义展示界面
  • WEB UI:UI 组件,基于 API 接口实现的上层应用,用户利用 UI 组件可以很方便地查询和分析链路数据

三、案例讲解

本章的案例一共有 4 个工程,基本信息如表所示:

应用名 端口 作用
eureka-server 8761 注册中心
eureka-client 8763 服务提供者、链路追踪客户端
eureka-client-feign 8765 服务消费者、链路追踪客户端
Zipkin(以 Jar 包的形式启动) 9411 链路追踪服务端

和之前的工程一样,采用 Maven 工程的多 Module 形式。新建一个主 Maven 工程,在主 Maven 工程的 pom 文件里指定 Spring Boot 的版本为 2.1.0,Spring Cloud 版本为 Greenwich。RELEASE。eureka-server 工程作为服务注册中心,zipkin-server 作为链路追踪服务中心,负责存储来南路数据,Spring Cloud 从 Edgware 版本开始,使用 Jar 包的形式启动,Jar 包需要从官方下载,用户不需要额外创建工程。eureka-client 工程是一个服务提供者,对外暴露 API 接口,同时它也作为链路追踪客户端,产生链路数据,并将链路数据传递给服务消费者。eureka-feign-client 作为服务消费者,调用 eureka-client 提供的服务,同时它也作为链路追踪客户端,产生链路数据,并将链路数据上传给链路追踪服务中心 zipkin-server

1.启动 Zipkin Server

eureka-server 的构建流程就不再一一演示,直接启动 Zipkin Server

在 Spring Cloud Dalston 版本中,zipkin-server 可以通过引入相关依赖的方式构建工程。从 Edgware 版本之后,这一方式改变为强制采用官方提供的 Jar 包的形式启动。因此用户需要下载官方提供的 Jar 包,使用 Git Bash 通过以下命令一键启动

curl -sSl https://zipkin.io/quickstart.sh | bash -s
java -jar zipkin.jar

上述第一行命令会从 Zipkin 官网下载官方的 Jar 包,第二行命令用来运行 zipkin.jar 文件

通过 java -jar zipkin.jar 的方式启动之后,在浏览器上访问 localhost:9411,显示的界面如图所示:

2.构建服务提供者

在主 Maven 工程下建一个 Module 工程,取名为 eureka-client,作为服务提供者,对外暴露 API 接口。user-service 工程的 pom 文件继承了主 Maven 工程的 pom 文件,并引入了 Eureka 的起步依赖 spring-cloud-starter-netflix-eureka-client、WEB 起步依赖 spring-boot-starter-web,以及 Zipkin 的起步依赖 spring-cloud-starter-zipkin 和 sleuth 的起步依赖 spring-cloud-starter-sleuth,代码如下:

<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-sleuth</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-zipkin</artifactId></dependency>
</dependencies>

在程序的配置文件 application.yml 中,指定程序名为 eureka-client,端口号为 8763,服务注册地址 http://localhost:8761/eureka/,Zipkin Server 地址为 http://localhost:9411。spring-sleuth.sample.percentage 为 1.0,即以 100% 的概率将链路的数据上传给 Zipkin Server,在默认情况下,该值为 0.1。另外,需要通过配置 spring.sleuth.web.client.enable 为 “true” 来开启 Sleuth 的功能,具体配置文件代码如下:

server:port: 8763spring:application:name: eureka-clientsleuth:web:client:enabled: truesampler:probability: 1.0  #设置采样比例,1.0 即全部都需要,默认为 0.1zipkin:base-url: http://localhost:9411/  #指定 zipkin 服务器地址eureka:client:service-url: defaultZone: http://lcoalhost:8761/eureka/

在 HiController 类建一个 “/hi” 的 API 接口,对外提供服务,代码如下:

package com.sisyphus.controller;import brave.Tracer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
public class HiController {@AutowiredTracer tracer;@Value("${server.port}")String port;public String home(@RequestParam String name){tracer.currentSpan().tag("name","sisyphus");return "hi " + name + ",i am from port:" + port;}
}

最后作为 Eureka Client,需要在程序的启动类 EurekaClientApplication 加上 @EnableEurekaClient 注解,开启 Eureka Client 的功能

3.构建服务消费者

新建一个名为 eureka-feign-client 的工程,这个工程作为服务消费者,使用 FeignClient 来消费服务;同时作为 Zipkin 客户端,将链路数据上传给 Zipkin Server。在工程的 pom 文件中除了需要继承主 Maven 工程的 pom 文件以外,还需引入如下的依赖,代码如下:

<dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><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-sleuth</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-zipkin</artifactId></dependency>
</dependencies>

在工程的配置文件 application.yml 中,配置程序名为 eureka-feign-client,端口号为 8765,服务注册地址为 http://localhost:8761/eureka/。另外,设置 spring.sleuth.web.client.enable 为 “true” 来使 WEB 开启 Sleuth 功能;spring.sleuth.sampler.probability 可以被设置为小数;最大值为 1.0,标识链路数据 100% 收集到 zipkin-server;当设置为 0.1 时,表示以 10% 的概率收集链路数据;spring.zipkin.base-url 设置 zipkin-server 的地址。配置代码如下:

server:port: 8765spring:application:name: eureka-feign-clientsleuth:web:client:enabled: truesampler:probability: 1.0zipkin:base-url: http://localhost:9411/  #指定 Zipkin 服务器的地址eureka:client:service-url:defaultZone: http://localhost:8761/eureka/

在程序的启动类 EurekaFeignClientApplication 上加 @EnableEurekaClient 和 @EnableFeignClients 注解,代码如下:

package com.sisyphus;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class EurekaFeignClientApplication {public static void main(String[] args) {SpringApplication.run(EurekaFeignClientApplication.class, args);}
}

服务消费者通过 FeignClient 消费服务提供者提供的服务,FeignClient 的代码如下:

package com.sisyphus.feign;import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;@FeignClient("eureka-client")
@Component
public interface EurekaClientFeign {@GetMapping("/hi")String sayHiFromClientEureka(@RequestParam("name") String name);
}

在 Service 层,通过调用 EurekaClientFeign 来消费服务,代码如下:

package com.sisyphus.service;import com.sisyphus.feign.EurekaClientFeign;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class HiService {@AutowiredEurekaClientFeign eurekaClientFeign;public String sayHi(String name){return eurekaClientFeign.sayHiFromClientEureka(name);}
}

在 Controller 层,对外暴露一个 API 接口,通过 HiService 来调用 eureka-client 的服务,代码如下:

package com.sisyphus.controller;import com.sisyphus.service.HiService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
public class HiController {@AutowiredHiService hiService;@GetMapping("/hi")public String sayHi(@RequestParam(defaultValue = "sisyphus", required = false) String name){return hiService.sayHi(name);}
}

4.项目演示

完整的项目搭建完毕,依次启动 eureka-server、eureka-client 和 eureka-feign-client,并启动 zipkin.jar。在浏览器上访问 http://localhost:8765/hi(如果报错,是因为服务与发现需要一定的时间,需要耐心等待几十秒),浏览器显示如下:

hi sisyphus,i am from port:8763

访问 http://localhost:9411,即访问 Zipkin 的展示界面,如图所示:

这个界面用于展示 Zipkin Server 收集的链路数据,可以根据服务名、开始时间、结束时间、请求消耗的时间等条件来查找:

从图中可知请求的调用情况,例如请求的调用时间、消耗时间,以及请求调用的链路情况等

单击导航栏的 “依赖” 按钮,可以查看服务的依赖关系。在本案例中,eureka-feign-client 消费了 eureka-client 提供的 API 服务,这两个服务的依赖关系如图所示:

四、在链路数据中添加自定义数据

现在需要实现这样一个功能:在链路数据中加上请求的操作人。本案例在 eureka-client 服务中实现。在 API 接口逻辑方法中,通过 Tracer 的 currentSpan 方法获取当前的链路数据的 Span,通过 tag 方法加上自定义的数据。在本案例中加上链路的操作人的代码如下:

package com.sisyphus.controller;import brave.Tracer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
public class HiController {@AutowiredTracer tracer;@Value("${server.port}")String port;@GetMapping("/hi")public String home(@RequestParam String name){tracer.currentSpan().tag("name","sisyphus");return "hi " + name + ",i am from port:" + port;}
}

五、使用 RabbitMQ 传输链路数据

在上述案例中,最终 eureka-feign-client 收集的数据是通过 HTTP 上传给 zipkin-server 的。在 Spring Cloud Sleuth 中支持消息组件来传输链路数据,本节使用 RabbitMQ 来传输链路数据。使用 RabbitMQ 前需要安装 RaabitMQ 程序

链路数据是通过 RabbitMQ 来传输的,那么 zipkin-server 是如何直到 RabbitMQ 的地址,又如何监听收到的链路数据呢?zipkin-server 是通过读取环境变量来获取 RabbitMQ 的配置信息的,这需要在程序启动时通过环境变量的形式将配置信息注入环境中, 然后 zipkin-server 从环境变量中读取,可配置的属性如下表所示

属性 环境变量 描述
zipkin.collector.rabbitmq.address RABBIT_ADDRESSES 用逗号分隔的 RabbitMQ 地址列表,例如 loaclhost:5672,localhost:5673
zipkin.collector.rabbitmq.password RABBIT_PASSWORD 连接到 RabbitMQ 时使用的密码,默认为 guest
zipkin.collector.rabbitmq.username RABBIT_USER 连接到 RabbitMQ 时使用的用户名,默认为 guest
zipkin.collector.rabbitmq.virtual-host RABBIT_VIRTUAL_HOST 使用的 RabbitMQ virtual host,默认为 /
zipkin.collector.rabbitmq.use-ssl RABBIT_USE_SSL 设置为 “true”,则用 SSL 的方式与 RabbitMQ 建立链接
zipkin.collector.rabbitmq.concurrency RABBIT_CONCURRENCY 并发消费者数量,默认为 1
zipkin.collector.rabbitmq.connection-timeout RABBIT_CONNECTION_TIMEOUT 建立连接时的超时时间,默认为 60000 毫秒,即 1 分钟
zipkin.collector.rabbitmq.queue RABBIT_QUEUE 从中获取 span 信息的队列,默认为 zipkin

比如用以下命令启动 Zipkin 的服务:

RABBIT_ADDRESSES=localhost java -jar zipkin.jar

上述命令等同于以下命令

java -jar zipkin.jar --zipkin.collector.rabbitmq.addressed=localhost

用上述两条命令中的任何一条重新启动 Zipkin 服务。接着改造 Zipkin Client(包括 gateway-service 工程和 user-service 工程),在它们的 pom 文件中加上 spring-cloud-stream-binder-rabbit 的依赖,代码如下:

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>

同时在配置文件 application.yml 中加上 RabbitMQ 的配置,并去掉 spring.zipkin.base-url 的配置,代码如下;

spring:rabbitmq:host: loaclhostusername: guestpassword: guestport: 5672

这样就可以将链路的上传数据方式从 HTTP 改为消息代理组件 RabbitMQ

六、在 MySQL 数据库中存储链路数据

在上面的例子中,Zipkin Server 将数据存储在内存中,一旦程序重启,之前的链路数据会全部丢失,那么怎么将链路数据存储起来呢?Zipkin 支持将链路数据存储在 MySQL、ElasticSearch 和 Cassandra 数据库中

首先需要初始化 Zipkin 存储在 MySQL 数据的脚本,具体可到 GitHub 中搜索 Zipkin 进行查看。本案例中的源码资源文件夹中也包含该数据库脚本

在数据库中初始化上述脚本后,需要让 Zipkin 连接上数据库。Zipkin 连接数据库同连接 RabbitMQ 一样,都是从环境变量中读取的。Zipkin 连接数据库的属性所对应的环境变量如下表所示:

属性 环境变量 描述
zipkin.torage.type STORAGE_TYPE 默认为 mem,即内存,其它可支持的为 Cassandra、Cassandra3、ElasticSearch、MySQL
zipkin.torage.mysql.host MYSQL_HOST 数据库的 host,默认为 localhost
zipkin.torage.mysql.port MYSQL_TCP_PORT 数据库的端口,默认为 3006
zipkin.torage.mysql.username MYSQL_USER 连接数据库的用户名,默认为空
zipkin.torage.mysql.password MYSQL_PASS 连接数据库的密码,默认为空
zipkin.torage.mysql.db MYSQL_DB Zipkin 使用的数据库名,默认为 zipkin
zipkin.torage.mysql.max-active MYSQL_MAX_CONNECTIONS 最大连接数,默认为 10

使用以下命令启动,Zipkin 就可以启动时连接数据库,并将链路数据存储在数据库中,命令如下:

STORAGE_TYPE=mysql MYSQL_HOST=localhost MYSQL_TCP_PORT=3306 MYSQL_USER=root MYSQL_PASS=123456 MYSQL_DB=zipkin java -jar zipkin.jar

上述命令等同于以下命令:

java -jar zipkin.jar --zipkin.torage.type=mysql --zipkin.torage.mysql.host=localhost --zipkin.torage.mysql.port=3306 --zipkin.torage.mysql.username=root -- zipkin.torage.mysql.password=123456

使用上面的命令启动 zipkin.jar 工程,然后在浏览器上访问 http://localhost:8765/hi,再访问 http://localhost:9411/zipkin/,就可以看到链路数据。这时去数据库查看数据,也可以看到存储再数据库的链路数据

这时重启应用 zipkin.jar,再次在浏览器上访问 httpL//localhost:9411/zipkin/,仍然可以得到之前的结果,证明链路数据存储在数据库中,而不是内存中

七、在 ElasticSearch 中存储链路数据

在高并发的情况下,使用 MySQL 存储链路数据显然不合理,这时可以选择使用 ElasticSearch 存储。读者需要自行安装 ElasticSearch 和 Kibana,下载地址为 http://www.elastic.co/products/elasticsearch。安装完成后启动,其中 ElasticSearch 的默认端口号为 9200,Kibana 的默认端口号为 5601

同理,Zipkin 连接 ElasticSearch 也是从环境变量中读取的连接属性,ElasticSearch 香瓜的呢环境变量和对应的属性如下表所示:

属性 环境变量 描述
zipkin.torage.elasticsearch.hosts ES_HOSTS 默认为空
zipkin.torage.elasticsearch.pipeline ES_PIPELINE 默认为空
zipkin.torage.elasticsearch.max-requests ES_MAX_REQUESTS 默认为 64
zipkin.torage.elasticsearch.timeout ES_TIMEOUT 默认为 10s
zipkin.torage.elasticsearch.index ES_INDEX 默认为 zipkin
zipkin.torage.elasticsearch.date-separator ES_DATE_SEPARATOR 默认为 zipkin
zipkin.torage.elasticsearch.index-shards ES_INDEX_SHARD 默认为 5
zipkin.torage.elasticsearch.index-replicas ES_INDEX_REPLICAS|ES_INDEX_REPLICAS 默认为 1
zipkin.torage.elasticsearch.username ES_USERNAME 默认为空
zipkin.torage.elasticsearch.password ES_PASSWORD 默认为空

使用以下命令启动 Zipkin,Zipkin 就将链路数据存储在 ElasticSearch 中,启动命令如下:

STORAGE_TYPE=elasticsearch ES_HOSTS=http://localhost:9200 ES_INDEX=zipkin java -jar zipkin.jar

启动完成后,在浏览器上访问 httpL//localhost:8765/hi,再访问 http://localhost:9411/zipkin/,可以看到链路数据,这时链路数据存储在 ElasticSearch 中

《深入理解 Spring Cloud 与微服务构建》第十四章 服务链路追踪 Spring Cloud Sleuth相关推荐

  1. 《深入理解 Spring Cloud 与微服务构建》第十一章 服务网关

    <深入理解 Spring Cloud 与微服务构建>第十一章 服务网关 文章目录 <深入理解 Spring Cloud 与微服务构建>第十一章 服务网关 一.服务网关简介 二. ...

  2. 《深入理解 Spring Cloud 与微服务构建》第六章 服务注册和发现 Eureka

    <深入理解 Spring Cloud 与微服务构建>第六章 服务注册和发现 Eureka 文章目录 <深入理解 Spring Cloud 与微服务构建>第六章 服务注册和发现 ...

  3. 《深入理解 Spring Cloud 与微服务构建》第四章 Dubbo

    <深入理解 Spring Cloud 与微服务构建>第四章 Dubbo 文章目录 <深入理解 Spring Cloud 与微服务构建>第四章 Dubbo 一.Dubbo 简介 ...

  4. 实训3:配置网络负载平衡服务_Linux系统配置及服务管理第十四章网络管理实战2...

    一.物理层 1.1信号分类 a.模拟信号 不断变换的物理量,适用于电话网络. b.数字信号 两种恒定物理量,适用于计算机网络,优点是抗噪音和抗衰减比较强. 1.2介质 a.双绞线 EIA/TIA568 ...

  5. 《深入理解 Spring Cloud 与微服务构建》第十七章 使用 Spring Cloud OAuth2 保护微服务系统

    <深入理解 Spring Cloud 与微服务构建>第十七章 使用 Spring Cloud OAuth2 保护微服务系统 文章目录 <深入理解 Spring Cloud 与微服务构 ...

  6. 《深入理解 Spring Cloud 与微服务构建》第十三章 配置中心 Spring Cloud Config

    <深入理解 Spring Cloud 与微服务构建>第十三章 配置中心 Spring Cloud Config 文章目录 <深入理解 Spring Cloud 与微服务构建>第 ...

  7. 《深入理解 Spring Cloud 与微服务构建》第十二章 服务注册和发现 Consul

    <深入理解 Spring Cloud 与微服务构建>第十二章 服务注册和发现 Consul 文章目录 <深入理解 Spring Cloud 与微服务构建>第十二章 服务注册和发 ...

  8. 《深入理解 Spring Cloud 与微服务构建》第七章 负载均衡 Ribbon

    <深入理解 Spring Cloud 与微服务构建>第七章 负载均衡 Ribbon 文章目录 <深入理解 Spring Cloud 与微服务构建>第七章 负载均衡 Ribbon ...

  9. 《深入理解 Spring Cloud 与微服务构建》第五章 Kubernetes

    <深入理解 Spring Cloud 与微服务构建>第五章 Kubernetes 文章目录 <深入理解 Spring Cloud 与微服务构建>第五章 Kubernetes 一 ...

最新文章

  1. c\c++ 随机数函数
  2. OpenCASCADE:函数机制的使用
  3. TCP三次握手、四次挥手、socket,tcp,http三者之间的区别和原理
  4. LeetCode 1681. 最小不兼容性(回溯+剪枝)
  5. Vue.js项目去除url中的#/ - 解决篇
  6. [ES6] 细化ES6之 -- async函数
  7. 学习笔记12JS异步请求
  8. 生成对抗式网络 GAN及其衍生CGAN、DCGAN、WGAN、LSGAN、BEGAN等原理介绍、应用介绍及简单Tensorflow实现
  9. springmvc06 静态资源
  10. fir.im Weekly - 聊聊 Google 开发者大会
  11. Java基础语法三 输入输出
  12. 改MD5防止你的视频被百度云秒和谐
  13. 宿舍计算机管理制度,【宿舍门禁系统能统计夜不归宿吗】_学生宿舍门禁系统使用管理规定(试行)...
  14. 批处理创建隐秘的加密文件夹,是男人就把秘密藏起来
  15. 利用注册表清除Office Word文档杀手病毒
  16. 读书《AB实验:科学归因与增长的利器》(刘玉凤)
  17. 树莓派卸载系统自带应用增大硬盘空间
  18. 2021全球十大外盘期货交易平台排名
  19. 微服务实战之春云与刀客(一)—— 微服务开篇
  20. 利用爬虫下载批量图片

热门文章

  1. 南林计算机课程表,南京林业大学课程表.doc
  2. java webservice报文过长_年薪百万IT大牛分享及(京东,阿里,58)Java初中高级765道面试题...
  3. PyChram使用技巧
  4. Excel向数据库插入数据(执行一次只需连接一次)-batch简单使用
  5. LeetCode OJ Symmetric Tree 判断是否为对称树(AC代码)
  6. 习题:输出九九乘法表
  7. android在程序中打开另一个程序
  8. 让数据库操作变成非阻塞的
  9. android studio第三方调试,Android Studio直接运行调试签名包
  10. android音频采集时延,有关音视频采样率帧率以及 Duration 的那些事