1、概念
微服务架构是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间互相协调,配合为用户提供最终价值。每个服务运行在其独立的进程中,服务与服务间采用轻量级的通信机制互相沟通(通常基于HTTP的RESTFUL API)
2、微服务框架
Dubbo 是 SOA 时代的产物,它的关注点主要在于服务的调用,流量分发、流量监控和熔断。而 Spring Cloud 诞生于微服务架构时代,考虑的是微服务治理的方方面面.
Dubbo 是阿里巴巴公司一个开源的高性能服务框架,致力于提供高性能和透明化的 RPC 远程服务调用方案,以及 SOA 服务治理方案,使得应用可通过高性能 RPC 实现服务的输出、输入功能和 Spring 框架无缝集成。Dubbo 包含远程通讯、集群容错和自动发现三个核心部分。
3、SpringCloud核心( springcloud(一):大话Spring Cloud - 纯洁的微笑博客 )
- Spring Cloud Netflix:各项服务依赖与它,与各种Netflix OSS组件集成,组成微服务的核心。
- Netflix Eureka:服务中心,云端服务发现,一个基于 REST 的服务,用于定位服务,以实现云端中间层服务发现和故障转移。
- Netflix Hystrix:熔断器,容错管理工具,旨在通过熔断机制控制服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。
- Netflix Zuul:Zuul 是在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门。
- Netflix Archaius:配置管理API,包含一系列配置管理API,提供动态类型化属性、线程安全配置操作、轮询框架、回调机制等功能。
- Spring Cloud Config:俗称的配置中心,配置管理工具包,让你可以把配置放到远程服务器,集中化管理集群配置,目前支持本地存储、Git以及Subversion。
- Spring Cloud Bus:事件、消息总线,用于在集群(例如,配置变化事件)中传播状态变化,可与Spring Cloud Config联合实现热部署。
- Ribbon:是一个客户端负载均衡器,主要功能是提供客户端的软件负载均衡算法,将Netflix的中间层服务连接在一起。Ribbon工作时分为两步:第一步先选择 Eureka Server, 它优先选择在同一个Zone且负载较少的Server;第二步再根据用户指定的策略,在从Server取到的服务注册列表中选择一个地址。
-
Feign:是一个声明式的web service客户端,它使得编写web service客户端更为容易。创建接口,为接口添加注解,即可使用Feign。
a、外部或者内部的非Spring Cloud项目都统一通过API网关(Zuul)来访问内部服务
b、网关接收到请求后,从注册中心(Eureka)获取可用服务
c、由Ribbon进行均衡负载后,分发到后端的具体实例
d、微服务之间通过Feign进行通信处理业务
e、Hystrix负责处理服务超时熔断
f、Turbine监控服务间的调用和熔断相关指标
4、Eureka
Eureka由两个组件组成:Eureka服务器和Eureka客户端。Eureka服务器用作服务注册服务器。Eureka客户端是一个java客户端,用来简化与服务器的交互、作为轮询负载均衡器,并提供服务的故障切换支持。
- Eureka Server:提供服务注册和发现
- Service Provider:服务提供方,将自身服务注册到Eureka,从而使服务消费方能够找到
- Service Consumer:服务消费方,从Eureka获取注册服务列表,从而能够消费服务
4.1 Eureka和Zookeeper对比
一个分布式系统不可能同时满足C(一致性)、A(可用性)、和P(分区容错性)。由于分区容错性P在分布式系统中必须要保证的,因此我们只能在A和C之间进行权衡。
- Zoopkeeper保证CP: zk会出现这样的一种情况,当master节点因网路故障与其他节点失去联系时,剩余的节点会重新进行leader选举。问题在于,选举leader的时间太长,30~120s,且选举期间整个zk集群是都是不可用的,这就导致在选举期间注册服务瘫痪。
- Eureka保证AP: Eureka各个节点都是平等的,几个节点挂掉不影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。Eureka的客户端在向某个Eureka注册时如果发现连接失败,则会自动切换至其他的节点,只要有一台Eureka还在,就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最新的(不保证一致性)。
4.2 Eureka自我保护机制
如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,Eureka不再从注册列表中移除因为长时间没收到心跳而应该过期的服务,Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上(即保证当前节点依然可用),当网络稳定时,当前实例新的注册信息会被同步到其它节点中。
4.3 Eureka工作流程
a. Eureka Server 启动成功,等待服务端注册。在启动过程中如果配置了集群,集群之间定时通过 Replicate 同步注册表,每个 Eureka Server 都存在独立完整的服务注册表信息;
b. Eureka Client 启动时根据配置的 Eureka Server 地址去注册中心注册服务;
c. Eureka Client 会每 30s 向 Eureka Server 发送一次心跳请求,证明客户端服务正常;
d. 当 Eureka Server 90s 内没有收到 Eureka Client 的心跳,注册中心则认为该节点失效,会注销该实例;
e. 单位时间内 Eureka Server 统计到有大量的 Eureka Client 没有上送心跳,则认为可能为网络异常,进入自我保护机制,不再剔除没有上送心跳的客户端;
f. 当 Eureka Client 心跳请求恢复正常之后,Eureka Server 自动退出自我保护模式;
g. Eureka Client 定时全量或者增量从注册中心获取服务注册表,并且将获取到的信息缓存到本地;
h. 服务调用时,Eureka Client 会先从本地缓存找寻调取的服务。如果获取不到,先从注册中心刷新注册表,再同步到本地缓存;
i. Eureka Client 获取到目标服务器信息,发起服务调用;
j. Eureka Client 程序关闭时向 Eureka Server 发送取消请求,Eureka Server 将实例从注册表中删除;
5、CAP和Base
- Partition tolerance(分区容错):区间通信可能失败
- Consistency(一致性):写操作之后的读操作,必须返回该值
- Availability(可用性):只要收到用户的请求,服务器就必须给出回应
一般来说,分区容错无法避免,因此可以认为 CAP 的 P 总是成立,C跟A不能同时存在。
BASE 理论是对 CAP 理论的延伸,核心思想是即使无法做到强一致性(Strong Consistency,CAP 的一致性就是强一致性),但应用可以采用适合的方式达到最终一致性(Eventual Consitency)。
-
基本可用(Basically Available)
基本可用是指分布式系统在出现故障的时候,允许损失部分可用性,即保证核心可用。
电商大促时,为了应对访问量激增,部分用户可能会被引导到降级页面,服务层也可能只提供降级服务。这就是损失部分可用性的体现。 -
软状态(Soft State)
软状态是指允许系统存在中间状态,而该中间状态不会影响系统整体可用性。分布式存储中一般一份数据至少会有三个副本,允许不同节点间副本同步的延时就是软状态的体现。mysql replication 的异步复制也是一种体现。 -
最终一致性(Eventual Consistency)
最终一致性是指系统中的所有数据副本经过一定时间后,最终能够达到一致的状态。弱一致性和强一致性相反,最终一致性是弱一致性的一种特殊情况。
6、熔断器Hystrix
- 雪崩效应:在微服务架构中通常会有多个服务层调用,基础服务的故障可能会导致级联故障,进而造成整个系统不可用的情况,这种现象被称为服务雪崩效应。服务雪崩效应是一种因“服务提供者”的不可用导致“服务消费者”的不可用,并将不可用逐渐放大的过程。
- 熔断器(CircuitBreaker):它可以实现快速失败,如果它在一段时间内侦测到许多类似的错误,会强迫其以后的多个调用快速失败,不再访问远程服务器,从而防止应用程序不断地尝试执行可能会失败的操作,使得应用程序继续执行而不用等待修正错误,或者浪费CPU时间去等到长时间的超时产生。熔断器也可以使应用程序能够诊断错误是否已经修正,如果已经修正,应用程序会再次尝试调用操作。
- 服务降级:一般是从整体负荷考虑。就是当某个服务熔断之后,服务器将不再被调用,此时客户端可以自己准备一个本地的fallback回调,返回一个缺省值。
断路器很好理解, 当Hystrix Command请求后端服务失败数量超过一定比例(默认50%), 断路器会切换到开路状态(Open). 这时所有请求会直接失败而不会发送到后端服务. 断路器保持在开路状态一段时间后(默认5秒), 自动切换到半开路状态(HALF-OPEN). 这时会判断下一次请求的返回情况, 如果请求成功, 断路器切回闭路状态(CLOSED), 否则重新切换到开路状态(OPEN). Hystrix的断路器就像我们家庭电路中的保险丝, 一旦后端服务不可用, 断路器会直接切断请求链, 避免发送大量无效请求影响系统吞吐量, 并且断路器有自我检测并恢复的能力。
Hystrix目前是有两种隔离策略,分别是线程池隔离和信号量隔离。
- 线程池隔离
当前端发起请求过来到服务A或者B之后,服务A和服务B是通过线程池隔离的。服务A是否熔断,是否正常都和服务B无关。
他其实是一个异步编程,用线程池将后面的服务包裹了起来,至于服务内部tomcate的线程运行怎么样是无关的。他适合于绝大多数的场景,对于一些超时的场景都非常好用。但是既然是通过线程池来操作的,不可避免的就是线程之间的计算开销,以及线程上下文的切换,调度消耗。 - 信号量隔离
隔离是通过信号量来做到的。他其实是一个计数器。一个请求进来就会减少一个信号,一个请求完成就会增加一个信号。
信号量的调用时同步的,也就是说他会阻塞直到请求回来。所以他自身是不能实现超时的,因此这里的超时只能依靠协议的超时来做,否则是无法释放的(比如socket超时等等)。所以当此服务不对外部服务依赖同时自身没有大量的计算或者说经过这个服务的时间比较短,则非常适合信号量,比如说spring cloud的zuul的gateway网关。
7、RPC和Restful的区别:
Restful架构是基于Http应用层协议的产物,RPC架构是基于TCP传输层协议的产物。
Feign 是一个http请求调用的轻量级框架,以Java接口注解的方式调用Http请求,而不用像Java中通过封装HTTP请求报文的方式直接调用。Feign通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的请求,这种请求相对而言比较直观。
- 从使用方面看,Http接口只关注服务提供方(服务端),对于客户端怎么调用,调用方式怎样并不关心,通常情况下,客户端使用Http方式进行调用时,只要将内容进行传输即可,这样客户端在使用时,需要更关注网络方面的传输,比较不适用与业务方面的开发;而RPC服务则需要客户端接口与服务端保持一致,服务端提供一个方法,客户端通过接口直接发起调用,业务开发人员仅需要关注业务方法的调用即可,不再关注网络传输的细节,在开发上更为高效。
- 从性能角度看,使用Http时,Http本身提供了丰富的状态功能与扩展功能,但也正由于Http提供的功能过多,导致在网络传输时,需要携带的信息更多,从性能角度上讲,较为低效。而RPC服务网络传输上仅传输与业务内容相关的数据,传输数据更小,性能更高。
-
从运维角度看,使用Http接口时,常常使用一个前端代理,来进行Http转发代理请求的操作,需要进行扩容时,则需要去修改代理服务器的配置,较为繁琐,也容易出错。而使用RPC方式的微服务,则只要增加一个服务节点即可,注册中心可自动感知到节点的变化,通知调用客户端进行负载的动态控制,更为智能,省去运维的操作。
8、RPC原理
(1). 服务消费方(client)以本地调用方式调用服务;
(2). client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体;
(3). client stub找到服务地址,并将消息发送到服务端;
(4). server stub收到消息后进行解码;
(5). server stub根据解码结果 反射调用 本地的服务;
(6). 本地服务执行并将结果返回给server stub;
(7). server stub将返回结果打包成消息并发送至消费方;
(8). client stub接收到消息,并进行解码;
(9). 服务消费方得到最终结果。
9、Feign原理(原文地址)
Feign 整体框架非常小巧,在处理请求转换和消息解析的过程中,基本上没什么时间消耗。真正影响性能的,是处理Http请求的环节。可以通过拓展该接口,使用Apache HttpClient 或者OkHttp3等基于连接池的高性能Http客户端。
/**
* feign请求拦截器
* 所有用feign发出的请求的拦截器,注意是feign作为客户端发出请求的,而不是服务端
*/
@Configuration
public class FeignRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
//这里可以添加feign请求的全局参数
requestTemplate.header("msClientId", "8888");
}
}
10、API网关
API Gateway是一个服务器,也可以说是进入系统的唯一节点。这跟面向对象设计模式中的Facade模式很像。API Gateway封装内部系统的架构,并且提供API给各个客户端。它还可能有其他功能,如授权、监控、负载均衡、缓存、请求分片和管理、静态响应处理等。下图展示了一个适应当前架构的API Gateway。API Gateway负责请求转发、合成和协议转换。所有来自客户端的请求都要先经过API Gateway,然后路由这些请求到对应的微服务。API Gateway将经常通过调用多个微服务来处理一个请求以及聚合多个服务的结果。它可以在web协议与内部使用的非Web友好型协议间进行转换,如HTTP协议、WebSocket协议。
- 请求转发:服务转发主要是对客户端的请求安装微服务的负载转发到不同的服务上
- 响应合并:把业务上需要调用多个服务接口才能完成的工作合并成一次调用对外统一提供服务。
- 协议转换:重点是支持SOAP,JMS,Rest间的协议转换。
- 数据转换:重点是支持XML和Json之间的报文格式转换能力(可选)
-
安全认证:
a. 基于Token的客户端访问控制和安全策略
b. 传输数据和报文加密,到服务端解密,需要在客户端有独立的SDK代理包
c. 基于Https的传输加密,客户端和服务端数字证书支持
d. 基于OAuth2.0的服务安全认证(授权码,客户端,密码模式等)
11、服务跟踪(starter-sleuth)
随着微服务数量不断增长,需要跟踪一个请求从一个微服务到下一个微服务的传播过程, Spring Cloud Sleuth 正是解决这个问题,它在日志中引入唯一ID,以保证微服务调用之间的一致性,这样你就能跟踪某个请求是如何从一个微服务传递到下一个。
a. 为了实现请求跟踪,当请求发送到分布式系统的入口端点时,只需要服务跟踪框架为该请求创建一个唯一的跟踪标识,同时在分布式系统内部流转的时候,框架始终保持传递该唯一标识,直到返回给请求方为止,这个唯一标识就是Trace ID。通过Trace ID的记录,我们就能将所有请求过程日志关联起来。
b. 为了统计各处理单元的时间延迟,当请求达到各个服务组件时,或是处理逻辑到达某个状态时,也通过一个唯一标识来标记它的开始、具体过程以及结束,该标识就是我们前文中提到的Span ID,对于每个Span来说,它必须有开始和结束两个节点,通过记录开始Span和结束Span的时间戳,就能统计出该Span的时间延迟,除了时间戳记录之外,它还可以包含一些其他元数据,比如:事件名称、请求信息等。
c. 在Spring Boot应用中,通过在工程中引入spring-cloud-starter-sleuth依赖之后, 它会自动的为当前应用构建起各通信通道的跟踪机制,比如:
- 通过诸如RabbitMQ、Kafka(或者其他任何Spring Cloud Stream绑定器实现的消息中间件)传递的请求。
- 通过Zuul代理传递的请求。
- 通过RestTemplate发起的请求。
12、Ribbon
-
服务器端负载均衡 Nginx
nginx 是客户端所有请求统一交给 nginx,由 nginx 进行实现负载均衡请求转发,属于服务器端负载均衡。
既请求由 nginx 服务器端进行转发。 -
客户端负载均衡 Ribbon
Ribbon 是从 eureka 注册中心服务器端上获取服务注册信息列表,缓存到本地,然后在本地实现轮询负载均衡策略。在客户端实现负载均衡。
应用场景的区别
Nginx 适合于服务器端实现负载均衡 比如 Tomcat ,Ribbon 适合与在微服务中 RPC 远程调用实现本地服务负载均衡,比如 Dubbo、SpringCloud 中都是采用本地负载均衡。
spring cloud的Netflix中提供了两个组件实现软负载均衡调用:ribbon和feign。
Ribbon
是一个基于 HTTP 和 TCP 客户端的负载均衡器
它可以在客户端配置 ribbonServerList(服务端列表),然后轮询请求以实现均衡负载。Feign
Spring Cloud Netflix 的微服务都是以 HTTP 接口的形式暴露的,所以可以用 Apache 的 HttpClient 或 Spring 的 RestTemplate 去调用,而 Feign 是一个使用起来更加方便的 HTTP 客戶端,使用起来就像是调用自身工程的方法,而感觉不到是调用远程方法。