简介
spring cloud是一个基于spring boot实现的微服务架构开发工具。它包含了多个子项目。
- spring cloud config: 配置管理工具,支持使用git存储配置内容,可以使用它实现应用配置的外部化存储,并支持客户端配置信息刷新、加密/解密配置内容等。
- spring cloud Netflix: 核心组件
- Eureka:服务治理组件,包含服务注册中心、服务注册与发现机制的实现。
- Hystrix:容错管理组件,实现断路器模式,帮助服务依赖中出现的延迟和为故障提供强大的容错能力。
- Ribbon:客户端负载均衡的服务调用组件。
- Feign: 基于Ribbon和Hystrix的声明式服务调用组件。
- Zuul: 网关组件,提供智能路由、访问过滤等功能。
- Archaius:外部化配置组件。
- spring cloud bus:事件、消息总线。
单元测试
1 | //待测试的Rest接口 |
1 | //测试代码 |
springboot 的属性加载顺序
- 命令行中传入的参数,例如
java -jar xxx.jar --server.port=8888 - SPRING_APPLICATION_JSON中的属性,SPRING_APPLICATION_JSON是以json格式配置在系统环境变量中的内容
- java:comp/env中的NBDI属性
- java的系统属性,可以通过System.getProperties()获得的内容
- 操作系统的环境变量
- 通过random.*配置的随机属性
- 位于当前应用jar包之外,针对不同{profile}环境的配置文件
- 位于当前应用jar包之内,针对不同{profile}环境的配置文件
- 位于当前应用jar包之外的application.properties和YAML中的配置内容
- 位于当前应用jar包之内的application.properties和YAML中的配置内容
- 使用@Configuration注解修改的类中,通过@PropertySource注解定义的属性
- 应用默认属性,使用SpringApplication.setDefaultProperties定义的内容。
监控与管理
在微服务架构中,将原本庞大的单体系统拆分为多个提供不同服务的应用,增加了应用部署的数量,提高了系统维护的复杂度。
为了让运维系统获取各个微服务应用的相关指标,以实现一些常规操作控制。springboot提供了一个特殊的依赖spring-boot-starter-actuator,使用该模块能够自动为springboot构建一系列用于监控的端点。
使用actuator
- 引入相关的依赖
1 | <dependency> |
- 启动应用
启动应用时,控制台会输出
1 | INFO 2560 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 2 endpoint(s) beneath base path '/actuator' |
我们访问http://localhost:8082//actuator/可以看到以json格式描述的访问各个端点的路径。
- 访问端点获取系统信息
我们通过http://localhost:8082//actuator/health访问health端点,获取的信息如下:{"status":"UP"}。在没有引入其它的组件前,从端点获取到的信息比较简单。
actuator模块中实现的原生端点介绍
根据端点的作用,可以将原生端点分为三大类
- 应用配置类:获取与springboot密切相关的配置类信息
- 度量指标类: 获取应用程序运行过程中用于监控的度量指标,如内存信息,线程池,HTTP请求统计等信息。
- 操作控制类:提供了应用程序的关闭等操作的功能。
服务治理 Spring Cloud Eureka
服务治理是微服务架构中最为核心和基础的模块。它主要用来实现各个微服务实例的自动化注册与发现。随着服务的增多,通过静态配置来实现服务的调用变得困难。为了解决微服务架构中的服务实例维护问题,通过使用服务注册和服务发现机制来实现对微服务应用实例的自动化管理。
服务注册
在服务治理框中,会使用一个注册中心。每个服务向注册中心登记自己提供的服务,其中包含主机与端口号、通信协议、版本号等一系列的信息。注册中心维护着一个服务清单。其中记录了所有注册过的服务的基本信息。
服务发现
得益于服务治理框架的运作,服务间的调用不再通过具体的实例地址来完成,而是通过向服务名发起请求调用实现。因此,服务调用方在调用相应的服务时,并不清楚服务的地址和端口等信息,仅仅知道服务名,这个时候就需要依赖注册中心,来获取相应服务名锁对应的服务。从注册中心获取到相应的服务列表后,依据一定的策略选取服务(负载均衡策略),来完成服务的调用。
Eureka简介
Eureka既包含服务端组件,也包含客户端组件。Eureka的服务端组件充当注册中心的角色。和其它的服务注册中心一样,它也支持高可用的配置。它依托于强一致性提供良好的服务实例可用性。
Eureka客户端主要处理服务的注册与发现。客户端服务通过组解和参数配置的方式嵌入在客户端应用程序代码中。Eureka客户端向注册中心注册自身提供的服务并周期性的发生心跳来更新它的服务租约。
Eureka的使用
搭建Eureka服务端
- 导入相关的依赖
1 | <dependency> |
- 在主类上添加
@EnableEurekaServer注册
1 |
|
- 编写配置文件
1 | server: |
- 方法注册中心面板
通过链接http://localhost:8083/即可访问
搭建Eureka客户端
- 导入相关的依赖
1 | <dependency> |
- 修改配置文件
1 | server: |
- 编辑启动类
添加一个@EnableEurekaClient注解即可
1 |
|
- 在注册中心中,查看服务注册信息

高可用注册中心
Eureka Server的高可用实际上就是将自己作为服务向其它服务注册中心注册自己,这样就可以实现一组互相注册的注册中心,以实现服务清单的互相同步,达到高可用的效果。
实现一组高可用注册中心
- 分别配置两套配置文件
application-peer1.yml
1 | server: |
application-peer2.yml
1 | server: |
- 修改系统的hosts文件
1 | # 新增 |
- 分别使用不同的配置文件启动服务
1 | java -jar eureka-server.jar --spring.profiles.active=peer1 |
- 分别查看两个注册中心


###使用高可用的注册中心
- 只需要在eureka的客户端的配置中心,添加多个注册中心的注册目标即可
1 | server: |
- 查看注册中心的注册信息
服务的注册信息,两个注册中心均有
搭建服务消费者
- 添加相关的依赖
1 | <dependency> |
- 编写应用主类
1 |
|
@EnableEurekaClient注解将该应用作为Eureka客户端使用@LoadBalanced注解开启客户端负载均衡
- 编写Controller类
编写controller类来调用EUREKA-CLIENT-HELLO服务中提供了/hello接口
1 |
|
- 编写相关的配置
1 | server: |
- 启动应用
先启动注册中心,再启动两个EUREKA-CLIENT-HELLO服务提供者,最后启动服务消费者。
Eureka详解
Eureka服务治理基础架构的三个核心要素
- 服务注册中心:Eureka提供的服务端,提供服务注册与发现的功能。
- 服务提供者: 提供服务的应用。只要遵循Eureka通信机制的应用都可以作为服务提供者。
- 服务消费者: 消费者应用从注册中心,获取所需要的服务信息的列表,从而让服务消费者知道从何处去调用其所需要的服务。
服务治理机制

注册中心之间可以相互注册,以实现高可用的注册中心。
服务提供者在启动时会通过发送Rest请求的方式将自己注册到Eureka服务器上,同时携带一些自身服务的信息。Eureka 服务端在接受到这些信息后,将信息存储到一个双层的map中,第一层是键服务名,第二层的键是具体服务的实例名。
服务同步:因为高可用注册中心的使用,当服务提供者将服务信息注册到其中一个注册中心后,由于注册中心之间互相注册为服务,所以服务提供者的服务信息会在注册中心之间同步。服务同步过后,服务提供者提供的服务信息,可以从任意一格注册中心中获得。
服务续约:在服务注册完成之后,服务提供者会维护一个心跳来告诉注册中心自己的状态,以免被Eureka服务器把其剔除。
获取服务:当服务消费者启动时,它会发送一个Rest请求给注册中心去获取服务清单。在EUreka服务器中缓存者一份服务清单。
服务调用:服务消费者在获取到服务清单后,会根据一个的负载均衡策略选择相应的服务。Eureka中有Region和zone的概念,一个Region中有多个zone,每个服务客户端都会注册到一个zone,在进行服务调用时,会优先选择同一个zone中的服务,如果同一个zone中服务访问不到就会去访问其它的zone。
服务下线:当服务正常关闭后,服务实例会触发一个服务下线的Rest请求给Eureka服务器,Eureka服务器会更新服务的状态,并将下线事件传播出去。
服务注册中心:
- 失效剔除
有些时候服务会不正常的下线,如内存溢出,网络故障等情况。Eureka会定时将那些没有正常续约的服务剔除掉。 - 自我保护
当Eureka服务器发现心跳失败的比例。在15分钟内是否低于85%,如果出现低于的情况(简单的讲就是在短时间内丢失了过多的实例的练级时就会触发自我保护),Eureka服务器会见当前的注册信息保护起来,这些注册信息不会过期,他会尽可能地保护这些注册信息。如果在这个阶段实例如果出现问题,那么客户端会很容易拿到实际已经不存在地服务实例,会出现调用失败。因此客户端必须要有容错机制。
1 | # 关闭自我保护机制 |
客户端负载均衡 Spring Cloud Ribbon
Spring Cloud Ribbon 是一个基于http和tcp的客户端负载均衡工具。
负载均衡是对系统的高可用,网络压力的缓解和处理能力扩容的重要手段之一。
服务端负载均衡
通常说的负载均衡是指服务端负载均衡,其中分为硬件负载均衡和软件负载均衡。
服务端负载均衡的架构一般如下图。
服务端负载均衡和客户端负载均衡的区别:
服务端负载均衡在负载均衡硬件或软件中需要维护一个服务清单,根据一定的负载均衡策略从服务清单中选择服务器。
而客户端负载均衡每个客户端节点存储一个服务清单。客户端负载均衡和服务端负载均衡的区别在于服务清单存储的位置。
使用Ribbon
得益于Ribbon的封装,使用客户端负载均衡的步骤大致只愮两步:
- 服务提供者启动多个实例并注册到一个注册中心,或是多个相关联的注册中心。
- 服务消费者调用被@Loadbanced注解修饰过的RestTemplate来实现面向服务的负载均衡。
RestTemplate详解
GET请求
- getForEntity函数,该方法的返回值是ResponseEntity,该对象是spring对http请求的封装。包含状态码、请求头,请求体等信息。
使用示例:
1 | ResponseEntity<String> responseEntity= |
getForEntity(String url,Class responseType,Object ... urlVariables),该方法和之前的类似,后面的urlVariables是替换url中的占位符,实现参数绑定。getForEntity(String url,Class responseType,Map urlVariables)方法只是把参数绑定换成了mapgetForEntity(URL url,Class responseType)使用了URL类getForObject(URL,Class responseType)该方法多用于不关心除body之外的响应的情况。其它几个重载方法和getForEntity一致
POST请求
postForEntity(String url,Object request,Class responseType)方法的第二个参数作为请求的参数,它也有多个重载方法postForObject(String url,Object requset,Class responseType)它也有许多类似的重载方法URL postForLocation(String url,Object request)该方法实现了以post请求提交资源并返回新的URL
还有一些put,delete请求相关的方法
负载均衡策略
RandomRule
该策略实行了从服务器示例清单中随机选择一个服务实例的功能。它使用一个生成的随机数作为UpList的索引值来选择具体的实例,同时具体的选择逻辑在一个while(server==null)循环之内。正常情况下每次选择都会选出一个服务实例,否则有可能存在并发bug。
RoundRobinRule
该策略实现了按照线性轮询的方式依次选择每个服务实例的功能。它的内部维护了一个count计数器,记录循环的次数,如果循环了10次还没有选择到服务实例,那么就会停止尝试,并打印警告信息。
RetryRule
该策略实现了一个具备重试机制的实例选择功能。它内部还定义了IRule(fuzziness均衡策略的接口)对象,默认使用RoundRobinRule实例,它会在chose方法中反复尝试,直到时间超过时间阈值。
WeightedResponseTimeRule
该策略是对RoundRobinRule的拓展。增加了根据实例的运行情况来计算权重,并根据权重来选择实例。它主要实现了三个核心功能。
定时任务: 启动一个定时任务(默认30s一次)来计算每个服务实例权重。
权重计算: 它首先会获取每个实例的统计信息,累计每个实例的平均响应时间,得到总平均响应时间totalResponseTime。然后逐个计算每个实例的权重。其计算方法为wightSoFar+totalResponseTime-实例的平均响应时间wightSoFar的初始值为零,计算出的权重会累加到weightSoFar供下一次计算使用(即紧接着的那个实例的权重计算使用)。值得注意的是这里计算出来的权重只是各实例的权重区间的上限。并非某个实例的优先级。
实例选择: 首先生成一个[0,最大权重值)区间内的随机数,然后看随机数落到那个实例的权重区间内,就选择。
例:
假设有四个实例分别是A,B,C,D。它们的平均响应时间分别为:10,40,80,100
解:
总平均响应时间为:10+40+80+100=230
A:0+230-10=220
B;220+230-40=410
C: 410+230-80=560
D:560+230-100=690
得到权重区间:
A [0,220]
B (220,410]
C (410,560]
D (560,690]
在[0,690)区间内生成一个随机数,假设为230,那么落在实例B的区间内,因此选择B。
ClientConfigEnabledRoundRobinRule
它内部本身没有实现特殊的策略,它内部定义了一个RoundRobinRule策略。我们一般不直接使用它,而是继承它实现高级策略。
BestAvaliableRule
它是继承自ClientConfigEnabledRoundRobinRule。它利用负载均衡器统计对象LoadBalancceStats,过滤掉故障的实例,选择并发请求数最小的实例。所以该策略的特性是选出最空闲的实例。同时该策略是严格依赖统计对象LoadBalanceStats,当该对象为空时,它会采用父类的线性轮询策略。
PredicateBasedRule
它是一个继承与ClientConfigEnabledRoundRobinRule的抽象策略。它的实现依赖于Predicate。它首先通过子类实现的Predicate逻辑来过滤掉一部分实例,然后再以线性轮询的方式从过滤的实例清单中选择一个。
AvaliabilityFitteringRule
该策略继承自PredicateBasedRule。它先以线性的方式选择一个实例,然后用过滤条件来判断该实例是否满足要求。若满足就直接使用该实例,若不满足就再其中过滤条件使用了AvailabilityPredicate.它是否过滤的主要依据以下几项内容(都要满足才不会被过滤)。
- 是否故障,即断路器是否生效已断开。
- 实例的并发请求数大于阈值,莫尔尼值为(2^32)-1.该配置可以通过
.ActiveConnectionsLimit来修改
ZoneAvoidanceRule
它是PredicateBasedRule的具体实现类。它完全按照父类的先过滤清单,再轮询选择的逻辑。它采用组合条件进行过滤。ZoneAvoidancePredicate为主过滤条件,AvailabilityPredicate为次过滤条件的组合过滤条件。





