2 断路器
在微服务架构中,存在着多个微服务,彼此之间可能存在依赖关系,当某个单元出现故障或者网络不通时,就会因为依赖关系形成故障蔓延,最终导致整个系统的瘫痪,相对于传统架构更加不稳定。为了解决这样的问题,因此产生了断路器模式。
断路器本身是一种开关装置,用于在电路上保护线路过载,当线路中有电器发生短路时,“断路器”能够及时切断故障电源,防止发生过载、发热甚至起火等严重后果。
在分布式架构中,断路器模式的作用是类似的,当某个微服务发生故障时,通过断路器的故障监控,向调用方返回一个错误响应,而不是长时间的等待,这样就不会使得线程因调用故障服务被长时间占用不释放,避免了故障在分布式系统中的蔓延。
Netflix Hystrix
在Spring Cloud中使用了Hystrix来实现断路器的功能。Hystrix是Netflix的分布式套件之一,该框架目标在于通过控制那些访问远程系统、服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。Hystrix具备拥有回退机制和断路器功能的线程和信号隔离,请求缓存和请求打包,以及监控和配置等功能。
在前文中,已经创建过服务注册中心,两个服务提供者,两个服务消费者。
2.1 Ribbon消费者引入Hystrix
先在Ribbon实现的消费者中引入Hystrix。在pom.xml中添加hystrix依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
然后在主类中添加注解开启断路器功能:@EnableCircuitBreaker(或@EnableHystrix,两者等价)。
改造原来的服务消费方式,新增ComputeService类,在使用ribbon消费服务的函数上增加@HystrixCommand注解来指定回调方法,指明当服务消费失败时回调的函数:
@Service
public class ComputeService {
@Autowired
RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "addServiceFallback")
public String addService(){
return restTemplate.getForEntity("http://COMPUTE-SERVICE/add?a=10&b=20", String.class).getBody();
}
public String addServiceFallback() {
return "error";
}
}
HystrixCommand表明该方法为hystrix包裹,可以对依赖服务进行隔离,降级,快速失败,快速重试等。该注解属性较多,其中有几个核心属性:
fallbackMethod:降级方法,即当前服务调用超时时进行回调的方法。
commandProperties:普通配置属性,可以配置HystrixCommand对应属性,例如采用线程池还是信号量隔离,熔断器熔断规则等等。
ignoreExceptions:忽略的异常,默认HystrixBadRequestException不计入失败
groupKey:组名称,默认使用类名称
commandKey:命令名称,默认使用方法名。
服务接口定义完毕之后,通过控制器调用服务:
@RestController
public class ConsumerController {
// @Autowired
// RestTemplate restTemplate;
// @RequestMapping(value = "/add", method = RequestMethod.GET)
// public String add() {
// return restTemplate.getForEntity("http://COMPUTE-SERVICE/add?a=10&b=20", String.class).getBody();
// }
@Resource
private ComputeService computeService;
@RequestMapping(value = "/add", method = RequestMethod.GET)
public String add() {
return computeService.addService();
}
}
结果验证:开启服务注册中心和服务提供者,启动使用Ribbon的服务消费者,调用http://localhost:3333/add,显示add结果为30。然后关掉服务提供者,此时就是服务出现故障了,再次调用接口,断路器就会发挥作用,addService服务消费失败,回调addServiceFallback方法,输出error。
2.2 Feign使用Hystrix
Feigh中已经依赖了Hystrix,因此不需要额外引入依赖包。
在ComputeClient接口上指定断路回调类:
@FeignClient(value="compute-service", fallback = ComputeClientHystrix.class)
public interface ComputeClient {
@RequestMapping(method = RequestMethod.GET)
Integer add(@RequestParam(value = "a") Integer a, @RequestParam(value = "b") Integer b);
}
注意:这里的add方法上必须指明请求的方法类型。
新建回调类,需实现ComputeClient接口:
@Component
public class ComputeClientHystrix implements ComputeClient{
public Integer add(@RequestParam(value = "a") Integer a, @RequestParam(value = "b") Integer b) {
return -9999;
}
}
注意:在idea环境下,控制器中注入的computeClient在提示报错,但实际是没有问题的。
运行服务注册中心和使用Feign的消费者后,调用http://localhost:5555/add接口,会输出-9999,表明执行了断路回调函数。
2.3 断路器工作原理
服务端的服务降级逻辑会因为hystrix命令调用依赖服务超时而触发,也就是说调用服务超时会进入断路回调逻辑处理。但是即使这样,受限于Hystrix超时时间的问题,调用依然会有可能产生堆积。
这个时候断路器就会发挥作用。这里涉及到断路器的三个重要参数:
快照时间窗
断路器确定是否打开需要统计一些请求和错误数据,而统计的时间范围就是快照时间窗,默认为最近的10秒。
请求总数下限
在快照时间窗内,必须满足请求总数下限才有资格熔断。默认为20,意味着在10秒内,如果该hystrix命令的调用次数不足20,即使所有的请求都超时或者其他原因失败,断路器都不会打开。
错误百分比下限
当请求总数在快照时间窗口内超过了下限,比如发生了30次调用,如果在这30次调用中有16次发生了超时异常,也就是超过了50%错误百分比,在默认设定50%下限情况下,这时候就会将断路器打开。
因此,断路器打开的条件是:在时间快照窗口期(默认为10s)内,至少发生20次服务调用,并且服务调用错误率超过50%。
不满足条件时断路器并不会打开,服务调用错误只会触发服务降级,也就是调用fallback函数,每个请求时间延迟就是近似hystrix的超时时间。如果将超时时间设置为5秒,那么每个请求都要延迟5每秒才会返回。当断路器在10秒内发现请求总数超过20并且错误率超过50%,这时候断路器会打开。之后再有请求调用的时候,将不会调用主逻辑,而是直接调用降级逻辑,这个时候就不会等待5秒之后才会返回fallback。通过断路器实现自动发现错误并将降级逻辑切换为主逻辑,减少响应延迟的效果。
在断路器打开之后,处理逻辑并没有结束,此时降级逻辑已经被切换为主逻辑了,那么原来的主逻辑要如何恢复呢?实际上hystrix也实现了这一点:当断路器打开,对主逻辑进行熔断之后,hystrix会启动一个休眠时间窗,在这个时间窗内,降级逻辑是临时的主逻辑,当休眠时间窗到期,断路器将进入半开状态,释放一次请求到原来的主逻辑,如果此次请求正常返回,那么断路器将进行闭合,主逻辑恢复,如果这次请求依然有问题,断路器继续进入打开状态,休眠时间窗重新计时。
换句话说,断路器每隔一段时间进行一次重试,看看原来的主逻辑是否可用,可用就关闭,不可用就继续打开。
通过上面的机制,hystrix的断路器实现了对依赖资源故障的处理,对降级策略的自动切换以及对主逻辑的自动恢复。这使得我们的微服务在依赖外部服务或资源的时候得到了非常好的保护,同时对于一些具备降级逻辑的业务需求可以实现自动化的切换和恢复,相比于设置开关由监控和运维来进行切换的传统实现方式显得更为智能和高效。
2.4 服务隔离
Hystrix的容错方法有服务降级,服务隔离和熔断,上文已经介绍过服务降级和服务熔断,这里要说的是服务隔离。
Hystrix的服务隔离有两种方式,分别是线程隔离和信号量隔离。
线程隔离
Hystrix通过命令模式HystrixCommand包装依赖调用逻辑,比如查询订单->订单command,查询商品->商品command,查询用户->用户command,每个类型的command对应一个线程池。创建好的线程池是被放入ConcurrentHashMap中的,比如查询订单:
final static ConcurrentHashMap<String,HystrixThreadPool> threadPools = new ConcurrentHashMap<~>();
threadPools.put(“hystrix-order”, new HystrixThreadPoolDefault(threadPoolKey, propertiesBuilder));
当第二次查询订单请求过来的时候,则可以直接从Map中获取该线程池。如果线程池中的线程已被全部使用完,就拒绝后续服务调用。线程池核心线程数跟最大线程数一样,默认为10。通常取值为:99.5%的服务响应时间 * QPS + 缓冲值。比如:每秒能处理1000个请求,99%的请求响应时间是60ms,那么核心线程数是:1000 * 0.06+12。
可以通过ApacheBench来进行测试,通过命令:
ab -c 10 -n 1000 http://localhost:3333/add
来并发请求上文中构建的服务。可以看到,当并发数为10时,请求全部正常通过。当并发数超过10时,就会有因线程池拒绝执行而导致失败的请求。此时在服务实例中配置一个参数:hystrix.threadpool.default.coreSize=100,此时只要并发数不超过100,请求就会全部正常通过。
2.5 Hystrix相关的常用配置信息
超时时间(默认1000ms,单位:ms)
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds
hystrix.command.HystrixCommandKey.execution.isolation.thread.timeoutInMilliseconds
线程池核心线程数
hystrix.threadpool.default.coreSize(默认为10)
Queue
hystrix.threadpool.default.maxQueueSize(最大排队长度。默认-1,使用SynchronousQueue。其他值则使用 LinkedBlockingQueue。如果要从-1换成其他值则需重启,即该值不能动态调整,若要动态调整,需要使用到下边这个配置)
hystrix.threadpool.default.queueSizeRejectionThreshold(排队线程数量阈值,默认为5,达到时拒绝,如果配置了该选项,队列的大小是该队列)
注意:如果maxQueueSize=-1的话,则该选项不起作用
断路器
hystrix.command.default.circuitBreaker.requestVolumeThreshold(当在配置时间窗口内达到此数量的失败后,进行短路。默认20个)
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds(短路多久以后开始尝试是否恢复,默认5s)
hystrix.command.default.circuitBreaker.errorThresholdPercentage(出错百分比阈值,当达到此阈值后,开始短路。默认50%)
fallback
hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests(调用线程允许请求HystrixCommand.GetFallback()的最大数量,默认10。超出时将会有异常抛出,注意:该项配置对于THREAD隔离模式也起作用)
3 分布式配置中心
Spring Cloud中有几大核心组件,分别是Eureka服务发现,Hystrix断路器,Config配置中心,Zuul服务网关。前文简略讲了Eureka服务注册与发现,Hystrix断路器,这里要讲的是Config配置中心。
Spring Cloud Config为服务端和客户端提供了分布式系统的外部化配置支持。配置服务器为各应用的所有环境提供了一个中心化的外部配置,它实现了对服务端和客户端,对Spring Environment和PropertySource抽象的映射,所以除了适用于Spring构建的应用程序外,也可以在其他应用程序中使用。作为一个应用可以通过配置部署管道来进行测试或投入生产,可以分别为这些环境创建配置,并且在需要迁移环境的时候获取对应环境的配置来运行。
配置服务器默认采用git来存储配置信息,这样就有助于对环境配置进行版本管理,并且可以通过git客户端工具来方便的管理和访问配置内容。还可以通过本地文件系统进行存储。
3.1 构建Config Server
构建配置服务器很简单,只需要三步。首先在pom.xml文件中引入依赖:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.5.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Brixton.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
接着创建主类,开启配置中心注解:
@EnableConfigServer
@SpringBootApplication
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).web(true).run(args);
}
}
然后在application.properties中配置服务信息以及git信息,例如:
spring.application.name=config-server
server.port=7001
#git管理配置
spring.cloud.config.server.git.uri=https://github.com/username/mysc
spring.cloud.config.server.git.searchPaths=mysc-configrepo/src/main/resources
spring.cloud.config.server.git.username=username
spring.cloud.config.server.git.password=password
说明:
spring.cloud.config.server.git.uri:配置git仓库位置
spring.cloud.config.server.git.searchPaths:配置仓库路径下的相对搜索位置,可以配置多个
spring.cloud.config.server.git.username:访问git仓库的用户名
spring.cloud.config.server.git.password:访问git仓库的用户密码
这里将配置文件放在mysc-configrepo/src/main/resources下面。
Spring Cloud Config也提供本地存储配置的方式。我们只需要设置属性spring.profiles.active=native,Config Server会默认从应用的src/main/resource目录下检索配置文件。也可以通过spring.cloud.config.server.native.searchLocations=file:F:/properties/属性来指定配置文件的位置。虽然Spring Cloud Config提供了这样的功能,但是为了支持更好的管理内容和版本控制的功能,还是推荐使用git的方式。
创建工程mysc-configrepo,在resources目录下创建配置文件:
mysc.properties
mysc-dev.properties
mysc-test.properties
mysc-prd.properties
每个配置文件中都有一个form属性:
form=git-default-1.0
form=git-dev-1.0
form=git-test-1.0
form=git-prd-1.0
将配置文件提交到git进行管理。直接通过url就可以访问配置内容了。
URL与配置文件的映射关系如下:
/{application}/{profile}[/{label}] -> /{application}-{profile}.properties和/{label}/{application}-{profile}.properties
{label}对应git上不同的分支,默认为master。
访问http://localhost:7001/mysc/prd.输出结果为:
{"name":"mysc","profiles":["prd"],"label":null,"version":"b1abd7079919312a5ec9bb060ab5929abf59a46e","propertySources":[{"name":"https://github.com/howetong/mysc/mysc-configrepo/src/main/resources/mysc-prd.properties","source":{"form":"git-prd-1.0"}},{"name":"https://github.com/howetong/mysc/mysc-configrepo/src/main/resources/mysc.properties","source":{"form":"git-defalut-1.0"}}]}
3.2 微服务端映射配置
创建一个maven工程mysc-configclient,引入依赖:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.5.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Brixton.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
创建主类:
@SpringBootApplication
public class ConfigClientApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(ConfigClientApplication.class).web(true).run(args);
}
}
创建bootstrap.properties配置,来指定config server:
spring.application.name=mysc
spring.cloud.config.profile=dev
spring.cloud.config.label=master
spring.cloud.config.uri=http://localhost:7001/
server.port=7002
spring.application.name:对应前配置文件中的{application}部分
spring.cloud.config.profile:对应前配置文件中的{profile}部分
spring.cloud.config.label:对应前配置文件的git分支
spring.cloud.config.uri:配置中心的地址
注意,此处的属性必须配置在bootstrap.properties中,onfig部分内容才能被正确加载。因为config的相关配置会先于application.properties,而bootstrap.properties的加载也是先于applicaion.properties。
接着创建一个rest接口如下:
@RefreshScope
@RestController
class TestController {
@Value("${form}")
private String from;
@RequestMapping("/from")
public String from() {
return this.from;
}
}
启动配置中心和sc-configclient,访问rest接口:http://localhost:7002/from。
输出bootstrap.properties中指定的mysc-dev.properties中form值:git-dev-1.0。
在上面直接通过url如http://localhost:7001/mysc/prd进行访问时可以看到,除了指定的mysc-prd.properties 配置文件外,mysc.properties也获取到了。这说明应用名.properties是默认获取的。因此指定profile为dev,可以获取mysc-dev.properties和mysc.properties。如果还想获得其他配置文件也是可以的,如指定spring.cloud.config.profile=dev,common后还可以获取mysc-common.properties。
3.3 配置中心注册为服务
传统做法
通常在生产环境中,Config Server与服务注册中心一样,需要扩展为高可用的集群。在之前实现的config-server基础上来实现高可用非常简单,不需要额外的配置,只需要遵守一个配置规则:将所有的Config Server都指向同一个git仓库,这样所有的配置内容就通过统一的共享文件系统来维护,而客户端指定Config Server位置时,只要配置Config Server外的均衡负载即可。
注册为服务
虽然通过服务端负载均衡已经能够实现,但是作为架构内的配置管理,本身其实也是可以看做架构中的一个微服务。所以,另外一种方式就是将config-server也注册为服务,这样所有客户端就能以服务的方式就行访问。通过这种方法,只需要启动多个指向同一git仓库位置的config-server就能实现高可用了。
在config-server原基础上添加依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
在application.properties中配置参数eureka.client.serviceUrl.defaultZone以指定服务注册中心的位置(在原有基础上添加):
# 配置服务注册中心
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
在主类中添加@EnableDiscoveryClient注解,使服务能够自动注册。这样配置中心就能注册为服务了。
然后配置服务消费者mysc-configclient:同样先添加eureka依赖包,接着配置bootstrap.properties:
spring.application.name=mysc
server.port=7002
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
spring.cloud.config.discovery.enabled=true
spring.cloud.config.discovery.serviceId=config-server
spring.cloud.config.profile=dev
其中,通过eureka.client.serviceUrl.defaultZone参数指定服务注册中心,用于服务的注册与发现。
再将spring.cloud.config.discovery.enabled参数设置为true,开启通过服务来访问Config Server的功能。
最后利用spring.cloud.config.discovery.serviceId参数来指定Config Server注册的服务名。
这里的spring.application.name和spring.cloud.config.profile如之前通过URI的方式访问时候一样,用来定位Git中的资源。
在主类中添加@EnableDiscoveryClient注解,用来发现config-server服务。
启动服务注册中心,配置中心服务提供者和mysc-configclient消费者后,访问http://localhost:7002/from接口,输出git仓库中mysc-dev.properties文件的form属性。
3.4 配置刷新
配置中心支持配置文件的实时更新。
在mysc-configclient的pom.xml中增加监控依赖包,其中包含/refresh刷新api。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
访问http://localhost:7002/from,此时输出git-dev-1.0。
修改git仓库中的mysc-dev.properties文件中form=git-dev-2.0,再次访问后可以看到返回内容仍然为git-dev-1.0。因为需要通过refresh接口进行刷新之后才能获取更新。
通过postman工具发送一个post请求,地址为http://localhost:7002/refresh,可以看到返回内容如下:
[
"from"
],
代表form参数的配置内容被更新了(注意这里直接通过地址栏发送的话是get请求,不支持refresh接口)。
再次访问可以看到返回的内容是更新后的值了。
还可以利用git仓库的web hook来实现git仓库中的内容修改触发应用程序的属性更新。但是若所有触发操作均需要手工去维护web hook中的应用位置的话,随着系统的扩展,维护将变得越来越困难。那么有什么好的解决办法呢?那就是消息代理中间件。消息代理中间件可以将消息路由到一个或多个目的地。利用该功能可以解决问题。这里使用rabbitMQ,关于rabbitMQ的安装问题这里就不说了。
RabbitMQ实现
在mysc-configclient的pom.xml中添加依赖包:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
在配置文件中增加关于RabbitMQ的连接信息:
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=springcloud
spring.rabbitmq.password=123456
复制一个注册中心客户端,端口为7003。
启动注册中心服务提供者和两个注册中心客户端,可以在客户端控制台下看到如下内容:
Mapped "{[/bus/refresh],methods=[POST]}" onto public void org.springframework.cloud.bus.endpoint.RefreshBusEndpoint.refresh(java.lang.String)
修改服务注册中心中的mysc-dev.properties中的值,发送POST请求到其中的一个/bus/refresh。然后分别访问两个服务注册客户端的from接口,此都会返回最新的参数值。通过消息总线,发送一次更新请求就能更新全部。
原理分析
通过spring cloud bus与spring cloud config的整合,并以rabbitMQ作为消息代理,实现了应用配置的动态更新。
整个方案的架构如上图所示,其中包含了Git仓库、Config Server、以及微服务“Service A”的三个实例,这三个实例中都引入了Spring Cloud Bus,所以他们都连接到了RabbitMQ的消息总线上。
当我们将系统启动起来之后,“Service A”的三个实例会请求Config Server以获取配置信息,Config Server根据应用配置的规则从Git仓库中获取配置信息并返回。
此时,若我们需要修改“Service A”的属性。首先,通过Git管理工具去仓库中修改对应的属性值,但是这个修改并不会触发“Service A”实例的属性更新。我们向“Service A”的实例3发送POST请求,访问/bus/refresh接口。此时,“Service A”的实例3就会将刷新请求发送到消息总线中,该消息事件会被“Service A”的实例1和实例2从总线中获取到,并重新从Config Server中获取他们的配置信息,从而实现配置信息的动态更新。
而从Git仓库中配置的修改到发起/bus/refresh的POST请求这一步可以通过Git仓库的Web Hook来自动触发。由于所有连接到消息总线上的应用都会接受到更新请求,所以在Web Hook中就不需要维护所有节点内容来进行更新,从而解决了通过Web Hook来逐个进行刷新的问题。
指定刷新范围
上面的例子中,我们通过向服务实例请求Spring Cloud Bus的/bus/refresh接口,从而触发总线上其他服务实例的/refresh。但是有些特殊场景下(比如:灰度发布),我们希望可以刷新微服务中某个具体实例的配置。
Spring Cloud Bus对这种场景也有很好的支持:/bus/refresh接口还提供了destination参数,用来定位具体要刷新的应用程序。比如,我们可以请求/bus/refresh?destination=customers:9000,此时总线上的各应用实例会根据destination属性的值来判断是否为自己的实例名,若符合才进行配置刷新,若不符合就忽略该消息。
destination参数除了可以定位具体的实例之外,还可以用来定位具体的服务。定位服务的原理是通过使用Spring的PathMatecher(路径匹配)来实现,比如:/bus/refresh?destination=customers:**,该请求会触发customers服务的所有实例进行刷新。
架构优化
既然Spring Cloud Bus的/bus/refresh接口提供了针对服务和实例进行配置更新的参数,那么我们的架构也相应的可以做出一些调整。在之前的架构中,服务的配置更新需要通过向具体服务中的某个实例发送请求,再触发对整个服务集群的配置更新。虽然能实现功能,但是这样的结果是,我们指定的应用实例就会不同于集群中的其他应用实例,这样会增加集群内部的复杂度,不利于将来的运维工作,比如:我们需要对服务实例进行迁移,那么我们不得不修改Web Hook中的配置等。所以我们要尽可能的让服务集群中的各个节点是对等的。
因此,我们将之前的架构做了一些调整,如下图所示:
主要做了这些改动:
1.在Config Server中也引入Spring Cloud Bus,将配置服务端也加入到消息总线中来。
2./bus/refresh请求不在发送到具体服务实例上,而是发送给Config Server,并通过destination参数来指定需要更新配置的服务或实例。
通过上面的改动,我们的服务实例就不需要再承担触发配置更新的职责。同时,对于Git的触发等配置都只需要针对Config Server即可,从而简化了集群上的一些维护工作。
参考
[1]http://blog.didispace.com/springcloud3/
[2]http://blog.didispace.com/springcloud4/
[2]http://blog.didispace.com/springcloud7/