Spring Cloud系列(二十二) Spring Cloud Feign配置详解(Finchley.RC2版本)


版本说明


	org.springframework.boot
	spring-boot-starter-parent
	2.0.2.RELEASE
	 



	UTF-8
	UTF-8
	1.8
	Finchley.RC2

Ribbon配置

全局配置

在application.properties中使用ribbon.=的方式来设置ribbon的各项默认参数。比如:

# 设置连接超时时间
ribbon.ConnectTimeout=500
# 设置读取超时时间
ribbon.ReadTimeout=2000

指定服务配置

在使用Spring Cloud Feign的时候,针对各个服务客户端进行个性化配置的方式与使用Spring Cloud Ribbon时的配置方式一样,都采用.ribbon.=的格式进行设置,而指代的Ribbon客户端就是我们使用@FeignClient注解的name或value属性,即服务名。

# 设置针对hello-service服务的连接超时时间
hello-service.ribbon.ConnectTimeout=500
# 设置针对hello-service服务的读取超时时间
hello-service.ribbon.ReadTimeout=2000

重试机制

Spring Cloud Feign默认实现了重试机制,默认重试5次,通过以下配置来配置相关属性。

# 设置针对hello-service服务的连接超时时间
hello-service.ribbon.ConnectTimeout=500
# 设置针对hello-service服务的读取超时时间
hello-service.ribbon.ReadTimeout=2000
# 设置针对hello-service服务所有操作请求都进行重试
hello-service.ribbon.OkToRetryOnAllOperations=true
# 设置针对hello-service服务切换实例的重试次数
hello-service.ribbon.MaxAutoRetriesNextServer=2
# 设置针对hello-service服务的当前实例的重试次数
hello-service.ribbon.MaxAutoRetries=1
# 设置断路器超时时间
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=2001

相关配置的说明

配置 说明
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds 断路器的超时时间需要大于ribbon的超时时间,不然不会触发重试。
hello-service.ribbon.ConnectTimeout 请求连接的超时时间
hello-service.ribbon.ReadTimeout 请求处理的超时时间
hello-service.ribbon.OkToRetryOnAllOperations 是否对所有操作请求都进行重试
hello-service.ribbon.MaxAutoRetriesNextServer 重试负载均衡其他的实例最大重试次数,不包括首次server,所有的提供者都重试失败,则返回失败
hello-service.ribbon.MaxAutoRetries 同一台实例最大重试次数,不包括首次调用

注意:断路器的超时时间和ribbon的超时时间是两个不同的概念,断路器的超时时间必须大于ribbon的超时时间,不然不会触发重试,因为断路器会直接熔断。

Feign重试和Ribbon重试

二者的重试机制相互独立,并无联系。但是因为用了feign肯定会用到ribbon,所以feign的重试机制相对来说比较鸡肋。另外,很重要的一点,不论是Feign重试还是Ribbon重试,如果开启了请求重试机制,对于get请求不会有太大的问题,但是对于post或者put请求就可能出现不可预计的后果。比如接口没有做幂等性,类似于对于一次订单支付过程,如果用户重复多次点击支付按钮,或者是网络异常导致订单已经支付成功了但没有及时反馈给用户,用户再次点击支付按钮,就可能造成重复扣款,造成严重后果。所以建议慎重设置这个参数,我建议关闭Feign重试和Ribbon重试,并且服务接口做幂等性等处理。

如果开启的话,建议合理配置Hystrix的超时时间,在一些没必要的重试请求执行时,根据Hystrix的超时时间,快速失败,结束重试。

测试

第一步,修改hello-service服务,也就是eureka-client-vFinchley.Rc2工程,修改FeignController,添加一个API,并让它随机睡眠0-3000ms

@GetMapping("hello")
public String hello(){
	System.out.println("hello");
	try {
		//休眠0-3000ms
		int sleepTime = new Random().nextInt(3000);
		Thread.sleep(sleepTime);
		System.out.println(sleepTime);
	} catch (InterruptedException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	return "hello";
}

第二步,修改feign-consumer服务,即feign-consumer-vFinchley.RC2工程,修改HelloService,添加对上面API的服务调用。

@GetMapping("feign/hello")
String testRetry();

第三步,修改FeignConsumerController,添加对testRetry()的调用

@GetMapping("testRetry")
public String testRetry(){
	return helloService.testRetry();
}

第四步,修改application.yml配置文件,配置Feign的重试机制

spring:
  application:
    name: feign-consumer #为服务命名
server:
  port: 5001
eureka:
  client:
    service-url: 
      defaultZone: http://localhost:1111/eureka/ #指定服务注册中心位置
  instance:
    prefer-ip-address: true
    instance-id: ${spring.cloud.client.ip-address}:${server.port}
hello-service:
  ribbon:
    # 配置首台服务器重试1次
    MaxAutoRetries: 1
    # 配置其他服务器重试两次
    MaxAutoRetriesNextServer: 2
    # 连接超时时间
    ConnectTimeout: 500
    # 请求处理时间
    ReadTimeout: 2000
    # 每个操作都开启重试机制
    OkToRetryOnAllOperations: true

# 配置断路器超时时间,默认是1000(1秒)
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 2001

启动服务注册中心,即eureka-server-vFinchley.Rc2工程

启动服务提供者hello-service,即eureka-client-vFinchley.Rc2 工程

启动服务消费者feign-consumer,即feign-consumer-vFinchley.RC2工程

请求localhost:5001/testRetry,直到遇见服务超时即可。

Spring Cloud系列(二十二) Spring Cloud Feign配置详解(Finchley.RC2版本)_第1张图片

可以看到我划线部分,第一次请求延迟时间为2628毫秒,请求超时,Feign客户端发起了请求重试,第二次请求延迟为1553毫秒,没有超时。Feign客户端在进行服务调用时经历了一次失败,但是通过重试机制,最终还是获得了请求结果。

Hystrix配置

全局配置

对于Hystrix的全局配置同Spring Cloud Ribbon的全局配置一样,直接使用它的默认配置前缀hystrix.command.default就可以进行设置,比如设置全局的超时时间

# 设置熔断超时时间
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=2000

指定命令配置

配置方法和传统的Hystrix命令的参数配置相似,采用hystrix.command.作为前缀。而默认情况下会采用Feign客户端中的方法名作为标识,所以针对上一节重试机制中对/feign/hello接口的熔断超时时间的配置可以通过其方法名作为来进行配置,比如:

hystrix.command.testRetry.execution.isolation.thread.timeoutInMilliseconds=1000

在使用指定命令配置的时候,需要注意,由于方法名很有可能重复,这个时候相同方法名的Hystrix配置会共用,所以尽量避免方法重名。当然,也可以重写Feign.Builder的实现,并在应用主类中创建它的实例来覆盖自动化配置的HystrixFeign.Builder实现。

禁用Hystrix

1.全局配置

# 关闭Hystrix功能
feign.hystrix.enabled=false
# 关闭熔断功能
hystrix.command.default.execution.timeout.enabled=false

2.针对某个服务禁用Hystrix

第一步,构建一个关闭Hystrix的配置类

@Configuration
public class DisabledHystrixConfiguration {

	@Bean
	@Scope("prototype")
	public Feign.Builder feignBuilder(){
		return Feign.builder();
	}
}

第二步,在需要关闭Hystrix功能的Feign客户端的服务绑定接口类,通过@FeignClient注解的configuration属性指定

@FeignClient(value = "hello-service", fallback = HelloServiceFallback.class, configuration = {DisabledHystrixConfiguration.class })
public interface HelloService {

	@GetMapping("hello")
	String hello();

	@GetMapping("feign/hello1")
	public User hello1(@RequestParam("name") String name, @RequestParam("age") int age);

	@GetMapping("feign/hello2")
	public User hello2(@RequestHeader("name") String name, @RequestHeader("age") int age);

	@PostMapping("feign/hello3")
	public User hello3(@RequestBody User user);

	@GetMapping("feign/hello")
	String testRetry();
}

此时全局的Hystrix是开启的,只是关闭了HelloService的Hystrix功能。

服务降级配置

第一步,为Feign客户端的定义接口编写一个具体的接口实现类,并重写每个方法的服务降级逻辑。

@Component//此注解不加无法扫描到这个类
public class HelloServiceFallback implements HelloService{

	@Override
	public String hello() {
		return "error";
	}

	@Override
	public User hello1(String name, int age) {
		return new User(1,"error",1);
	}

	@Override
	public User hello2(String name, int age) {
		// TODO Auto-generated method stub
		return new User(1,"error",1);
	}

	@Override
	public User hello3(User user) {
		// TODO Auto-generated method stub
		return new User(1,"error",1);
	}

	@Override
	public String testRetry() {
		// TODO Auto-generated method stub
		return "error";
	}

}

第二步,在服务绑定接口中通过@FeignClient注解的fallback属性指定对应的服务降级实现类。

@FeignClient(value="hello-service",fallback=HelloServiceFallback.class)
public interface HelloService {

	@GetMapping("hello")
	String hello();
	
	@GetMapping("feign/hello1")
	public User hello1(@RequestParam("name") String name,@RequestParam("age") int age);
	
	@GetMapping("feign/hello2")
	public User hello2(@RequestHeader("name") String name,@RequestHeader("age") int age);
	
	@PostMapping("feign/hello3")
	public User hello3(@RequestBody User user);
	
	@GetMapping("feign/hello")
	String testRetry();
}

测试

只启动服务注册中心和服务消费者feign-consumer,即feign-consumer-vFinchley.RC2,不启动服务提供者hello-service。

访问http://localhost:5001/hello1,结果说明直接触发了服务降级。

User [id=1, name=error, age=1]
User [id=1, name=error, age=1] 
User [id=1, name=error, age=1]

注意:feign.hystrix.enabled默认值为false,Feign默认关闭Hystrix熔断功能,需要先通过feign.hystrix.enabled=true配置开启。

其他配置

请求压缩

Spring Cloud Feign支持对请求与响应进行GZIP压缩,以减少通信过程中的性能损耗,只需要下面两个参数设置就可以开启请求与响应的压缩功能。

# 配置请求GZIP压缩
feign.compression.request.enabled=true
# 配置响应GZIP压缩
feign.compression.response.enabled=true

Spring Cloud Feign也对请求压缩支持更细致的设置。

# 配置请求GZIP压缩
feign.compression.request.enabled=true
# 配置压缩支持的MIME TYPE 默认值text/xml,application/xml,application/json
feign.compression.request.mime-types=text/xml,application/xml,application/json
# 配置压缩数据大小的下限 默认值2048
feign.compression.request.min-request-size=2048 

日志配置

Spring Cloud Feign在构建被@FeignClient注解修饰的服务客户端时,会为每一个客户端都创建一个feign.Logger实例,我们可以利用该日志对象的DEBUG模式来帮助分析Feign的请求细节。可以使用logging.level.的参数配置格式来指定Feign客户端的DEBUG日志,其中为客户端定义接口的完整路径,比如对于HelloService可以这么配置

logging.level.com.wya.springboot.service.HelloService=DEBUG

在application.yml文件中这样设置

logging:
  level:
    com:
      wya:
        springboot:
          service:
            HelloService: DEBUG

我的版本就可以直接输出日志了,老版本的话这样还不能实现对DEBUG日志的输出,因为Feign客户端默认的logging.level对象定义为NONE级别,该级别不会记录Feign调用过程中的信息,我们需要调整它的级别。

对于全局的日志级别,在主类直接加入Logger.Level的Bean创建

@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class Application {
	
	@Bean
	Logger.Level feignLoggerLevel(){
		return Logger.Level.FULL;
	}

	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
}

对于不同的Feign客户端指定配置类可以通过实现配置类来调整日志级别。

@Configuration
public class FullLoggerConfiguration{
	
	@Bean
	Logger.Level feignLoggerLevel(){
		return Logger.Level.FULL;
	}

}

然后通过@FeignClient注解的configuration属性指定。

@FeignClient(value="hello-service",fallback=HelloServiceFallback.class,configuration={FullLoggerConfiguration.class})
public interface HelloService {

	@GetMapping("hello")
	String hello();
	
	@GetMapping("feign/hello1")
	public User hello1(@RequestParam("name") String name,@RequestParam("age") int age);
	
	@GetMapping("feign/hello2")
	public User hello2(@RequestHeader("name") String name,@RequestHeader("age") int age);
	
	@PostMapping("feign/hello3")
	public User hello3(@RequestBody User user);
	
	@GetMapping("feign/hello")
	String testRetry();
}

再次请求http://localhost:5001/hello1接口我们就可以看到控制台打印请求的详细信息。

2018-09-12 15:48:07.170  INFO 9036 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient    : Disable delta property : false
2018-09-12 15:48:07.171  INFO 9036 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient    : Single vip registry refresh property : null
2018-09-12 15:48:07.171  INFO 9036 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient    : Force full registry fetch : false
2018-09-12 15:48:07.171  INFO 9036 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient    : Application is null : false
2018-09-12 15:48:07.171  INFO 9036 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient    : Registered Applications size is zero : true
2018-09-12 15:48:07.171  INFO 9036 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient    : Application version is -1: false
2018-09-12 15:48:07.171  INFO 9036 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient    : Getting all instance registry info from the eureka server
2018-09-12 15:48:07.222  INFO 9036 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient    : The response status is 200
2018-09-12 15:48:08.349  INFO 9036 --- [nio-5001-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring FrameworkServlet 'dispatcherServlet'
2018-09-12 15:48:08.349  INFO 9036 --- [nio-5001-exec-2] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
2018-09-12 15:48:08.366  INFO 9036 --- [nio-5001-exec-2] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 17 ms
2018-09-12 15:48:08.578 DEBUG 9036 --- [hello-service-1] com.wya.springboot.service.HelloService  : [HelloService#hello1] ---> GET http://hello-service/feign/hello1?name=test1&age=22 HTTP/1.1
2018-09-12 15:48:08.578 DEBUG 9036 --- [hello-service-1] com.wya.springboot.service.HelloService  : [HelloService#hello1] ---> END HTTP (0-byte body)
2018-09-12 15:48:08.580  INFO 9036 --- [hello-service-1] s.c.a.AnnotationConfigApplicationContext : Refreshing SpringClientFactory-hello-service: startup date [Wed Sep 12 15:48:08 CST 2018]; parent: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@1fde5d22
2018-09-12 15:48:08.638  INFO 9036 --- [hello-service-1] f.a.AutowiredAnnotationBeanPostProcessor : JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
2018-09-12 15:48:08.846  INFO 9036 --- [hello-service-1] c.netflix.config.ChainedDynamicProperty  : Flipping property: hello-service.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2018-09-12 15:48:08.865  INFO 9036 --- [hello-service-1] c.n.u.concurrent.ShutdownEnabledTimer    : Shutdown hook installed for: NFLoadBalancer-PingTimer-hello-service
2018-09-12 15:48:08.886  INFO 9036 --- [hello-service-1] c.netflix.loadbalancer.BaseLoadBalancer  : Client: hello-service instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=hello-service,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null
2018-09-12 15:48:08.892  INFO 9036 --- [hello-service-1] c.n.l.DynamicServerListLoadBalancer      : Using serverListUpdater PollingServerListUpdater
2018-09-12 15:48:08.917  INFO 9036 --- [hello-service-1] c.netflix.config.ChainedDynamicProperty  : Flipping property: hello-service.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2018-09-12 15:48:08.918  INFO 9036 --- [hello-service-1] c.n.l.DynamicServerListLoadBalancer      : DynamicServerListLoadBalancer for client hello-service initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=hello-service,current list of Servers=[192.168.1.227:2222],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone;	Instance count:1;	Active connections count: 0;	Circuit breaker tripped count: 0;	Active connections per server: 0.0;]
},Server stats: [[Server:192.168.1.227:2222;	Zone:defaultZone;	Total Requests:0;	Successive connection failure:0;	Total blackout seconds:0;	Last connection made:Thu Jan 01 08:00:00 CST 1970;	First connection made: Thu Jan 01 08:00:00 CST 1970;	Active Connections:0;	total failure count in last (1000) msecs:0;	average resp time:0.0;	90 percentile resp time:0.0;	95 percentile resp time:0.0;	min resp time:0.0;	max resp time:0.0;	stddev resp time:0.0]
]}ServerList:org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList@58287100
2018-09-12 15:48:09.193 DEBUG 9036 --- [hello-service-1] com.wya.springboot.service.HelloService  : [HelloService#hello1] <--- HTTP/1.1 200 (614ms)
2018-09-12 15:48:09.193 DEBUG 9036 --- [hello-service-1] com.wya.springboot.service.HelloService  : [HelloService#hello1] content-type: application/json;charset=UTF-8
2018-09-12 15:48:09.193 DEBUG 9036 --- [hello-service-1] com.wya.springboot.service.HelloService  : [HelloService#hello1] date: Wed, 12 Sep 2018 07:48:09 GMT
2018-09-12 15:48:09.193 DEBUG 9036 --- [hello-service-1] com.wya.springboot.service.HelloService  : [HelloService#hello1] transfer-encoding: chunked
2018-09-12 15:48:09.193 DEBUG 9036 --- [hello-service-1] com.wya.springboot.service.HelloService  : [HelloService#hello1] 
2018-09-12 15:48:09.207 DEBUG 9036 --- [hello-service-1] com.wya.springboot.service.HelloService  : [HelloService#hello1] {"id":1,"name":"test1","age":22}
2018-09-12 15:48:09.208 DEBUG 9036 --- [hello-service-1] com.wya.springboot.service.HelloService  : [HelloService#hello1] <--- END HTTP (32-byte body)
2018-09-12 15:48:09.246 DEBUG 9036 --- [hello-service-2] com.wya.springboot.service.HelloService  : [HelloService#hello2] ---> GET http://hello-service/feign/hello2 HTTP/1.1
2018-09-12 15:48:09.246 DEBUG 9036 --- [hello-service-2] com.wya.springboot.service.HelloService  : [HelloService#hello2] name: test2
2018-09-12 15:48:09.246 DEBUG 9036 --- [hello-service-2] com.wya.springboot.service.HelloService  : [HelloService#hello2] age: 22
2018-09-12 15:48:09.246 DEBUG 9036 --- [hello-service-2] com.wya.springboot.service.HelloService  : [HelloService#hello2] ---> END HTTP (0-byte body)
2018-09-12 15:48:09.268 DEBUG 9036 --- [hello-service-2] com.wya.springboot.service.HelloService  : [HelloService#hello2] <--- HTTP/1.1 200 (21ms)
2018-09-12 15:48:09.269 DEBUG 9036 --- [hello-service-2] com.wya.springboot.service.HelloService  : [HelloService#hello2] content-type: application/json;charset=UTF-8
2018-09-12 15:48:09.269 DEBUG 9036 --- [hello-service-2] com.wya.springboot.service.HelloService  : [HelloService#hello2] date: Wed, 12 Sep 2018 07:48:09 GMT
2018-09-12 15:48:09.269 DEBUG 9036 --- [hello-service-2] com.wya.springboot.service.HelloService  : [HelloService#hello2] transfer-encoding: chunked
2018-09-12 15:48:09.269 DEBUG 9036 --- [hello-service-2] com.wya.springboot.service.HelloService  : [HelloService#hello2] 
2018-09-12 15:48:09.270 DEBUG 9036 --- [hello-service-2] com.wya.springboot.service.HelloService  : [HelloService#hello2] {"id":2,"name":"test2","age":22}
2018-09-12 15:48:09.270 DEBUG 9036 --- [hello-service-2] com.wya.springboot.service.HelloService  : [HelloService#hello2] <--- END HTTP (32-byte body)
2018-09-12 15:48:09.292 DEBUG 9036 --- [hello-service-3] com.wya.springboot.service.HelloService  : [HelloService#hello3] ---> POST http://hello-service/feign/hello3 HTTP/1.1
2018-09-12 15:48:09.293 DEBUG 9036 --- [hello-service-3] com.wya.springboot.service.HelloService  : [HelloService#hello3] Content-Type: application/json;charset=UTF-8
2018-09-12 15:48:09.293 DEBUG 9036 --- [hello-service-3] com.wya.springboot.service.HelloService  : [HelloService#hello3] Content-Length: 32
2018-09-12 15:48:09.293 DEBUG 9036 --- [hello-service-3] com.wya.springboot.service.HelloService  : [HelloService#hello3] 
2018-09-12 15:48:09.293 DEBUG 9036 --- [hello-service-3] com.wya.springboot.service.HelloService  : [HelloService#hello3] {"id":3,"name":"test1","age":22}
2018-09-12 15:48:09.293 DEBUG 9036 --- [hello-service-3] com.wya.springboot.service.HelloService  : [HelloService#hello3] ---> END HTTP (32-byte body)
2018-09-12 15:48:09.346 DEBUG 9036 --- [hello-service-3] com.wya.springboot.service.HelloService  : [HelloService#hello3] <--- HTTP/1.1 200 (52ms)
2018-09-12 15:48:09.346 DEBUG 9036 --- [hello-service-3] com.wya.springboot.service.HelloService  : [HelloService#hello3] content-type: application/json;charset=UTF-8
2018-09-12 15:48:09.346 DEBUG 9036 --- [hello-service-3] com.wya.springboot.service.HelloService  : [HelloService#hello3] date: Wed, 12 Sep 2018 07:48:09 GMT
2018-09-12 15:48:09.347 DEBUG 9036 --- [hello-service-3] com.wya.springboot.service.HelloService  : [HelloService#hello3] transfer-encoding: chunked
2018-09-12 15:48:09.347 DEBUG 9036 --- [hello-service-3] com.wya.springboot.service.HelloService  : [HelloService#hello3] 
2018-09-12 15:48:09.347 DEBUG 9036 --- [hello-service-3] com.wya.springboot.service.HelloService  : [HelloService#hello3] {"id":3,"name":"test1","age":22}
2018-09-12 15:48:09.347 DEBUG 9036 --- [hello-service-3] com.wya.springboot.service.HelloService  : [HelloService#hello3] <--- END HTTP (32-byte body)
2018-09-12 15:48:09.895  INFO 9036 --- [erListUpdater-0] c.netflix.config.ChainedDynamicProperty  : Flipping property: hello-service.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647

对于Feign的Logger级别有如下四种,可根据实际情况调整。

  1. NONE:不记录任何信息
  2. BASIC:仅记录请求方法、URL以及响应状态码和执行时间
  3. HEADERS:除了记录BASIC级别的信息外,还会记录请求和响应的头信息
  4. FULL:记录所有请求与响应的明细,包括头信息、请求头、元数据等。

Feign原理与覆盖默认配置

推荐阅读:深入理解Feign之源码解析,这里我就不赘述了。

你可能感兴趣的:(Spring,Cloud)