[Java EE]Spring Boot 与 Spring Cloud的关系/过去-现在-未来

2023-05-19,,

1 微服务架构

定义

微服务 (Microservices) 是一种软件架构风格
它是以专注于单一责任功能的小型功能区块 (Small Building Blocks) 为基础,
利用模块化的方式组合出复杂的大型应用程序,
各功能区块使用与语言无关 (Language-Independent/Language agnostic) 的 API 集相互通信。

目前业界跟微服务相关的开发平台和框架更是不胜枚举:Spring CloudService FabricLinkerdEnvoyIstio ...

单体应用架构 => 微服务应用架构

从【单体应用】向【微服务架构】的转型,已成为企业数字化转型的主流趋势。

在微服务模式下,企业内部服务少则几个到几十个,多则上百个,每个服务一般都以集群方式部署。

微服务架构的问题?

分布式架构的4个核心问题?
1 这么多服务,客户端如何访问? - 客户端访问 => API网关
2 这么多服务,服务(端)之间如何通信? - 服务端通信 => HTTP/RPC调用
3 这么多服务,如何治理? - 微服务治理 => 服务注册/发现
4 服务挂了,该怎么办? - 容灾 => 熔断机制

2 解决方案

Spring Cloud 是一套生态,就是来解决上述分布式架构的问题的。

1 Spring Cloud的生态中存在众多的解决方案/子框架
例如:
Spring Cloud Netflix
[ Apache Dubbo Zookeeper ]
Spring Cloud Alibaba
...
2 想使用Spring Cloud,必须掌握Spring Boot,因为Spring Cloud是基于/依赖于 Spring Boot
=> Spring boot可以离开Spring Cloud独立使用开发项目,但是Spring Cloud离不开Spring boot

PS: 如果某不知名团队X,基于上述4个核心问题开发一套解决方案,也叫 Spring Cloud!

Spring Boot 与 Spring Cloud的关系

Spring Cloud 基于/依赖于 Spring Boot

Spring Cloud 包含:

微服务(Spring Boot) /
API网关: API Gateway /
服务注册/发现 : Service Registry Center / Config Server /
通信调用
熔断机制
...

Spring Boot - Build Anything

Spring Cloud - Coordinate(协调) Anything

Spring Cloud Data Flow - Connect Everything

2-1 Spring Cloud Netflix

NetFlix: 出来了一套解决方案——Spring Cloud Netflix!

API网关 - zuul组件 (NetFlix开源)

网络通信/HTTP调用 - Feign (NetFlix开源)

Feign --> 基于 HTTPClient --> HTTP的通信方式 [同步且阻塞]

服务注册/发现 - Eureka (NetFlix开源)

熔断机制 - Hystrix (NetFlix开源)

Hystrix [hɪst'rɪks],译为 豪猪(因其背上长满棘刺,从而拥有了自我保护的能力)

Hystrix是Netflix开源的一款容错框架,同样具有自我保护能力。

2018年底,NetFlix宣布无限期停止维护。

开源软件不再维护,就会脱节,乃至被社区和生态弃用? 下一步怎么走?

2-2 Apache Dubbo Zookeeper

Alibaba: 出来了一套解决方案——Apache Dubbo Zookeeper!

Alibaba开源的, 一款基于Java的、高性能的RPC通信框架

http://dubbo.apache.org/zh-cn/ / https://zookeeper.apache.org/

API网关 -

需使用第三方组件 或 自己实现

网络通信/RPC调用 - Dubbo (Alibaba开源)
服务注册/发现 - Zookeeper

Zookeeper: 动物园管理者 (Hadoop / Hive / ...)

熔断机制 - Hystrix (NetFlix开源)

继续 借用 Hystrix

2-3 Spring Cloud Alibaba

https://spring.io/projects/spring-cloud-alibaba

简述

Alibaba: 又出来了一套解决方案——Spring Cloud Alibaba

Spring Cloud Alibaba 提供分布式应用开发的一站式解决方案。它包含开发分布式应用程序所需的所有组件,使您可以轻松地使用 Spring Cloud 开发应用程序。
使用 Spring Cloud Alibaba,您只需要添加一些注解和少量配置,即可将 Spring Cloud 应用连接到阿里巴巴的分布式解决方案,并通过阿里巴巴中间件构建分布式应用系统。

特征

Spring Cloud

流量控制和服务降级: Alibaba Sentinel的流量控制、断路和系统自适应保护
服务注册与发现:实例可以注册到阿里巴巴的 Nacos,客户端可以通过 Spring 管理的 bean 发现实例。通过 Spring Cloud Netflix 支持客户端负载均衡器 Ribbon
分布式配置:使用阿里巴巴Nacos作为数据存储
事件驱动:构建与Spring Cloud Stream RocketMQ Binder连接的高度可扩展的事件驱动微服务
Message Bus : 用 Spring Cloud Bus RocketMQ 链接分布式系统的节点
Distributed Transaction :支持Seata高性能、易用的分布式事务解决方案
Dubbo RPC :通过Apache Dubbo RPC扩展Spring Cloud service-to-service调用的通信协议

Spring Boot

所有的 Spring Boot Starter 都在阿里云 Spring Boot Project中维护。
阿里云对象存储服务Spring Boot Starter
阿里云短信服务Spring Boot Starter
阿里云 Redis的 Spring Boot Starter
阿里云RDS MySQL的Spring Boot Starter
阿里云 SchedulerX的 Spring Boot Starter

...

2-4 Future: 服务网格——Service Mesh

目前,又提出了一种解决方案————服务网格(下一代微服务标准, Service Mesh)
代表性的具体解决方案: Istio (未来,可能需要掌握) Service Mesh 是微服务时代的 TCP/IP 协议。

3 小结: Spring Cloud

万变不离其宗,一通百通!

API网关 - 服务路由(客户端访问)

参考文献

微服务网关概述 - CSDN
[谈谈微服务中的 API 网关(API Gateway) - 博客园
吐血整理微服务架构API网关的小技巧(Gateway简单入门) - CSDN
springCloud网关Zuul和GateWay区别 - CSDN

问题背景

不同的微服务一般会有不同的网络地址,客户端在访问这些微服务时必须记住几十甚至几百个地址,这对于客户端方来说太复杂也难以维护。如下图:

如果让客户端直接与各个微服务通讯,可能会有很多问题:

1.客户端会请求多个不同的服务,需要维护不同的请求地址,增加【开发/维护难度】
2.在某些场景下存在【跨域请求】的问题
3.加大【身份认证】的难度,每个微服务需要【独立认证】

因此,我们需要一个微服务网关,介于客户端与服务器之间的中间层,所有的外部请求都会先经过微服务网关。客户端只需要与网关交互,只知道一个网关地址即可,这样简化了开发还有以下优点:

1、易于【监控】
2、易于【认证】
3、减少了客户端与各个微服务之间的交互复杂度和次数

定义: 什么是微服务网关?

API网关是一个服务器,是系统对外的唯一入口。
API网关封装了系统内部架构,为每个客户端提供一个定制的API。
API网关方式的核心要点是:

所有的客户端和消费端都通过【统一的网关接入微服务】,在网关层处理所有的【非业务功能】.
通常,网关也是提供REST/HTTP的访问API。服务端通过【API-GW注册】和【管理服务】。

主要功能

微服务网关作为微服务后端服务的统一入口,它可以统筹管理后端服务,主要分为数据平面和控制平面:

数据平面主要功能是接入用户的HTTP请求和微服务被拆分后的聚合。使用微服务网关统一对外暴露后端服务的API和契约,路由和过滤功能正是网关的核心能力模块。另外,微服务网关可以实现拦截机制和专注跨横切面的功能,包括协议转换、安全认证、熔断限流、灰度发布、日志管理、流量监控等。

控制平面主要功能是对后端服务做统一的管控和配置管理。例如,可以控制网关的弹性伸缩;可以统一下发配置;可以对网关服务添加标签;可以在微服务网关上通过配置Swagger功能统一将后端服务的API契约暴露给使用方,完成文档服务,提高工作效率和降低沟通成本。

作用/应用场景

网关具有的职责,如身份验证、监控、负载均衡、缓存、请求分片与管理、静态响应处理。

当然,最主要的职责还是与“外界联系”。

路由功能:路由是微服务网关的核心能力。通过路由功能微服务网关可以将请求转发到目标微服务。在微服务架构中,网关可以结合注册中心的动态服务发现,实现对后端服务的发现,调用方只需要知道网关对外暴露的服务API就可以透明地访问后端微服务。

负载均衡:API网关结合负载均衡技术,利用Eureka或者Consul等服务发现工具,通过轮询、指定权重、IP地址哈希等机制实现下游服务的负载均衡。

统一鉴权:一般而言,无论对内网还是外网的接口都需要做用户身份认证,而用户认证在一些规模较大的系统中都会采用统一的单点登录(Single Sign On)系统,如果每个微服务都要对接单点登录系统,那么显然比较浪费资源且开发效率低。API网关是统一管理安全性的绝佳场所,可以将认证的部分抽取到网关层,微服务系统无须关注认证的逻辑,只关注自身业务即可。

协议转换:API网关的一大作用在于构建异构系统,API网关作为单一入口,通过协议转换整合后台基于REST、AMQP、Dubbo等不同风格和实现技术的微服务,面向Web Mobile、开放平台等特定客户端提供统一服务。

指标监控:网关可以统计后端服务的请求次数,并且可以实时地更新当前的流量健康状态,可以对URL粒度的服务进行延迟统计,也可以使用Hystrix Dashboard查看后端服务的流量状态及是否有熔断发生。

限流熔断:在某些场景下需要控制客户端的访问次数和访问频率,一些高并发系统有时还会有限流的需求。在网关上可以配置一个阈值,当请求数超过阈值时就直接返回错误而不继续访问后台服务。当出现流量洪峰或者后端服务出现延迟或故障时,网关能够主动进行熔断,保护后端服务,并保持前端用户体验良好。

黑白名单:微服务网关可以使用系统黑名单,过滤HTTP请求特征,拦截异常客户端的请求,例如DDoS攻击等侵蚀带宽或资源迫使服务中断等行为,可以在网关层面进行拦截过滤。比较常见的拦截策略是根据IP地址增加黑名单。在存在鉴权管理的路由服务中可以通过设置白名单跳过鉴权管理而直接访问后端服务资源。

灰度发布:微服务网关可以根据HTTP请求中的特殊标记和后端服务列表元数据标识进行流量控制,实现在用户无感知的情况下完成灰度发布。

流量染色:和灰度发布的原理相似,网关可以根据HTTP请求的Host、Head、Agent等标识对请求进行染色,有了网关的流量染色功能,我们可以对服务后续的调用链路进行跟踪,对服务延迟及服务运行状况进行进一步的链路分析。

文档中心:网关结合Swagger,可以将后端的微服务暴露给网关,网关作为统一的入口给接口的使用方提供查看后端服务的API规范,不需要知道每一个后端微服务的Swagger地址,这样网关起到了对后端API聚合的效果。

日志审计:微服务网关可以作为统一的日志记录和收集器,对服务URL粒度的日志请求信息和响应信息进行拦截。

解决方案1: Tyk

Tyk

解决方案2: Kong(基于Nginx+Lua)

Kong

github: https://github.com/traefik/traefik

特点: 基于Nginx+Lua开发,性能高,稳定,有多个可用的插件(限流、鉴权等等)可以开箱即用。

不足:只支持Http协议;二次开发,自由扩展困难;提供管理API,缺乏更易用的管控、配置方式。

解决方案3: Orange

Orange

解决方案4: Netflix zuul

Netflix zuul

特点: Netflix开源,功能丰富,使用JAVA开发,易于二次开发;需要运行在web容器中,如 Tomcat。

不足:缺乏管控,无法动态配置;依赖组件较多;处理Http请求依赖的是Web容器,性能不如 Nginx;
Zuul 1.0 : Netflix开源的网关,使用Java开发,基于Servlet架构构建,便于二次开发。因为基于Servlet内部延迟严重,并发场景不友好,一个线程只能处理一次连接请求。

Zuul 2.0 : 采用Netty实现异步非阻塞编程模型,一个CPU一个线程,能够处理所有的请求和响应,请求响应的生命周期通过事件和回调进行处理,减少线程数量,开销较小

解决方案4: apiaxle

apiaxle

解决方案5: api-umbrella

api-umbrella /

解决方案6: Traefik

Traefik

特点: Go语言开发;轻量易用;提供大多数的功能:服务路由,负载均衡等等;提供 WebUI

不足:二进制文件部署,二次开发难度大;UI更多的是监控,缺乏配置、管理能力;

解决方案6: Spring Cloud Gateway

Spring Cloud Gateway

Spring Cloud的一个全新的API网关项目,替换Zuul开发的网关服务

基于Spring5.0 + SpringBoot2.0 + WebFlux(基于性能的Reactor模式响应式通信框架Netty,异步阻塞模型)等技术开发,性能高于Zuul

解决方案6: Nginx

Nginx + lua 实现

特点: 使用Nginx的【反向代理】和【负载均衡】,可实现对api服务器的【负载均衡】及【高可用】
不足:自注册的问题和网关本身的扩展性

解决方案6: Envoy

Envoy

envoy 是作为微服务服务架构中以独立进程方式实现高级网络功能的,轻量级的7层服务代理程序,通常以sidecar的方式运行在应用程序的周边,也可以作为网络的边缘代理来运行

envoy 的特性 进程外体系结构 ,L3/L4过滤器体系结构,HTTP L7过滤器体系结构, 一流的HTTP/2支持, HTTP/3支持(目前为alpha),HTTP L7路由,gRPC支持,服务发现和动态配置,健康检查,高级负载平衡,前端/边缘代理支持, 一流的可观察性

小结

2022.7.12

API 网关方案 发起方 社区活跃度(github star数) 编程语言 优点 缺点
spring-cloud-gateway(2016) spring cloud 3.6K Java 异步、配置灵活方便、性能优于zuul(因zuul老是跳票而另起炉灶) 晚期产品
zuul(2012) Netflix 12K Java 成熟、简略/门槛低 (...)
nginx(2004) Nginx Inc 16.7K C/Lua 高性能、成熟稳固 门槛高、偏运维、可编程弱
Envoy(2006) Lyft 20K C++ 高性能、可编程、API/ServiceMesh集成 门槛较高
Kong(2004) Kong Inc 32.4K OpenResty/Lua 高性能、可编程 API 门槛较高
Traefik(2015) Containous 38.8K Golang 云原生、可编程 API、对接各种服务发现 生产案例不多

HTTP/RPC框架(同步/异步 | 阻塞/非阻塞) - 服务通信/调用(服务端通信)

若项目涉及多个语言平台之间的相互调用,就应该选择跨语言平台的 RPC 框架。

解决方案(RPC调用框架)A1: Dubbo(Alibaba/Java)

解决方案(RPC调用框架)A2: gRPC(Google/支持多语言)

解决方案(RPC调用框架)A3: Thrift (Facebook/支持多语言)

解决方案(RPC调用框架)A4: Tars(Tencent/C++)

解决方案(HTTP调用框架)B1: Feign(from NetFlix)

注:

RestFul API规范

底层网络通信框架: Netty / ...

注册中心 - 服务注册/发现(微服务治理、高可用)

解决方案1: Nacos

Nacos

解决方案2: Eureka

解决方案3: Zookeeper

解决方案4: consul

服务容灾与监控(解决服务降级、服务崩溃后怎么办?)

[容灾]熔断/降级机制:Hystrix

参考文献

[1] 微服务组件--限流框架Spring Cloud Hystrix详解 - 博客园

作用

  【1】Hystrix是springCloud的组件之一,Hystrix 可以让我们在分布式系统中对服务间的调用进行控制加入一些调用延迟或者依赖故障的容错机制。

  【2】Hystrix 通过将依赖服务进行资源隔离进而阻止某个依赖服务出现故障时在整个系统所有的依赖服务调用中进行蔓延;【防止服务雪崩】

  【3】其核心功能:
  1)服务隔离(服务限流)
    通过线程池或者信号量判断是否已满,超出容量的请求直接降级,以达到限流的作用。   2)服务熔断
    当失败率达到阈值自动触发降级,熔断器触发的快速失败会有助于系统防止崩溃。【可以说熔断是特定条件的降级】   3)服务降级
    服务降级是当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行。

2 快速使用

step1 引入依赖

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

step2 启动类开启hystrix功能

@SpringBootApplication
//注册到eureka
@EnableEurekaClient
//开启断路器功能
@EnableCircuitBreaker
public class WebApplication {
...
}

step3 注解@HystrixCommand参数分析

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface HystrixCommand {
// HystrixCommand 命令所属的组的名称:默认注解方法类的名称
String groupKey() default "";
// HystrixCommand 命令的key值,默认值为注解方法的名称
String commandKey() default "";
// 线程池名称,默认定义为groupKey
String threadPoolKey() default "";
// 定义回退方法的名称, 此方法必须和hystrix的执行方法在相同类中
String fallbackMethod() default "";
// 配置hystrix命令的参数
HystrixProperty[] commandProperties() default {};
// 配置hystrix依赖的线程池的参数
HystrixProperty[] threadPoolProperties() default {};
// 如果hystrix方法抛出的异常包括RUNTIME_EXCEPTION,则会被封装HystrixRuntimeException异常。我们也可以通过此方法定义哪些需要忽略的异常
Class<? extends Throwable>[] ignoreExceptions() default {};
// 定义执行hystrix observable的命令的模式,类型详细见ObservableExecutionMode
ObservableExecutionMode observableExecutionMode() default ObservableExecutionMode.EAGER;
// 如果hystrix方法抛出的异常包括RUNTIME_EXCEPTION,则会被封装HystrixRuntimeException异常。此方法定义需要抛出的异常
HystrixException[] raiseHystrixExceptions() default {};
// 定义回调方法:但是defaultFallback不能传入参数,返回参数和hystrix的命令兼容
String defaultFallback() default "";
}

step4 使用示例

//线程池隔离的设置,线程池隔离与信号量隔离的最大区别在于发送请求的线程,信号量是采用调用方法的线程,而线程池则是用池内的线程去发送请求
@HystrixCommand(
groupKey="test-provider",
threadPoolKey="test-provider",
threadPoolProperties = {
@HystrixProperty(name = "coreSize", value = "20"),//线程池大小
@HystrixProperty(name = "maximumSize", value = "30"),//最大线程池大小
@HystrixProperty(name = "maxQueueSize", value = "20"),//最大队列长度
@HystrixProperty(name = "keepAliveTimeMinutes", value = "2")//线程存活时间
},commandProperties = {
@HystrixProperty(name = "execution.isolation.strategy",value = "THREAD")
}
//信号量隔离的设置
@HystrixCommand(
//用来设置降级方法
fallbackMethod = "myTestFallbackMethod",
commandProperties = {
//进行熔断配置
//条件1,设置在滚动时间窗口中,断路器的最小请求数(没有达到不会熔断)。默认20。
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold" ,value = "10"),
//条件2,设置断路器打开的错误百分比。在滚动时间内,在请求数量超过requestVolumeThreshold的值,且错误请求数的百分比超过这个比例,断路器就为打开状态。
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage" ,value = "30"),
//条件3,设置滚动时间窗的长度,单位毫秒。这个时间窗口就是断路器收集信息的持续时间。断路器在收集指标信息的时会根据这个时间窗口把这个窗口拆分成多个桶,每个桶代表一段时间的指标,默认10000.
@HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds" ,value = "10000"),
//设置当断路器打开之后的休眠时间,休眠时间结束后断路器为半开状态,断路器能接受请求,如果请求失败又重新回到打开状态,如果请求成功又回到关闭状态
//单位是毫秒
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds" ,value = "3000"), //配置信号量隔离
//配置信号量的数值
@HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests",value = "100"),
//选择策略为信号量隔离
@HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE"),
//设置HystrixCommand执行的超时时间,单位毫秒
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000000000")
}
)
public String Test(){
....
} public String myTestFallbackMethod() {
log.info("========myTestFallbackMethod=========");
return "myTestFallbackMethod";
}

[容灾]流量控制 : Sentinel

[容灾]负载均衡(客户端) : Spring Cloud Ribbon

负载均衡的解决方案种类

服务端负载均衡:可细分为硬件负载均衡(如:F5)、软件负载均衡(如:Nginx)

【服务端负载均衡】维护一个可用的服务端清单,通过心跳检测来剔除故障的服务端节点,保证清单中都是可正常访问的服务端节点。当客户端发送请求到负载均衡设备的时候,该设备按某种算法(轮询、加权等)从维护的可用服务端清单中取出一台服务端端地址,然后进行转发。

客户端负载均衡:(如:Ribbon)

【客户端负载均衡】和服务端负载均衡最大的不同点在于服务清单所存储的位置。在客户端负载均衡中,所有客户端节点都维护着自己要访问的服务端清单,而这些服务端端清单来自于服务注册中心。

简述

Spring Cloud Ribbon是一个基于HTTPTCP客户端负载均衡工具,它基于Netflix Ribbon实现,通过Spring Cloud Ribbon的封装,在微服务架构中使用客户端负载均衡调用非常简单。Ribbon是Spring Cloud整个大家庭中相对而言比较复杂的模块,直接影响到服务调度的质量和性能。

负载均衡的流程(Ribbon)

负载均衡的策略(Ribbon)

内置负载均衡规则类&规则描述

1、RoundRobinRule:
简单轮询服务列表来选择服务器,它是Ribbon默认的负载均衡策略 2、AvailabilityFilterRule
对以下两种服务器进行忽略:
(1)在默认情况下,这台服务器如果3次连接失败,这台服务器就会被设置为“短路”状态,短路状态将持续30秒,如果再次连接失败,短路持续时间就会几何级地增加。
(2)并发数过高的服务器,如果一个服务器的并发连接数过高,配置了AvailabilityFilteringRule规则的客户端也会将其忽略。并发连接数的上限,可以由客户端的ActiveConnectionsLimit进行配置。 3、WeightedResponseTimeRule
为每一个服务器赋予一个权重值。服务器响应时间越长,这个服务器的权重就越小。这个规则会随机选择服务器,这个权重会影响服务器的选择。 4、ZoneAvoidanceRule
以区域可用的服务器为基础进行服务器的选择,使用Zone对服务器进行分类,这个Zone可用理解为一个机房,一个机架等。而后再对Zone内对多个服务做轮询。 5、BestAvailableRule
忽略那些短路等服务器,并选择并发数较低的服务器 6、RandomRule
随机选择一个可用的服务器 7、RetryRule
重试机制的选择逻辑

maven导入

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency> //--------------springcloud-H 版引入 eureka-client 自带ribbon-----------------------//
<!-- eureka client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

启用 ribbon 作为 loadbalancer

spring:
cloud:
loadbalancer:
ribbon: # 使用 ReactiveLoadBalancerClient 时通过本参数禁用调 ribbon
enabled: true

[容灾]负载均衡(客户端) : Spring Cloud LoadBalacer

启用 Spring Cloud LoadBalance 作为 loadbalancer

spring:
cloud:
loadbalancer:
enabled: true

[容灾]负载均衡(服务端) : Nginx

X 参考/推荐资料

【狂神说Java】SpringBoot最新教程IDEA版通俗易懂 - Bilibili
Spring boot与Spring cloud 是什么关系? - Zhihu
[谈谈微服务中的 API 网关(API Gateway) - 博客园
微服务架构的核心技术问题 - 博客园
微服务:注册中心ZooKeeper、Eureka、Consul 、Nacos对比 - CSDN
什么是 Service Mesh - Zhihu
spring-cloud - spring
spring-cloud-alibaba - spring
nacos - github
【微服务】(四)—— Ribbon - CSDN

[Java EE]Spring Boot 与 Spring Cloud的关系/过去-现在-未来的相关教程结束。

《[Java EE]Spring Boot 与 Spring Cloud的关系/过去-现在-未来.doc》

下载本文的Word格式文档,以方便收藏与打印。