本博客练习项目已上传 gitee->失铭/SpringCloud 练习
微服务概述
什么是微服务
引用 ThoughtWorks 公司的首席科学家 Martin Fowler Microservices 文中的介绍
In short, the microservice architectural style is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business capabilities and independently deployable by fully automated deployment machinery. There is a bare minimum of centralized management of these services, which may be written in different programming languages and use different data storage technologies.
谷歌翻译如下:
简而言之,微服务架构样式是一种将单个应用程序开发为一组小服务的方法,每个小服务都在自己的进程中运行并与轻量级机制(通常是 HTTP 资源 API)进行通信。这些服务围绕业务功能构建,并且可以由全自动部署机制独立部署。这些服务的集中管理几乎没有,可以用不同的编程语言编写并使用不同的数据存储技术。
单体架构和微服务:
微服务架构风格中以构建一组小型服务的方式来构建应用系统。这些服务除了能被独立地部署和扩展之外,每一个服务还能提供一个稳固的模块边界,甚至能允许使用不同的编程语言来编写不同的服务。这些服务也能被不同的团队来管理。
每一个功能元素最终都是一个可独立替换和独立升级的软件单元。
微服务优缺点
优点:
- 每个服务足够内聚,足够小,代码容易理解,能聚焦一个指定的业务功能或业务需求。
- 开发简单,开发效率高,一个服务可能只需要干一件事。
- 微服务能够被小团队单独开发(2-5 人)。
- 易与第三方集成,微服务允许容易且灵活的方式集成自动部署
- 微服务易于被一个开发人员理解,修改和维护,这样小团队能够更关注自己的工作成果。无需通过合作才能体现价值。
- 解耦:微服务是松耦合的,是有功能意义的服务,无论是在开发阶段还是部署阶段都是独立的
- 微服务能适用不同语言开发
- 微服务只是业务逻辑的代码,不会和 HTML,CSS 或其他界面组件混合。
- 每个微服务有自己的存储能力,它可以有自己的数据库,也可以有统一的数据库
缺点:
- 分布式部署,调用复杂性
单体应用的时候,所有模块之前的调用都是在本地进行的,在微服务中,每个模块都是独立部署的,通过 HTTP 来进行通信,这当中会产生很多问题,比如网络问题、容错问题、调用关系等。 - 多服务运维难度
- 系统部署依赖
- 服务间通信成本
- 数据一致性
- 系统集成测试
- 性能监控
微服务技术栈
微服务条目 | 落地技术 |
---|---|
服务开发 | SpringBoot、Spring、SpringMVC |
服务配置与管理 | Netflix 公司的 Archaius、阿里的 Diamind 等 |
服务注册与发现 | Eureka、Consul、Zookeeper 等 |
服务调用 | Rest、RPC、gRPC |
服务熔断器 | Hystrix、Envoy 等 |
负载均衡 | Ribbon、Nginx 等 |
服务接口调用(客户端调用服务的简化工具) | Feign 等 |
消息队列 | Kafka、RabbitMQ、ActiveMQ 等 |
服务配置中心管理 | SpringCloudConfig、Chef 等 |
服务路由(API 网关) | Zuul 等 |
服务监控 | Zabbix、Nagios、Metrics、Spectator |
全链路追踪 | Zipkin、Brave、Dapper 等 |
服务部署 | Docker、OpernStack、Kubernetes |
数据流操作开发包 | SpringCloud Stream(封装与 Redis,Rabbit,Kafaka 等发送接收消息) |
事件消息总线 | Spring Cloud Bus |
Spring Cloud
Spring Cloud 是一系列框架的有序集合。它利用 Spring Boot 的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用 Spring Boot 的开发风格做到一键启动和部署。
Spring Cloud 并没有重复制造轮子,它只是将各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过 Spring Boot 风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
SpringCloud 是分布式微服务下的一站式解决方案,是各个微服务架构落地技术的集合体,俗称微服务全家桶
Spring Cloud 版本
Spring Cloud 是一个由众多独立子项目组成的大型综合项目,每个子项目有不同的发行节奏,都维护着自己的发布版本号。Spring Cloud 通过一个资源清单 BOM(Bill of Materials)来管理每个版本的子项目清单。为避免与子项目的发布号混淆,所以没有采用版本号的方式,而是通过命名的方式。
这些版本名称的命名方式采用了伦敦地铁站的名称,同时根据字母表的顺序来对应版本时间顺序,比如:最早的 Release 版本:Angel,第二个 Release 版本:Brixton,然后是 Camden、Dalston、Edgware,当前稳定版为 Hoxton SR7。
当一个版本的 Spring Cloud 项目的发布内容积累到临界点或者解决了一个严重 bug 后,就会发布一个“service releases”版本,简称 SRX 版本,其中 X 是一个递增数字。
SpringBoot 和 SpringCloud 关系
- SpringBoot 专注于快速、方便的开发单个微服务个体,SpringCloud 关注全局的服务治理框架。
- SpringCloud 是关注全局的微服务协调整理治理框架,它将 SpringBoot 开发的一个个单体微服务整合并管理起来,为各个服务之间提供,配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、精选决策、分布式会话等集成服务。
- SpringBoot 可以离开 SpringCloud 独立开发项目,但是 SpringCloud 离不开 SpringBoot,属于依赖关系。
- Spring Boot 是 Spring 的一套快速配置脚手架,可以基于 Spring Boot 快速开发单个微服务,Spring Cloud 是一个基于 Spring Boot 实现的云应用开发工具。
- API:API 网关
- Config server:云配置
- Service registry:注册中心
- Microservices:微服务
- Distrubuted tracing:追踪
Dubbo
Apache Dubbo |ˈdʌbəʊ| 是一款高性能、轻量级的开源 Java RPC 框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。
Dubbo 核心组件:
- Provider:暴露服务的提供方,可以通过 jar 或者容器的方式启动服务。
- Consumer:调用远程服务的服务消费方
- Registry:服务注册中和发现中心
- Monitor:统计服务和调用次数,调用时间监控中心。(Dubbo 的控制台页面中可以显示,目前只有一个简单版本。)
- Container:服务运行的容器。
Dubbo 和 SpringCluod 区别
Dubbo | SpringCloud | |
---|---|---|
服务注册中心 | Zookeeper | Spring Cloud Netfix Eureka |
服务调用方式 | RPC | REST API |
服务监控 | Dubbo-monitor | Spring Boot Admin |
熔断器 | 不完善 | Spring Cloud Netflix Hystrix |
服务网关 | 无 | Spring Cloud Netflix Zuul |
分布式配置 | 无 | Spring Cloud Config |
服务跟踪 | 无 | Spring Cloud Sleuth |
数据流 | 无 | Spring Cloud Stream |
批量任务 | 无 | Spring Cloud Task |
信息总线 | 无 | Spring Cloud Bus |
注意:Dubbo 对于上表中总结为“无”的组件不代表不能实现,而只是 Dubbo 框架自身不提供,需要另外整合以实现对应的功能。
整体比较:
- dubbo 由于是二进制的传输,占用带宽会更少
- springCloud 是 http 协议传输,带宽会比较多,同时使用 http 协议一般会使用 JSON 报文,消耗会更大
- dubbo 的开发难度较大,原因是 dubbo 的 jar 包依赖问题很多大型工程无法解决
- springcloud 的接口协议约定比较自由且松散,需要有强有力的行政措施来限制接口无序升级
- dubbo 的注册中心可以选择 zk,redis 等多种,springcloud 的注册中心只能用 eureka 或者自研
- Dubbo 只是实现了服务治理,而 Spring Cloud 子项目分别覆盖了微服务架构下的众多部件,服务治理只是其中的一个方面。
- Dubbo 的定位是一款 RPC 框架,Spring Cloud 的目标是微服务架构下的一站式解决方案。
Zureka
Eureka 是 Netflix 开发的服务发现框架,本身是一个基于 REST 的服务,主要用于定位运行在 AWS 域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。
SpringCloud 将它集成在其子项目 spring-cloud-netflix 中,以实现 SpringCloud 的服务发现功能。
Eureka 包含两个组件:Eureka Server 和 Eureka Client。
- Eureka Server 提供服务注册服务,各个节点启动后,会在 Eureka Server 中进行注册,这样 EurekaServer 中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。
- Eureka Client 是一个 java 客户端,用于简化与 Eureka Server 的交互,客户端同时也就是一个内置的、使用轮询(round-robin)负载算法的负载均衡器。
在应用启动后,将会向 Eureka Server 发送心跳,默认周期为 30 秒,如果 Eureka Server 在多个心跳周期内没有接收到某个节点的心跳,Eureka Server 将会从服务注册表中把这个服务节点移除(默认 90 秒)。
Eureka Server 之间通过复制的方式完成数据的同步,Eureka 还提供了客户端缓存机制,即使所有的 Eureka Server 都挂掉,客户端依然可以利用缓存中的信息消费其他服务的 API。综上,Eureka 通过心跳检查、客户端缓存等机制,确保了系统的高可用性、灵活性和可伸缩性。
服务注册与发现
在微服务架构中,每个微服务通常有多个实例,每个实例有不同位置,而且实例位置会动态变化。而服务注册与发现技术就是用来解决微服务开发中的这个问题
服务要被使用,就需要对外提供服务的位置信息,这个位置信息通常是一个 IP 地址+端口。在服务只有单个实例并且地址不会动态变化的情况下,服务的位置在使用端可以通过配置文件甚至代码等方式固定死。但在位置信息会动态发生变化的情况下,服务实例就需要将这个地址注册到一个注册中心。
服务的所有实例在自己可以对外提供服务后,将位置注册到注册中心。这个注册中心具有固定的位置或域名,负责保存所有服务实例的位置信息。
举个例子:
电商中的订单服务需要调用库存服务,但是这两个服务部署在不同的客户端,如果库存的 ip 地址不变,那么只需要告诉订单:我是库存,我的地址是:192.137.1.33。订单就可以调用库存服务了。
当库存地址发生变化时,那么又需要重新通知订单,订单需要修改库存的地址才能调用。如果库存地址经常动态变化,显而易见,将会增加很多麻烦。而服务注册中心就是用来解决这个问题。
在系统架构引入独立部署在一台机器上的注册中心后,订单,库存在启动时都发送请求到服务注册中心进行注册。也就是说,得告诉服务注册中心,自己是哪个服务,然后自己部署在哪台机器上。然后服务注册中心会把大家注册上来的信息放在注册表里,如下图:
接着订单服务假如想要调用库存服务,那么就找服务注册中心问问:能不能告诉我库存服务部署在哪台机器上?服务注册中心是知道这个信息的,所以就会告诉订单服务:库存服务部署在 192.1371.133 这台机器上,你就给这台机器发送请求吧。然后,订单服务就可以往库存服务的那台机器发送请求了,完成了服务间的调用。整个过程,如下图所示:
Zureka 服务注册参考微服务架构中服务注册与发现
Zureka 使用
Zureka 具体使用参考Bilibili-【狂神说 Java】SpringCloud 最新教程 IDEA 版
注意:在 SpringCloud Hoxton.SR7 版本中 server 依赖为 spring-cloud-starter-netflix-eureka-server
1 | <dependency> |
Zureka 和 Zookeeper 的区别
先了解两个概念
Zookeeprer : ZooKeeper 是用于维护配置信息,命名,提供分布式同步以及提供组服务的集中式服务。所有这些类型的服务都以某种形式被分布式应用程序使用。每次实施它们时,都会进行很多工作来修复不可避免的错误和竞争条件。由于难以实现这类服务,因此应用程序最初通常会跳过它们,这会使它们在发生更改时变脆并且难以管理。即使部署正确,这些服务的不同实现也会导致管理复杂。
分布式的 CAP 理论:
- 一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(严格的一致性,所有节点访问同一份最新的数据副本)
- 可用性(A)在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性,不保证获取的数据为最新数据,但是保证最终一致性)
- 分区容错性(P):分布式系统在遇到任何网络分区故障的时候,仍然能够对外提供满足一致性和可用性的服务,除非整个网络环境都发生了故障。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在 C 和 A 之间做出选择。
CAP 原则又称 CAP 定理,指的是在一个分布式系统中:一致性(Consistency)可用性(Availability)分区容错性(Partition tolerance)。这三个要素最多只能同时实现两点,不可能三者兼顾。
在分布式存储系统中,最多只能实现以上两点。而由于当前网络延迟故障会导致丢包等问题,分区容错性 P 在分布式系统中是必须要保证的,因此我们只能在 A 和 C 之前权衡
- Zookeeper 保证的是 CP
- Eureka 保证的是 AP
Zookeeper 保证 CP
当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的注册信息,但不能接受服务直接 down 掉不可用。也就是说,服务注册功能对可用性的要求要高于一致性。
但是 zk 会出现这样一种情况,当master 节点因为网络故障与其他节点失去联系时,剩余节点会重新进行 leader 选举。问题在于,选举 leader 的时间太长,30 ~ 120s, 且选举期间整个 zk 集群都是不可用的,这就导致在选举期间注册服务瘫痪。
在云部署的环境下,因网络问题使得 zk 集群失去 master 节点是较大概率会发生的事,虽然服务能够最终恢复,但是漫长的选举时间导致的注册长期不可用是不能容忍的。
Eureka 保证 AP
Eureka 看明白了这一点,因此在设计时就优先保证可用性。Eureka 各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。
而 Eureka 的客户端在向某个 Eureka 注册时如果发现连接失败,则会自动切换至其它节点,只要有一台 Eureka 还在,就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最新的(不保证强一致性)。
除此之外,Eureka 还有一种自我保护机制,如果在 15 分钟内超过 85%的节点都没有正常的心跳,那么 Eureka 就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况:
- Eureka 不再从注册列表中移除因为长时间没收到心跳而应该过期的服务
- Eureka 仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上(即保证当前节点依然可用)
- 当网络稳定时,当前实例新的注册信息会被同步到其它节点中
因此, Eureka 可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像 zookeeper 那样使整个注册服务瘫痪。
Ribbon
什么是 Ribbon
Spring Cloud Ribbon 是一个基于 HTTP 和 TCP 的客户端负载均衡工具,它基于 Netflix Ribbon 实现。
Ribbon 是 Netflix 发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将 Netflix 的中间层服务连接在一起。Ribbon 客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出 Load Balancer 后面所有的机器,Ribbon 会自动的帮助你基于某种规则(如简单轮询,随即连接等)去连接这些机器。我们也很容易使用 Ribbon 实现自定义的负载均衡算法。
通过 Spring Cloud 的封装,可以让我们轻松地将面向服务的 REST 模版请求自动转换成客户端负载均衡的服务调用。Spring Cloud Ribbon 虽然只是一个工具类框架,它不像服务注册中心、配置中心、API 网关那样需要独立部署,但是它几乎存在于每一个 Spring Cloud 构建的微服务和基础设施中。
因为微服务间的调用,API 网关的请求转发等内容,实际上都是通过 Ribbon 来实现的,包括后续介绍的 Feign,它也是基于 Ribbon 实现的工具。所以,对 Spring Cloud Ribbon 的理解和使用,对于我们使用 Spring Cloud 来构建微服务非常重要。
负载均衡
负载均衡是高可用网络基础架构的关键组件,通常用于将工作负载分布到多个服务器来提高网站、应用、数据库或其他服务的性能和可靠性。
一个没有负载均衡的 web 架构类似下面这样:
在这里用户是直连到 web 服务器,如果这个服务器宕机了,那么用户自然也就没办法访问了。另外,如果同时有很多用户试图访问服务器,超过了其能处理的极限,就会出现加载速度缓慢或根本无法连接的情况。
而通过在后端引入一个负载均衡器和至少一个额外的 web 服务器,可以缓解这个故障。通常情况下,所有的后端服务器会保证提供相同的内容,以便用户无论哪个服务器响应,都能收到一致的内容。
如图,用户访问负载均衡器,再由负载均衡器将请求转发给后端服务器。在这种情况下,单点故障现在转移到负载均衡器上了。这里又可以通过引入第二个负载均衡器来缓解。
负载均衡方案
主流的负载均衡方案分为两类:
- 集中式负载均衡
- 即在服务的消费方和提供方之间使用独立的 LB 设施(可以是硬件,如 F5, 也可以是软件,如 nginx), 由该设施负责把访问请求通过某种策略转发至服务的提供方;
- 进程内负载均衡
- 将 LB 逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。
- Ribbon 就属于后者,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。
Ribbon 负载均衡策略
常用策略:
- RoundRobinRule:轮询
- RandomRule:从状态为 up(运行中)的服务中随机
- AvailabilityFilteringRule:先过滤掉跳闸,访问故障的服务,再轮询
- RetryRule:先轮询,如果服务访问失败,在指定的时间内,进行重试
Ribbon 策略(转)
策略名 | 策略声明 | 策略描述 | 实现说明 |
---|---|---|---|
BestAvailableRule | public class BestAvailableRule extends ClientConfigEnabledRoundRobinRule | 选择一个最小的并发请求的 server | 逐个考察 Server,如果 Server 被 tripped 了,则忽略,在选择其中 ActiveRequestsCount 最小的 server |
AvailabilityFilteringRule | public class AvailabilityFilteringRule extends PredicateBasedRule | 过滤掉那些因为一直连接失败的被标记为 circuit tripped 的后端 server,并过滤掉那些高并发的的后端 server(active connections 超过配置的阈值) | 使用一个 AvailabilityPredicate 来包含过滤 server 的逻辑,其实就就是检查 status 里记录的各个 server 的运行状态 |
WeightedResponseTimeRule | public class WeightedResponseTimeRule extends RoundRobinRule | 根据相应时间分配一个 weight,相应时间越长,weight 越小,被选中的可能性越低。 | 一个后台线程定期的从 status 里面读取评价响应时间,为每个 server 计算一个 weight。Weight 的计算也比较简单 responsetime 减去每个 server 自己平均的 responsetime 是 server 的权重。当刚开始运行,没有形成 statas 时,使用 roubine 策略选择 server。 |
RetryRule | public class RetryRule extends AbstractLoadBalancerRule | 对选定的负载均衡策略机上重试机制。 | 在一个配置时间段内当选择 server 不成功,则一直尝试使用 subRule 的方式选择一个可用的 server |
RoundRobinRule | public class RoundRobinRule extends AbstractLoadBalancerRule | roundRobin 方式轮询选择 server | 轮询 index,选择 index 对应位置的 server |
RandomRule | public class RandomRule extends AbstractLoadBalancerRule | 随机选择一个 server | 在 index 上随机,选择 index 对应位置的 server |
ZoneAvoidanceRule | public class ZoneAvoidanceRule extends PredicateBasedRule | 复合判断 server 所在区域的性能和 server 的可用性选择 server | 使用 ZoneAvoidancePredicate 和 AvailabilityPredicate 来判断是否选择某个 server,前一个判断判定一个 zone 的运行性能是否可用,剔除不可用的 zone(的所有 server),AvailabilityPredicate 用于过滤掉连接数过多的 Server。 |
同时,你也可以自定义算法策略,注意,自定义配置应放于 xxxApplication.java 父类同级的包下,避免被 SpringBoot 扫描到,从而覆盖掉之前的配置,结构如下:
1 | public class MyRule { |
1 | // 修改后的随机策略 |
在启动类中启动自定义策略
1 | // Eureka和Ribbon整合以后,不用关心ip地址和端口号 |
Feign
Feign 简介
Feign 是声明式的服务调用工具,我们只需创建一个接口并用注解的方式来配置它,就可以实现对某个服务接口的调用,简化了直接使用 RestTemplate 来调用服务接口的开发量。Feign 具备可插拔的注解支持,同时支持 Feign 注解、JAX-RS 注解及 SpringMvc 注解。当使用 Feign 时,Spring Cloud 集成了 Ribbon 和 Eureka 以提供负载均衡的服务调用及基于 Hystrix 的服务容错保护功能。
Feign 能干什么
Feign 旨在使编写 Java Http 客户端变得更容易.
使用 Ribbon+RestTemplate 时,利用 RestTemplate 对 http 请求的封装处理,形成了一套模板化的调用方法.但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。
所以,Feign 在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义.在 Feign 的实现下,我们只需要创建一个接口并使用注解的方式来配置它(以前是 Dao 接口上标注 Mapper 注解,现在是一个微服务接口上面标注一个 Feign 注解即可),即可完成对服务提供方的接口绑定,简化了使用 Spring cloud Ribbon 时,自动封装服务调用客户端的开发量
Feign 集成了 Ribbon
利用 Ribbon 维护了 MicroServiceCloud-Dept 的服务列表信息,并且通过轮询实现了客户端的负载均衡。而与 Ribbon 不同的是,通过 feign 只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用。
Feign 使用
在原有 consumer 依赖基础上添加 OpenFeign 依赖
1 | <!--启动器--> |
创建接口调用服务端方法:
1 |
|
在客户端中添加 Controller 调用接口
1 |
|
添加注解启动服务
1 |
|
总结:浏览器调用客户端 Controller,然后调用客户端接口,客户端接口再调用服务端 Controller。
Hystrix
Netflix Hystrix 是 SOA/微服务架构中提供服务隔离、熔断、降级机制的工具/框架。Netflix Hystrix 是断路器的一种实现,用于高微服务架构的可用性,是防止服务出现雪崩的利器。
在分布式环境中,不可避免地会有许多服务依赖项中的某些失败。Hystrix 是一个库,可通过添加等待时间容限和容错逻辑来帮助您控制这些分布式服务之间的交互。Hystrix 通过隔离服务之间的访问点,停止服务之间的级联故障并提供后备选项来实现此目的,所有这些都可以提高系统的整体弹性。
Hystrix 的作用:
- 提供保护并控制延迟和失败,以及通过第三方客户端库(通常是通过网络)访问的依赖项的失败。
- 停止复杂的分布式系统中的级联故障。
- 快速失败并快速恢复。
- 回退并在可能的情况下正常降级。
- 启用近乎实时的监视,警报和操作控制
Hystrix 解决了什么问题
复杂分布式体系结构中的应用程序具有数十种依赖关系,每种依赖关系不可避免地会在某个时刻失败。如果主机应用程序未与这些外部故障隔离开来,则可能会被淘汰。
当一个应用依赖多个外部服务,一切正常时,请求流如下:
多个微服务正常调用,无阻塞。
如果有一个服务发生延迟,当前请求就会阻塞:
随着高流量,单个服务延迟可能导致所有服务器上的资源在几分钟内饱和。
应用程序中可能会导致网络请求的,通过网络或客户端库延伸的每个点都是潜在故障的根源。比故障更糟糕的是,这些应用程序还会导致服务之间的延迟增加,从而备份队列,线程和其他系统资源,从而导致整个系统出现更多级联故障。
每个请求都占用了系统的 CPU、内存、网络等资源,如果该应用的 QPS 较高,那么该应用所有的服务资源会被快速消耗完毕,直至应用死掉。如果这个出问题的依赖(Dependency I),不止这一个应用,亦或是受影响的应用上层也有更多的依赖,那就会带来服务雪崩效应。
服务雪崩:一个服务失败,导致整个应用都崩溃。
Hystrix 提供的服务熔断和服务降级就是解决服务雪崩的手段之一
服务熔断
服务熔断:当下游的服务因为某种原因突然变得不可用或响应过慢,上游服务为了保证自己整体服务的可用性,不再继续调用目标服务,直接返回,快速释放资源。如果目标服务情况好转则恢复调用。例如在高压电路中,如果某个地方的电压过高,熔断器就会熔断,对电路进行保护。
需要说明的是熔断其实是一个框架级的处理,那么这套熔断机制的设计,基本上业内用的是断路器模式,如 Martin Fowler 提供的状态转换图如下所示:
- 最开始处于 closed 状态,一旦检测到错误到达一定阈值,便转为 open 状态;
- 这时候会有个 reset timeout,到了这个时间了,会转移到 half open 状态;
- 尝试放行一部分请求到后端,一旦检测成功便回归到 closed 状态,即恢复服务;
在 Spring Cloud 框架里,熔断机制通过 Hystrix 实现。Hystrix 会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是 5 秒内 20 次调用失败,就会启动熔断机制。
服务熔断解决如下问题:
- 当所依赖的对象不稳定时,能够起到快速失败的目的;
- 快速失败后,能够根据一定的算法动态试探所依赖对象是否恢复。
服务降级
服务降级是指当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理或换种简单的方式处理,从而释放服务器资源以保证核心业务正常运作或高效运作。说白了,就是尽可能的把系统资源让给优先级高的服务。
资源有限,而请求是无限的。如果在并发高峰期,不做服务降级处理,一方面肯定会影响整体服务的性能,严重的话可能会导致宕机某些重要的服务不可用。所以,一般在高峰期,为了保证核心功能服务的可用性,都要对某些服务降级处理。例如当双 11 活动时,把交易无关的服务统统降级,如查看蚂蚁深林,查看历史订单等等。
上面是服务降级的一种情况,服务降级存在两种场景:
- 当下游的服务因为某种原因响应过慢,下游服务主动停掉一些不太重要的业务,释放出服务器资源,增加响应速度!
- 当下游的服务因为某种原因不可用,上游主动调用本地的一些降级逻辑,避免卡顿,迅速返回给用户!
降级与熔断可以理解为:
- 服务降级有很多种降级方式!如开关降级、限流降级、熔断降级!
- 服务熔断属于降级方式的一种!
从实现上来说,熔断和降级必定是一起出现。因为当发生下游服务不可用的情况,这个时候为了对最终用户负责,就需要进入上游的降级逻辑了。因此,将熔断降级视为降级方式的一种,也是可以说的通的!
服务降级需要考虑以下几个问题:
- 那些服务是核心服务,哪些服务是非核心服务
- 那些服务可以支持降级,那些服务不能支持降级,降级策略是什么
- 除服务降级之外是否存在更复杂的业务放通场景,策略是什么?
自动降级分类
- 超时降级:主要配置好超时时间和超时重试次数和机制,并使用异步机制探测回复情况
- 失败次数降级:主要是一些不稳定的 api,当失败调用次数达到一定阀值自动降级,同样要使用异步机制探测回复情况
- 故障降级:比如要调用的远程服务挂掉了(网络故障、DNS 故障、http 服务返回错误的状态码、rpc 服务抛出异常),则可以直接降级。降级后的处理方案有:默认值(比如库存服务挂了,返回默认现货)、兜底数据(比如广告挂了,返回提前准备好的一些静态页面)、缓存(之前暂存的一些缓存数据)
- 限流降级:秒杀或者抢购一些限购商品时,此时可能会因为访问量太大而导致系统崩溃,此时会使用限流来进行限制访问量,当达到限流阀值,后续请求会被降级;降级后的处理方案可以是:排队页面(将用户导流到排队页面等一会重试)、无货(直接告知用户没货了)、错误页(如活动太火爆了,稍后重试)。
Hystrix 监控
启用 Actuator
Actuator 是 Springboot 提供的用来对应用系统进行自省和监控的功能模块,借助于 Actuator 开发者可以很方便地对应用系统某些监控指标进行查看、统计等。
若要使用 Actuator 对 Hystrix 流进行监控,除了需在工程 POM 文件中引入 spring-boot-starter-actuator 依赖:
1 | <dependency> |
启用 Hystrix Dashboard
使用 Hystrix 一个最大的好处就是它会为我们自动收集每一个 HystrixCommand 的信息,并利用 Hystrix-Dashboard 通过一种高效的方式对每一个断路器的健康状态进行展示。
值得注意的是,在使用 HystrixCommand 对 RibbonClient 进行包装的时候,你需要确保你配置的 Hystrix 超时时间要比 Ribbon 的超时时间长,包括由它们引起的重试时间,举个例子:如果你的 Ribbon 连接超时时间是 1 秒,并且 Ribbon 会连续重试请求 3 次,那么你的 Hystrix 连接超时时间需要配置成稍大于 3 秒。
使用 Hystrix Dashboard 需要新建一个项目作为监控页面
引入依赖:
1 | <dependency> |
修改监控项目端口
1 | 9001 = |
启用监控代理并启动项目
1 |
|
访问http://localhost:9001/hystrix
可以看到监控面板已经启动了
被监控项目添加一个 ServletRegistrationBean。
注意:被监控项目需要启用熔断,并且只能监控设置了 fallbackMethod 的方法。
1 |
|
在我使用的 Hystrix 2.2.4.RELEASE 版本中需要设置允许列表
修改监控项目配置文件,重启项目
1 | * = |
启动 eureka,消费者,生产者,监控面板。
在消费者页面连接生产者查询数据,然后访问http://localhost:生产者端口/actuator/hystrix.stream
可以看到数据流。例:
打开监控面板http://localhost:9001/hystrix
输入完成后点击Monitor Stream
就可以看到数据面板了。
例:
Zuul
Zuul 是 Netflix 开源的微服务网关,可以和 Eureka、Ribbon、Hystrix 等组件配合使用。
Spring Cloud 对 Zuul 进行了整合与增强,Zuul 默认使用的 HTTP 客户端是 Apache HTTPClient,也可以使用 RestClient 或 okhttp3.OkHttpClient。
Zuul 的主要功能是路由转发和过滤器。路由功能是微服务的一部分,比如/demo/test 转发到到 demo 服务。zuul 默认和 Ribbon 结合实现了负载均衡的功能
API 网关是一个服务器,是系统的唯一入口。从面向对象设计的角度看,它与外观模式类似。API 网关封装了系统内部架构,为每个客户端提供一个定制的 API。它可能还具有其它职责,如身份验证、监控、负载均衡、缓存、请求分片与管理、静态响应处理。
API 网关方式的核心要点是,所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有的非业务功能。通常,网关也是提供 REST/HTTP 的访问 API。服务端通过 API-GW 注册和管理服务。
Zuul 的作用
网关有以下几个作用:
- 统一入口:为全部服务提供一个唯一的入口,网关起到外部和内部隔离的作用,保障了后台服务的安全性。
- 权限校验:识别每个请求的权限,拒绝不符合要求的请求。
- 动态路由:动态的将请求路由到不同的后端集群中。
- 减少客户端与服务端的耦合:服务可以独立发展,通过网关层来做映射。
Zuul 的使用
新建 Zuul 模块,添加依赖
1 | <dependency> |
配置 application.yml 文件
1 | 9527 = |
默认路由规则:Zuul 和 Eureka 结合使用,可以实现路由的自动配置,自动配置的路由以服务名称为匹配路径,相当于如下配置:
1 | # zuul路由配置 |
添加@EnableZuulProxy 注解
1 |
|
配置完成后,可以在注册中心看到 Zuul 已经注册了,也可以通过http:localhost:9527/dept/user/1
调用服务端
注意:在 Zuul 2.2.4.RELEASE 版本的使用中,eureka 的 application 要转为小写,别问我怎么知道的
URL 路径匹配
1 | # URL pattern |
服务名称匹配
1 | # service id pattern 通过服务名称路由 |
也可以使用简化配置:
1 | # simple service id pattern 简化配置方案 |
路由排除设置
1 | # ignored service id pattern |
访问前缀
1 | # prefix URL pattern 前缀路由匹配 |
如上,配置访问前缀需要在路由前添加前缀才能访问,例:http://localhost:9527/proxy/dept/userinfos/1
访问。
zuul 网关其底层使用 ribbon 来实现请求的路由,并内置 Hystrix,可选择性提供网关 fallback 逻辑。使用 zuul 的时候,并不推荐使用 Feign 作为 application client 端的开发实现。毕竟 Feign 技术是对 ribbon 的再封装,使用 Feign 本身会提高通讯消耗,降低通讯效率,
SpringCloud Config
分布式系统中,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件。在 Spring Cloud 中,有分布式配置中心组件 springCloud Config ,它支持从远程 Git 仓库中读取配置文件并存放到本地 Git 仓库。
备注:这是整个模块最简单的项目,简而言之就是将配置文件上传到 git 仓库,项目再从 git 获取并初始化项目。SpringCloud Config 是 C-S 结构的。
Config-Server
创建一个 git 仓库,这一步就不介绍了。直接开始项目搭建
添加 Config 依赖
1 | <!-- config启动器 --> |
配置 Server 配置文件
创建 application.yml 文件
1 | # 端口号 |
启动 Server
添加注解@EnableConfigServer
1 |
|
只需要三步就可以迅速搭建一个服务端,接下来看项目中如何获取配置文件。
Config-Client
在仓库新建一个 Config-Client 文件,内容就是你接下用到的项目的配置,例:
1 | spring: |
将文件上传 git 仓库,就可以在http://localhost:3344/config-client-dev.yml
访问到刚才配置的文件中的 dev 环境配置
访问规则:
- /{application}/{profile}[/{label}] 例:
http://localhost:3344/config-client/dev/master
- /{application}-{profile}.yml 例:
http://localhost:3344/config-client-dev.yml
- /{label}/{application}-{profile}.yml 例:
http://localhost:3344/master/config-client-dev.yml
- /{application}-{profile}.properties
- /{label}/{application}-{profile}.properties
添加客户端依赖
1 | <dependency> |
修改客户端配置文件
在 resources 目录下新建bootstrap.yml
文件,
注:bootstrap.yml 是系统配置,application.yml 是个人配置,会被前者覆盖
1 | # 系统级别配置 |
如上,两步就配置完成了。
测试一个属性取到没有
1 |
|
启动项目访问http://localhost:8182/config
能看到返回了刚才配置的值。大功告成
SpringCloud 总结
- 微服务
- SpringCluod
- Netflix
- Eureka
- Ribbon
- Feign
- Zuul
- Hystrix
- SpringCloud Config