Spring Cloud 是如何实现服务治理的

Table of Contents

建议提前阅读

Spring Cloud Commons 之服务治理浅析

Spring 在设计的时候,通常会考虑方便扩展和消除样板代码,在 Spring Clond 同样存在这样的设计。

在 Spring Cloud 体系中,Spring Cloud Commons 是最重要的一个项目,其中定义了服务注册、服务发现、复杂均衡相关的接口以及一些公共组件,通过看这个项目,我们可以简单的理解一下 Spring Cloud 注册发现的核心流程。

Spring Clond Commons 项目中提供了如下的项目结构(在这里省略了部分代码文件和结构)

└── src

├── main

│ ├── java

│ │ └── org

│ │ └── springframework

│ │ └── cloud

│ │ ├── client

│ │ │ ├── DefaultServiceInstance.java

│ │ │ ├── ServiceInstance.java Spring Cloud 对服务实例信息的定义

│ │ │ ├── discovery 服务发现相关

│ │ │ │ ├── DiscoveryClient.java

│ │ │ │ ├── EnableDiscoveryClient.java

│ │ │ │ ├── EnableDiscoveryClientImportSelector.java

│ │ │ │ ├── ManagementServerPortUtils.java

│ │ │ │ ├── ReactiveDiscoveryClient.java

│ │ │ │ ├── composite

│ │ │ │ │ ├── CompositeDiscoveryClient.java

│ │ │ │ │ ├── CompositeDiscoveryClientAutoConfiguration.java

│ │ │ │ │ └── reactive

│ │ │ │ │ ├── ReactiveCompositeDiscoveryClient.java

│ │ │ │ │ └── ReactiveCompositeDiscoveryClientAutoConfiguration.java

│ │ │ │ ├── health 健康检查相关

│ │ │ │ ├── DiscoveryClientHealthIndicator.java

│ │ │ │ ├── DiscoveryClientHealthIndicatorProperties.java

│ │ │ │ ├── DiscoveryCompositeHealthContributor.java

│ │ │ │ ├── DiscoveryHealthIndicator.java

│ │ │ │ └── reactive

│ │ │ │ ├── ReactiveDiscoveryClientHealthIndicator.java

│ │ │ │ ├── ReactiveDiscoveryCompositeHealthContributor.java

│ │ │ │ └── ReactiveDiscoveryHealthIndicator.java

│ │ │ ├── loadbalancer 这下面是负载均衡相关逻辑

│ │ │ └── serviceregistry 服务注册相关

│ │ │ ├── AbstractAutoServiceRegistration.java

│ │ │ ├── AutoServiceRegistration.java

│ │ │ ├── AutoServiceRegistrationAutoConfiguration.java

│ │ │ ├── AutoServiceRegistrationConfiguration.java

│ │ │ ├── AutoServiceRegistrationProperties.java

│ │ │ ├── Registration.java

│ │ │ ├── ServiceRegistry.java

│ │ │ ├── ServiceRegistryAutoConfiguration.java

│ │ ├── commons

│ │ ├── httpclient http 工厂类,在配置中可以选择使用 Apache Http 还是 OKHttp

│ │ │ ├── ApacheHttpClientFactory.java

│ │ │ └── OkHttpClientFactory.java

│ │ └── util

│ │ ├── IdUtils.java 通过这工具类来生成实例 id

│ │ └── InetUtils.java Spring Cloud 就是通过这个工具类是获取服务项目的 ip 地址的

│ └── resources

│ └── META-INF

│ ├── additional-spring-configuration-metadata.json

│ └── spring.factories

└── test

├── java 测试相关代码

在项目结构中可以看出各个部分对应的源码,在服务治理中,首先是服务信息 ServiceInstance , 其中包括

服务名 ServiceId 这个就是我们类似的 xxx-server (spring.application.name)

服务实例唯一标识符 InstanceId

host

port

一些扩展信息 metadata, 这个主要用来提供给三方实现增加以下扩展信息

// 为了缩短篇幅,删除了一些注释

public interface ServiceInstance {

default String getInstanceId() {

return null;

}

String getServiceId();

String getHost();

int getPort();

boolean isSecure();

URI getUri();

Map getMetadata();

default String getScheme() {

return null;

}

}

服务注册

Registration 是 Spring Cloud 提供的一个注册实现

public interface Registration extends ServiceInstance {

// 这里面是真没有代码

}

服务注册的实际接口是 ServiceRegistry

public interface ServiceRegistry {

/**

* Registers the registration. A registration typically has information about an

* instance, such as its hostname and port.

* @param registration registration meta data

*/

void register(R registration);

/**

* Deregisters the registration.

* @param registration registration meta data

*/

void deregister(R registration);

/**

* Closes the ServiceRegistry. This is a lifecycle method.

*/

void close();

/**

* Sets the status of the registration. The status values are determined by the

* individual implementations.

* @param registration The registration to update.

* @param status The status to set.

* @see org.springframework.cloud.client.serviceregistry.endpoint.ServiceRegistryEndpoint

*/

void setStatus(R registration, String status);

/**

* Gets the status of a particular registration.

* @param registration The registration to query.

* @param The type of the status.

* @return The status of the registration.

* @see org.springframework.cloud.client.serviceregistry.endpoint.ServiceRegistryEndpoint

*/

T getStatus(R registration);

}

通过实现 ServiceRegistry 即可完成一个简单服务注册功能

服务发现

在 discovery 下存在两个服务发现定义接口 DiscoveryClient 和 ReactiveDiscoveryClient

其提供了如下功能:

获取所有的服务名称

根据服务名称获取对应的服务实例列表

public interface DiscoveryClient extends Ordered {

/**

* Default order of the discovery client.

*/

int DEFAULT_ORDER = 0;

/**

* A human-readable description of the implementation, used in HealthIndicator.

* @return The description.

*/

String description();

/**

* Gets all ServiceInstances associated with a particular serviceId.

* @param serviceId The serviceId to query.

* @return A List of ServiceInstance.

*/

List getInstances(String serviceId);

/**

* @return All known service IDs.

*/

List getServices();

/**

* Default implementation for getting order of discovery clients.

* @return order

*/

@Override

default int getOrder() {

return DEFAULT_ORDER;

}

}

通过实现 DiscoveryClient 即可完成服务发现

健康检测

ReactiveDiscoveryClientHealthIndicator 提供了健康检测功能

从 DiscoveryClient 中获取所有的服务名列表

根据服务名列表获取对应的服务实例列表

对每个实例进行健康检测,如果响应成功则 UP 否则为 DOWN

public class ReactiveDiscoveryClientHealthIndicator

implements ReactiveDiscoveryHealthIndicator, Ordered, ApplicationListener> {

private final ReactiveDiscoveryClient discoveryClient;

private final DiscoveryClientHealthIndicatorProperties properties;

private final Log log = LogFactory.getLog(ReactiveDiscoveryClientHealthIndicator.class);

private AtomicBoolean discoveryInitialized = new AtomicBoolean(false);

private int order = Ordered.HIGHEST_PRECEDENCE;

public ReactiveDiscoveryClientHealthIndicator(ReactiveDiscoveryClient discoveryClient,

DiscoveryClientHealthIndicatorProperties properties) {

this.discoveryClient = discoveryClient;

this.properties = properties;

}

@Override

public void onApplicationEvent(InstanceRegisteredEvent> event) {

if (this.discoveryInitialized.compareAndSet(false, true)) {

this.log.debug("Discovery Client has been initialized");

}

}

@Override

public Mono health() {

if (this.discoveryInitialized.get()) {

return doHealthCheck();

}

else {

return Mono.just(

Health.status(new Status(Status.UNKNOWN.getCode(), "Discovery Client not initialized")).build());

}

}

private Mono doHealthCheck() {

// @formatter:off

return Mono.justOrEmpty(this.discoveryClient)

.flatMapMany(ReactiveDiscoveryClient::getServices)

.collectList()

.defaultIfEmpty(emptyList())

.map(services -> {

ReactiveDiscoveryClient client = this.discoveryClient;

String description = (this.properties.isIncludeDescription())

? client.description() : "";

return Health.status(new Status("UP", description))

.withDetail("services", services).build();

})

.onErrorResume(exception -> {

this.log.error("Error", exception);

return Mono.just(Health.down().withException(exception).build());

});

// @formatter:on

}

@Override

public String getName() {

return discoveryClient.description();

}

@Override

public int getOrder() {

return this.order;

}

public void setOrder(int order) {

this.order = order;

}

}

通过上面的接口定义和自带的健康检测逻辑可以看出做一个服务治理需要实现的最简单的逻辑

实现 ServiceRegistry 功能

实现 DiscoveryClient 功能

Spring Cloud Consul 实现

实现 ServiceRegistry 功能

在 Spring Cloud Consul 中,首先自定义了 Registration 的实现

其中 NewService 为 Consul 定义的一些服务实例信息

public class ConsulRegistration implements Registration {

private final NewService service;

private ConsulDiscoveryProperties properties;

public ConsulRegistration(NewService service, ConsulDiscoveryProperties properties) {

this.service = service;

this.properties = properties;

}

public NewService getService() {

return this.service;

}

protected ConsulDiscoveryProperties getProperties() {

return this.properties;

}

public String getInstanceId() {

return getService().getId();

}

public String getServiceId() {

return getService().getName();

}

@Override

public String getHost() {

return getService().getAddress();

}

@Override

public int getPort() {

return getService().getPort();

}

@Override

public boolean isSecure() {

return this.properties.getScheme().equalsIgnoreCase("https");

}

@Override

public URI getUri() {

return DefaultServiceInstance.getUri(this);

}

@Override

public Map getMetadata() {

return getService().getMeta();

}

}

NewService

其包含了服务的基本信息和 Consul 本身提供一些特有功能如:Tags、Check

// 删除了通用的 getter、setter、toString 方法

public class NewService {

@SerializedName("ID")

private String id;

@SerializedName("Name")

private String name;

@SerializedName("Tags")

private List tags;

@SerializedName("Address")

private String address;

@SerializedName("Meta")

private Map meta;

@SerializedName("Port")

private Integer port;

@SerializedName("EnableTagOverride")

private Boolean enableTagOverride;

@SerializedName("Check")

private NewService.Check check;

@SerializedName("Checks")

private List checks;

public NewService() {

}

public static class Check {

@SerializedName("Script")

private String script;

@SerializedName("DockerContainerID")

private String dockerContainerID;

@SerializedName("Shell")

private String shell;

@SerializedName("Interval")

private String interval;

@SerializedName("TTL")

private String ttl;

@SerializedName("HTTP")

private String http;

@SerializedName("Method")

private String method;

@SerializedName("Header")

private Map> header;

@SerializedName("TCP")

private String tcp;

@SerializedName("Timeout")

private String timeout;

@SerializedName("DeregisterCriticalServiceAfter")

private String deregisterCriticalServiceAfter;

@SerializedName("TLSSkipVerify")

private Boolean tlsSkipVerify;

@SerializedName("Status")

private String status;

@SerializedName("GRPC")

private String grpc;

@SerializedName("GRPCUseTLS")

private Boolean grpcUseTLS;

public Check() {

}

}

}

ConsulServiceRegistry 实现 ServiceRegistry

public class ConsulServiceRegistry implements ServiceRegistry {

private static Log log = LogFactory.getLog(ConsulServiceRegistry.class);

private final ConsulClient client;

private final ConsulDiscoveryProperties properties;

private final TtlScheduler ttlScheduler;

private final HeartbeatProperties heartbeatProperties;

public ConsulServiceRegistry(ConsulClient client, ConsulDiscoveryProperties properties, TtlScheduler ttlScheduler,

HeartbeatProperties heartbeatProperties) {

this.client = client;

this.properties = properties;

this.ttlScheduler = ttlScheduler;

this.heartbeatProperties = heartbeatProperties;

}

@Override

public void register(ConsulRegistration reg) {

log.info("Registering service with consul: " + reg.getService());

try {

// 同样是通过 consul 提供的 api 接口进行服务注册

this.client.agentServiceRegister(reg.getService(), this.properties.getAclToken());

NewService service = reg.getService();

if (this.heartbeatProperties.isEnabled() && this.ttlScheduler != null && service.getCheck() != null

&& service.getCheck().getTtl() != null) {

this.ttlScheduler.add(reg.getInstanceId());

}

}

catch (ConsulException e) {

if (this.properties.isFailFast()) {

log.error("Error registering service with consul: " + reg.getService(), e);

ReflectionUtils.rethrowRuntimeException(e);

}

log.warn("Failfast is false. Error registering service with consul: " + reg.getService(), e);

}

}

@Override

public void deregister(ConsulRegistration reg) {

if (this.ttlScheduler != null) {

this.ttlScheduler.remove(reg.getInstanceId());

}

if (log.isInfoEnabled()) {

log.info("Deregistering service with consul: " + reg.getInstanceId());

}

this.client.agentServiceDeregister(reg.getInstanceId(), this.properties.getAclToken());

}

@Override

public void close() {

}

@Override

public void setStatus(ConsulRegistration registration, String status) {

if (status.equalsIgnoreCase(OUT_OF_SERVICE.getCode())) {

this.client.agentServiceSetMaintenance(registration.getInstanceId(), true);

}

else if (status.equalsIgnoreCase(UP.getCode())) {

this.client.agentServiceSetMaintenance(registration.getInstanceId(), false);

}

else {

throw new IllegalArgumentException("Unknown status: " + status);

}

}

// 服务实例状态

@Override

public Object getStatus(ConsulRegistration registration) {

String serviceId = registration.getServiceId();

Response> response = this.client.getHealthChecksForService(serviceId,

HealthChecksForServiceRequest.newBuilder().setQueryParams(QueryParams.DEFAULT).build());

List checks = response.getValue();

for (Check check : checks) {

if (check.getServiceId().equals(registration.getInstanceId())) {

if (check.getName().equalsIgnoreCase("Service Maintenance Mode")) {

return OUT_OF_SERVICE.getCode();

}

}

}

return UP.getCode();

}

}

ConsulDiscoveryClient 实现 DiscoveryClient

在发现逻辑中也是通过 consul 提供的 api 接口进行查询

public class ConsulDiscoveryClient implements DiscoveryClient {

private final ConsulClient client;

private final ConsulDiscoveryProperties properties;

public ConsulDiscoveryClient(ConsulClient client, ConsulDiscoveryProperties properties) {

this.client = client;

this.properties = properties;

}

@Override

public String description() {

return "Spring Cloud Consul Discovery Client";

}

@Override

public List getInstances(final String serviceId) {

return getInstances(serviceId, new QueryParams(this.properties.getConsistencyMode()));

}

public List getInstances(final String serviceId, final QueryParams queryParams) {

List instances = new ArrayList<>();

addInstancesToList(instances, serviceId, queryParams);

return instances;

}

private void addInstancesToList(List instances, String serviceId, QueryParams queryParams) {

HealthServicesRequest.Builder requestBuilder = HealthServicesRequest.newBuilder()

.setPassing(this.properties.isQueryPassing()).setQueryParams(queryParams)

.setToken(this.properties.getAclToken());

String queryTag = this.properties.getQueryTagForService(serviceId);

if (queryTag != null) {

requestBuilder.setTag(queryTag);

}

HealthServicesRequest request = requestBuilder.build();

Response> services = this.client.getHealthServices(serviceId, request);

for (HealthService service : services.getValue()) {

instances.add(new ConsulServiceInstance(service, serviceId));

}

}

public List getAllInstances() {

List instances = new ArrayList<>();

Response>> services = this.client

.getCatalogServices(CatalogServicesRequest.newBuilder().setQueryParams(QueryParams.DEFAULT).build());

for (String serviceId : services.getValue().keySet()) {

addInstancesToList(instances, serviceId, QueryParams.DEFAULT);

}

return instances;

}

@Override

public List getServices() {

CatalogServicesRequest request = CatalogServicesRequest.newBuilder().setQueryParams(QueryParams.DEFAULT)

.setToken(this.properties.getAclToken()).build();

return new ArrayList<>(this.client.getCatalogServices(request).getValue().keySet());

}

@Override

public int getOrder() {

return this.properties.getOrder();

}

}

总结

简要的 Spring Cloud Consul 的服务治理逻辑大致如此,当然 Spring Cloud Consul 还要处理大量的细节,代码还是很多的

在 Spring Cloud 体系中 Consul 并不提供服务请求转发的功能,只是提供对服务信息的保存、查询、健康检测剔除功能

参考

java什么是服务治理平台_Java | Spring Cloud 是如何实现服务治理的相关推荐

  1. Spring Cloud Alibaba 大型微服务项目实战

    作者介绍 程序员十三,多年一线开发经验,历任高级开发工程师.后端主程.技术部门主管等职位.同时也是开源项目的爱好者和贡献者.掘金优秀作者.CSDN 博客专家.实体图书作者.专栏作者.视频讲师. 小册介 ...

  2. java springcloud版b2b2c社交电商spring cloud分布式微服务-docker-feign(四)

    简介 Spring Cloud大型企业分布式微服务云构建的B2B2C电子商务平台源码请加企鹅求求:一零三八七七四六二六.上一节,我们讨论了怎么通过,restTemlate调用cloud的生产者,实现起 ...

  3. 关于SpringCloud微服务云架构构建B2B2C电子商务平台之- Spring Cloud集成项目简介(三)...

    2019独角兽企业重金招聘Python工程师标准>>> Spring Cloud集成项目有很多,下面我们列举一下和Spring Cloud相关的优秀项目,我们的企业架构中用到了很多的 ...

  4. Spring Cloud Alibaba 分布式微服务高并发数据平台化(中台)思想+多租户saas企业开发架构技术选型和设计方案

    基于Spring Cloud Alibaba 分布式微服务高并发数据平台化(中台)思想+多租户saas设计的企业开发架构,支持源码二次开发.支持其他业务系统集成.集中式应用权限管理.支持拓展其他任意子 ...

  5. 微服务等于Spring Cloud?了解微服务架构和框架

    作者:TIM XU 来源:https://xiaoxubeii.github.io/articles/microservices-architecture-introduction/ 微服务初探 什么 ...

  6. spring cloud微服务分布式云架构 - Spring Cloud集成项目简介

    Spring Cloud集成项目有很多,下面我们列举一下和Spring Cloud相关的优秀项目,我们的企业架构中用到了很多的优秀项目,说白了,也是站在巨人的肩膀上去整合的.在学习Spring Clo ...

  7. 用Spring Cloud Alibaba开发微服务会更香吗?

    关注DD,除了前沿消息,还有每周福利哦 Spring Cloud Alibaba致力于提供微服务开发的一站式解决方案,它是Spring Cloud组件被植入Alibaba元素之后的产物. 利用Spri ...

  8. Spring Cloud Alibaba 新一代微服务解决方案

    本篇是「跟我学 Spring Cloud Alibaba」系列的第一篇, 每期文章会在公众号「架构进化论」进行首发更新,欢迎关注. 1.Spring Cloud Alibaba 是什么 Spring ...

  9. 微服务等于 Spring Cloud?了解微服务架构和框架

    点击上方"芋道源码",选择"设为星标" 管她前浪,还是后浪? 能浪的浪,才是好浪! 每天 8:55 更新文章,每天掉亿点点头发... 源码精品专栏 原创 | J ...

最新文章

  1. 尖峰 mysql 源码方向_MySql轻松入门系列————第一站 从源码角度轻松认识mysql整体框架图...
  2. GAN眼中的图像翻译(附神奇歌单)
  3. 为什么现在大家喜欢用3*3小卷积?
  4. 《HelloGitHub》第 19 期
  5. 《MATLAB图像处理375例》——1.8 MATLAB程序流程控制结构
  6. 论文翻译《Object-Level Ranking: Bringing Order to Web Objects》
  7. 大数据人工智能时代,这个行业终于爆发了!
  8. 现实世界 机器学习_公司沟通分析简介现实世界的机器学习方法
  9. 数据结构与算法(七) 栈以及栈的应用
  10. php warning: directive,安装Composer PHP Warning: copy(): SSL operation failed with code
  11. 动态规划 —— 阶乘
  12. ADO.NET编程(3)在内存中对DataTable进行增/删/改操作
  13. 时间选择插件jquery.timepickr
  14. 电工技师技能实训考核装置QY-W601C
  15. Premiere Pro之视频转场效果(四)
  16. 手把手教你怎样用U盘装系统
  17. 知乎上看到的关于异步双核的解释
  18. 搭建 lepus 监控
  19. dotnet 配置 Gitlab 的 Runner 做 CI 自动构建
  20. 论文笔记—LeGO-LOAM: Lightweight and Ground-Optimized Lidar Odometry and Mapping on Variable Terrain

热门文章

  1. malloc/free和new/delete的区别
  2. mybatisplus修改单个属性_Mybatis Plus 中 参数传递的优化之路
  3. Ubuntu 14.04安装JDK1.8.0_25与配置环境变量
  4. CF788B Weird journey
  5. R400换XP 所有驱动程序安装一览表
  6. Redis允许远程访问
  7. MySQl 8.0.13版本修改密码
  8. python获取城市天气数据案例
  9. 带着问题学 Kubernetes 架构!
  10. 面试题热个身:5 亿整数的大文件,来排个序?