Spring Boot和Spring Cloud
SpringCloud
Spring Cloud是-个分布式的整体解决方案。Spring Cloud为开发者提供了在分布式系统(配
置管理,服务发现,熔断,路由,微代理,控制总线,- -次性token,全局琐,leader选举, 分
布式session,集群状态)中快速构建的工具,使用Spring Cloud的开发者可以快速的启动服务
或构建应用、同时能够快速和云平台资源进行对接。
●SpringCloud分布式开发五大常用组件
●服务发现一 Netflix Eureka
●客服端负载均衡一Netflix Ribbon
●断路器一 Netflix Hystrix
●服务网关一-Netlix Zuul
●分布式配置一 -Spring Cloud Config
快速开始Cloud
注册中心
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RGNq6ueY-1603278766654)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\1584929037174.png)]
配置文件
server:
port: 8761
eureka:
instance:
hostname: eureka-server #eureka实例的主机名字
client:
register-with-eureka: false #不把自己注册到 eureka
fetch-registry: false #不从eureka上获取服务的注册信息
service-url:
defaultZone: http://localhost:8761/eureka/
在启动类上添加注解
@EnableEurekaServer //启动注册中心
服务提供者
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qilRvHXc-1603278766658)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\1584929138810.png)]
配置文件
server:
port: 8002
spring:
application:
name: provider-ticket
eureka:
instance:
prefer-ip-address: true #注册服务时使用ip地址
client:
service-url:
defaultZone: http://localhost:8761/eureka/
服务消费者
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HhK6zVEo-1603278766661)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\1584929282691.png)]
配置文件
spring:
application:
name: consumer-user
server:
port: 8200
eureka:
instance:
prefer-ip-address: true #注册服务时使用ip地址
client:
service-url:
defaultZone: http://localhost:8761/eureka/
启动类添加 获取服务注解 开启负载均衡
@EnableDiscoveryClient //开启获取服务
@SpringBootApplication
public class ConsumerUserApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerUserApplication.class, args);
}
@LoadBalanced //使用负载均衡
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
开始获取服务
@RestController
public class UserController {
@Autowired
RestTemplate template;
@GetMapping("/buy")
public String buyTicket(String name){
//第一个参数为注册中心的服务名 , 第二个参数返回值类型;
String s = template.getForObject("http://PROVIDER-TICKET/ticket",String.class);
return name+"购买了"+s;
}
}
SpringBoot
Spring --> SpringMvc --> Tomcat
简化配置值
舍弃笨重的xml 改用 yml 或properties
产品独立运行
产品可独立运行,打成jar包,内置了tomcat 或其他servlet容器,契合微服务的理念;
强大的场景启动器
每一个特定场景需求都封装成了有个starter,只需要导入这个启动器就有了这个场景所需的一切,其中包括针对这个场景的自动化配置,依赖信息;
重要的组成部分
starter 启动器
@Enablexxx 注解 功能启用
yml 或 propertires 配置文件
主启动类
相关注解
@Configuration 注解标记一个类后,该类成为配置类,加载这个类中的配置可以取代以前的xml 配置文件
@Bean 注解把一个类的对象加入IOC容器
@Import 相对于@Bean,能更便捷的将已给类jiaruIOC容器
@Conditional注解 一个类满足特定条件时才加如IOC容器
@ComponentScan 指定IOC容器扫描的包,自动扫描指定的包下的注解 等同于 xml context:component-scan;
@ComponentScan({"全限定包名1","全限定包名2"}) 可以扫描多个包
@SpringBootConfiguration @Configuration注解的 SpringBoot版
@Targer(ElementType.Type)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@COnfiguration
public @interface SpringBootConfiguration{
}
@EnableAutoConfigurtion 启动自动化配置
@AutoConfigurationPackage 指定自动配置的包
@SpringBootAppliction 注解相当于 ssm中的总配置文件,包含了 @SpringBootConfiguration @ComponentScan @EnableAutoConfigurtion ...
SpringBoot 工作原理
读取spring.factories文件
SpringBoot启动时会读取spring-boot-autoconfigure-2.2.5.RELEASE.jar包下的ME TA-INF/spring.factories文件。读取
org.springframework.boot.autoconfigure.EnableAutoConfiguration属性的值加载自动配置类。
加载XxxProperties类
根据自动配置类中指定的XxxPropertes类设置自动配置的属性值,开发者也可以根据XxxProperties类中指定的属性在yml
配置文件中修改自动配置。.
根据@ConditionalXxx注解决定加载哪些组件
SpringBoot通过@ConditionalXxx注解指定特定组件加入IOC容器时所需要具备的特定条件。这个组件会在满足条件时加入
I0C容器。
整合bootRedis
导入依赖
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
boot 无法连接redis
解决办法:
查看有没有启动Redis服务器。
redis的配置application.yml(或application.properties)中
spring.redis.timeout连接超时时间(毫秒)中设置不能为0,
一般修改如下:spring.redis.timeout=5000。
找到redis的配置文件 redis.conf : 执行 vim redis.conf
3.1 protected-mode yes 改为 protected-mode no (即该配置项表示是否开启保护模式,默认是开启,开启后Redis只会本地进行访问,拒绝外部访问)。
3.2 注释掉 bin127.0.0.1 即 #bin 127.0.0.1 (ps: 不注释掉,表示指定 redis 只接收来自于该 IP 地址的请求,注释掉后,则表示将处理所有请求)。
如果在Redis中没有配置requirepass ,那么在application.properties(或application.yaml)中就不要写spring.redis.password。
Redis (error) NOAUTH Authentication required.解决方法
出现认证问题,应该是设置了认证密码,输入密码既可以啦
注意密码是字符串形式!
cdm 命令
//密码
auth "yourpassword"
//查询所有的 keys
keys *
//查询指定 key的值
get key
SpringCloud 的组件
注册中心:Eureka
客户端负载均衡:Ribbon
声明式远程方法调用:Feign
服务降级 熔断 :Hystrix;
网关:Zuul
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ka3ibEb2-1603278766665)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\1585097293833.png)]
创建 Cloud项目
1.创建注册中心
创建boot 项目
导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
启动类 添加注解
@EnableEurekaServer //启用Eurek服务器功能
yml 配置文件
server:
port: 5000
eureka:
instance:
hostname: localhost #注册中心的地址
client:
register-with-eureka: false #不把自己注册到注册中心,自己本身就是
fetch-registry: false #不需要从注册中行取回信息
service-url: #注册中心地址 , 客户端访问注册中心时使用
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
2.Feign (提供业务接口)
建议使用boot 项目
openfeign 提供远程业务接口
导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
编写远程业务接口
调用者:
//远程调用的接口方法
//要求 方法完全一致 (修饰符,参数,方法名,返回值)
//请求方式 ,请求路径完全一致
//使用的注解完全一致
import com.sl.commons.pojo.Employee;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
// 表示当前接口和一个 provider的服务对应
@FeignClient("provider")
public interface EmployeeRemoteService {
//远程调用的接口方法
//要求 方法完全一致 (修饰符,参数,方法名,返回值)
//请求方式 ,请求路径完全一致
//使用的注解完全一致
@RequestMapping("/provider/get/employee/remote")
public Employee getEmployeeRemote();
}
3.服务提供者
创建boot 项目 (web)
1.导入依赖
<!--依赖接口提供者工程-->
<dependency>
<groupId>com.sl</groupId>
<artifactId>commons</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!--客户端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
yml配置
server:
port: 1000
eureka:
client:
service-url:
defaultZone: http://localhost:5000/eureka/
spring:
application:
name: provider
启动类 2.0版本之下的需要添加注解 其下任意一个即可
@EnableEurekaClient
只能是获取 Eureka注册中心提供的服务;
@EnableDiscoveryClient
可以获取任意注册中写 的服务
实现Feign提供的业务接口
注意点:
//远程调用的接口方法
//要求 方法完全一致 (修饰符,参数,方法名,返回值)
//请求方式 ,请求路径完全一致
//使用的注解完全一致
@RestController
public class EmployeeController {
@RequestMapping("/provider/get/employee/remote")
public Employee getEmployeeRemote(){
System.out.println("1000");
return new Employee(555,"provider:",555.55);
}
4.服务消费者
创建 boot项目 (web)
导入依赖
<!--依赖接口提供者工程-->
<dependency>
<groupId>com.sl</groupId>
<artifactId>commons</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!--服务消费者依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
<!--客户端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
yml配置
server:
port: 4000
spring:
application:
name: cosumer
eureka:
client:
service-url:
defaultZone: http://localhost:5000/eureka/
启动类添加注解
扫描Feign提供的远程接口
@EnableFeignClients("com.sl.commons")
远程调用微服务接口
@RestController
public class HumanResourcrHandler {
//准备 远程调用微服务的接口
@Autowired
private EmployeeRemoteService employeeRemoteService;
@RequestMapping("/feign/get/employee")
public Employee getEmployee() {
return employeeRemoteService.getEmployeeRemote();
}
@RequestMapping("/s")
public String s() {
return "ssss";
}
}
小拓展
ribbon
负载均衡:的实现过程被隐藏 在远程调用微服务是开启;
spring-cloud-starter-netflix-ribbon
集群:一模一样的业务的集合,来自于同一个注册中兴,端口不一样,服务名一样;
参数的传递
基本类型
@RequestParam(“id”) String id
复杂类型
@RequestBody Student stu
服务雪崩
在微服务架构体系下,服务间的调用错综复杂,交织成一张大网。 如果其中某个节点突然无法正常工作,则访问它的众多服务都会被卡住,进而有更多服务被卡住,系统中的线程、CPU、内存等资源有可能被迅速耗尽,最终整个服务体系崩溃。
我们管这样的现象叫服务雪崩。
Hytrix介绍
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里, 许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix 能够保正在个依 赖出问题的情况下,不会导致整体服务失败,避免级联故险,以提商分布式系统的弹性。
“断路器”本身是种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),面不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用用方的线程不会被长时间、不必要地占用,从而避见了故障在分布式系统中的蔓延。乃至雪崩:
Hytrix能够提供眼务降级、服务熔断、服务用流、按近实时的监控等方面的功能。
服务熔断机制
[熔断机制]是应对雪蹦效应的一种微服务链路保护机制。
当扇出链路的某个微服务不可用或者响应时何太长时。会进行服务的[降级]:近而熔断该节点服务的调用,快速响应错误信息:当检测到该节点微服务调用响施正常后恢复调用链路,在SpringCloud框架里熔断机制通过Hystrix 实现。Hystrix 会监控微厦务间调用的状况,当失败的调用到一定阀值,缺省是5秒内20次调用夫败就会启动熔断机制。熔断机制的注解是@HystrixCommand.
服务消费者 备用方案 : 降级
服务降级 处理是在 客户端(Consumer) 实现完成的 , 于服务端(Provider)没有关系,当某个Consumer访问Provider却迟迟得不到响应时执行预先设定好的一个解决方案; 而不是一直等待
Consumer ------->
正常方案 : XXX --------------> provider
|
异常
↓
备用方案 : XXX <-------------- 启用备选方案
|
↓
返回相同类型的数据
让整个系统可以据需运行
在 业务接口 导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
自定义返回值类
作为项目的统一返回值
ResultEntity
package com.sl.commons.util;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 项目统一使用 这个类型作为Ajax 请求 或远程
* 方法条用返回响应的数据格式
* @param <T>
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ResultEntity<T> {
public static final String SUCCESS = "SUCCESS"; //成功
public static final String FAILED = "FAILED"; //失败
public static final String NO_MESSAGE = "NO_MESSAGE"; //无消息
public static final String NO_DATA = "NO_DATA"; //无数据
private String resulr;
private String message;
private T data;
/**
* 操作成功,无需返回数据
* @return
*/
public static ResultEntity<String> successWithoutData(){
return new ResultEntity<String>(SUCCESS,NO_MESSAGE,NO_DATA);
}
/**
* 操作成功 ,需要返回数据
* @param data
* @param <E>
* @return
*/
public static <E> ResultEntity<E> successWithoutData(E data){
return new ResultEntity<>(SUCCESS,NO_MESSAGE,data);
}
/**
* 操作失败 ,返回错误信息
* @param message
* @param <E>
* @return
*/
public static <E> ResultEntity<E> failed(String message){
return new ResultEntity<>(SUCCESS,message,null);
}
}
创建 MyFallBackFactiry
实现 FallbackFactory
package com.sl.commons.factory;
import com.sl.commons.api.EmployeeRemoteService;
import com.sl.commons.pojo.Employee;
import com.sl.commons.util.ResultEntity;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 实现 Conumer 服务降级的功能
* 实现 FallbackFactory 这个借口是 要传入 @FeignClient注解标记的接口类型;
* 实现create 方法 返回 @FeignClient注解标记的接口类型的对象 当 provide调用失败时 会执行这个对象对应的方法
*/
@Component
public class MyFallBackFactiry implements FallbackFactory<EmployeeRemoteService> {
@Override
public EmployeeRemoteService create(Throwable throwable) {
return new EmployeeRemoteService() {
@Override
public Employee getEmployeeRemote() {
return null;
}
@Override
public List<Employee> getEList(String key) {
return null;
}
@Override
public ResultEntity<Employee> breaker(String signal) {
return ResultEntity.failed(throwable.getMessage()+"降级使用");
}
};
}
}
接口业务类
注解
@FeignClient(value = “provider”,fallbackFactory = MyFallBackFactiry.class)
package com.sl.commons.api;
import com.sl.commons.factory.MyFallBackFactiry;
import com.sl.commons.pojo.Employee;
import com.sl.commons.util.ResultEntity;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
// 表示当前接口和一个 provider的服务对应
//fallbackFactory 指定 provider 不可使用时 提供备用的 工厂对象
@FeignClient(value = "provider",fallbackFactory = MyFallBackFactiry.class)
public interface EmployeeRemoteService {
//远程调用的接口方法
//要求 方法完全一致 (修饰符,参数,方法名,返回值)
//请求方式 ,请求路径完全一致
//使用的注解完全一致
@RequestMapping("/provider/get/employee/remote")
public Employee getEmployeeRemote();
@RequestMapping("/provider/get/empList/remote")
List<Employee> getEList(@RequestParam("key") String key);
@RequestMapping("/provider/get/emp/circuit/breaker")
public ResultEntity<Employee> breaker(@RequestParam("signal") String signal)throws InterruptedException;
}
服务消费者类 扫描接口业务的包;
@EnableFeignClients("com.sl.commons")@SpringBootApplication(scanBasePackages = {"com.sl.commons","com.sl.consumer"})
服务消费者 导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
配置文件
feign:
hystrix:
enabled: true
服务提供者 备用方案 : 熔断
服务提供者 导入 依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
为指定的方法添加 备用方法
@HystrixCommand 指定方法出现问题时调用备份方法
// @HystrixCommand 指定方法出现问题时调用备份方法;
@HystrixCommand(fallbackMethod = "breaker2")
@RequestMapping("/provider/get/emp/circuit/breaker")
public ResultEntity<Employee> breaker(@RequestParam("signal") String signal) throws InterruptedException {
if ("quick-bang".equals(signal)) {
throw new RuntimeException();
}
if ("slow-bang".equals(signal)) {
Thread.sleep(5000);
}
return ResultEntity.successWithoutData(new Employee(555,"provider3",555.55));
}
public ResultEntity<Employee> breaker2(@RequestParam("signal") String signal){
String message = "方法出现问题"+signal+"/n执行备用方法";
return ResultEntity.failed(message);
}
监控 服务
在 服务提供者
provider工程中添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
配置yml添加
management:
endpoints:
web:
exposure:
include: hystrix.stream
创建一个 监控工程
导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
启用仪表盘监控功能
@EnableHystrixDashboard
访问服务监控
http://localhost:9000/hystrix
完成一整套的服务 ;才能有该服务的监控;
http://localhost:8001/actuator/hystrix.stream
zuul网关
为什么 要 定义统一的入口
不同的微服务有不同的网络地址,而外部的客户端 可能需要调用多个服务的接口才能完成一个业务需求.
会存在一下不利因素
1.客户端胡多次请求不同的微服务,增加客户端的复杂性;
2.存在跨域请求,在一定场景下处理相对复杂;
3.认证复杂,每一个服务器都要独立认证;
4.难以重构,随着项目的迭代,可能需要重新划分微服务,如果客户端直接和微服务同行,那么重构会难以实施;
5.某些微服务可能使用了其他的协议,直接访问有一定困难
Zuul 包含了对请求打的路由和过滤两个最主要的功能;
其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础,
而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验,服务聚合功能的基础;
Zuul和Eureka进行整合,将Zuul自身注册为Eureka服务治理下的应用,同时从Eureka中或得其他微服务的信息,以及以后的访问微服务都是Zuul跳转后获得;
总的来说, Zuul提供了 代理, 路由 和 过滤 的功能
(别名)
(避免暴露微服务的名字) 具体微服务
请求 ---- Zuul ---- 请求特征 --- 微服务名字 --- Eureka --- 具体微服务
具体微服务
创建 Zuul网关 代理
创建新的 Sprinboot项目
导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
启动Zuul 网关代理功能
@EnableZuulProxy
访问测试
http://localhost:9000/ consumer/feign/get/fallback
zuul网关 微服务名字 目标具体的微服务
yml 配置
spring:
application:
name: zuul
eureka:
client:
service-url:
defaultZone: http://localhost:5000/eureka/
server:
port: 9999
#自定义路由
zuul:
routes:
employye: #自定义路由规则名称,在底层的结构式map的键
service-id: consumer #目标微服务名称 ,ZuulRoute类型的一个属性
path: /zuul-emp/** # 用来代替目标微服务的名称 /**表示多层
ignored-services: '*' # 忽略所有的微服务 名称
# ignored-services:
# - consumer # 忽略指定指定微服务名称
prefix: /sl #给访问的路径添加统一 的前缀
配置为zuul ,从zuul 进入调用服务 必须按照 zuul 定义的规则来进行访问服务
http://localhost:9000/ sl/zuul-empfeign/get/fallback
Zuul过滤器
MyZuulFilter
package com.sl.zuul.config;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Component
public class MyZuulFilter extends ZuulFilter {
Logger logger = LoggerFactory.getLogger(MyZuulFilter.class);
@Override
public String filterType() {
//返回当前过滤器的类型,并决定当前过滤器在什么时候执行
//pre 表示 在目标微服务之前执行
String filterType = "pre";
return filterType;
}
@Override
public int filterOrder() {
return 0;
}
//判断当前请求是否过滤
//如果过滤 执行 run方法
//不过滤 直接放行
//shouldFilter 返回 true 调用 run 方法 false 放行
@Override
public boolean shouldFilter() {
//获取 RequestContext 对象
//threadLocal; 本地化;
// 你在上游通过threadLocal.set(T value) 存
// 你在下游 通过threadLocal.get() 方法取 ,因为是同一条河(同一线程)所以可以取;
RequestContext currentContext = RequestContext.getCurrentContext();
//获取 Request 对象
HttpServletRequest request = currentContext.getRequest();
String signal = request.getParameter("signal");
return "hello".equals(signal);
}
@Override
public Object run() throws ZuulException {
logger.info("当前请求要过滤,执行了 run() 方法");
//当前实现会忽略这个返回值 , 所以返回null,不做特殊处理;
return null;
}
}
SpringCloud小结
SpringBoot 和SpringCloud关系
SpringBoot是基础
SpringCloud要基于SpringBoot开发
SpringCloud 和Dubbo对比
核心:
Dubbo底层基于RPC
SpringCloud底层基于RestFul.也可以说是基于HTTP
其他区别:
SpringCloud相对于Dubbo功能更全面
SpringCloud 是一个一站式解决方案
SpringCloud能够天然的基于Spring全家桶开发
开发业务功能相关程度
Eureka ★★★★★
Ribbon ★★★
Feign ★★★★★
Hystrix ★★★
Zuul ★★★★★