Zookeeper 是什么?
zk是一个开源的分布式协调服务,它是集群的管理者,监视着集群中各个节点的状态根据节点提交反馈进行下一步操作,最终将简单易用的接口和性能高效1、文档的系统提供给用户
分布式应用程序可以基于zk实现数据发布/订阅,负载均衡、Master选举、分布式锁、分布式队列等功能
特性
-
顺序一致性(有序性)
从同一个客户端发起的事务请求,最终将会严格地按照其发起顺序被应用到 Zookeeper 中去。
有序性是 Zookeeper 中非常重要的一个特性。
所有的更新都是全局有序的,每个更新都有一个唯一的时间戳,这个时间戳称为zxid(Zookeeper Transaction Id)。
而读请求只会相对于更新有序,也就是读请求的返回结果中会带有这个 Zookeeper 最新的 zxid 。
-
原子性
所有事务请求的处理结果在整个集群中所有机器上的应用情况是一致的,即整个集群要么都成功应用了某个事务,要么都没有应用。
-
单一视图
无论客户端连接的是哪个 Zookeeper 服务器,其看到的服务端数据模型都是一致的。
-
可靠性
一旦服务端成功地应用了一个事务,并完成对客户端的响应,那么该事务所引起的服务端状态变更将会一直被保留,除非有另一个事务对其进行了变更。
-
实时性
Zookeeper 保证在一定的时间段内,客户端最终一定能够从服务端上读取到最新的数据状态。
Zookeeper 有哪几种节点类型?
-
PERSISTENT 持久节点
创建之后一直存在,除非有删除操作,创建节点的客户端会话失效也不影响此节点。
-
PERSISTENT_SEQUENTIAL 持久顺序节点
跟持久一样,就是父节点在创建下一级子节点的时候,记录每个子节点创建的先后顺序,会给每个子节点名加上一个数字后缀。
-
EPHEMERAL 临时节点
创建客户端会话失效(注意是会话失效,不是连接断了),节点也就没了。不能建子节点。
-
EPHEMERAL_SEQUENTIAL 临时顺序节点
基本特性同临时节点,增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。
Zookeeper 的通知机制是什么?
Zookeeper 允许客户端向服务端的某个 znode 注册一个 Watcher 监听,当服务端的一些指定事件触发了这个 Watcher ,服务端会向指定客户端发送一个事件通知来实现分布式的通知功能,然后客户端根据 Watcher 通知状态和事件类型做出业务上的改变。
整个流程如下:
- 第一步,客户端注册 Watcher 。
- 第二步,服务端处理 Watcher 。
- 第三步,客户端回调 Watcher 。
Zookeeper 采用什么权限控制机制?
目前,在 Linux/Unix 文件系统中,使用 UGO(User/Group/Others) 权限模型,也是使用最广泛的权限控制方式。是一种粗粒度的文件系统权限控制模式。
一般我们管理后台,采用的 RBAC 居多,和 UGO 比较类似,差别在于一般将权限分配给 Role ,而不是直接给 User 。
对于 Zookeeper ,它采用 ACL(Access Control List)访问控制列表。包括三个方面:
-
权限模式(Scheme)
- IP :从 IP 地址粒度进行权限控制
- 【常用】Digest :最常用,用类似于
username:password
的权限标识来进行权限配置,便于区分不同应用来进行权限控制。 - World :最开放的权限控制方式,是一种特殊的 digest 模式,只有一个权限标识
“world:anyone”
。 - Super :超级用户。
-
授权对象
授权对象指的是权限赋予的用户或一个指定实体,例如 IP 地址或是机器等。
-
权限 Permission
- CREATE :数据节点创建权限,允许授权对象在该 znode 下创建子节点。
- DELETE :子节点删除权限,允许授权对象删除该数据节点的子节点。
- READ :数据节点的读取权限,允许授权对象访问该数据节点并读取其数据内容或子节点列表等。
- WRITE :数据节点更新权限,允许授权对象对该数据节点进行更新操作。
- ADMIN :数据节点管理权限,允许授权对象对该数据节点进行 ACL 相关设置操作。
集群中的机器角色有哪些?
-
1、Leader
- 事务请求的唯一调度和处理者,保证集群事务处理的顺序性。
- 集群内部各服务的调度者。
-
2、Follower
- 处理客户端的非事务请求,转发事务请求给 Leader 服务器。
- 参与事务请求 Proposal 的投票。 参与 Leader 选举投票。
-
3、Observer
3.3.0 版本以后引入的一个服务器角色,在不影响集群事务处理能力的基础上提升集群的非事务处理能力。
- 处理客户端的非事务请求,转发事务请求给 Leader 服务器
- 不参与任何形式的投票。
如果 ZooKeeper 集群的读取负载很高,或者客户端多到跨机房,可以设置一些 Observer 服务器,以提高读取的吞吐量。Observer 和 Follower 比较相似,只有一些小区别:
- 首先 Observer 不属于法定人数,即不参加选举也不响应提议,也不参与写操作的“过半写成功”策略;
- 其次是 Observer 不需要将事务持久化到磁盘,一旦 Observer 被重启,需要从 Leader 重新同步整个名字空间。
集群如果有 3 台机器,挂掉 1 台集群还能工作吗?挂掉 2 台呢?
记住一个原则:过半存活即可用。所以挂掉 1 台可以继续工作,挂掉 2 台不可以工作。
集群支持动态添加机器吗
在 3.5 版本开始,支持动态扩容。
而在 3.5 版本之前,Zookeeper 在这方面不太好。所以需要如下两种方式:
- 全部重启:关闭所有 Zookeeper 服务,修改配置之后启动。不影响之前客户端的会话。
- 逐个重启:顾名思义。这是比较常用的方式。
ZooKeeper 的工作原理?
结合ZK介绍来讲
ZooKeeper 的核心是原子广播,这个机制保证了各个 Server 之间的同步。实现这个机制的协议叫做 Zab 协议。Zab 协议有两种模式,它们分别是恢复模式(选主)和广播模式(同步):
-
选主:当服务启动或者 Leader 崩溃后,Zab 就进入了恢复模式,当新的 Leader 被选举出来,且大多数 Server 完成了和 Leader 的状态同步以后,恢复模式就结束了。
当整个 Zookeeper 集群刚刚启动,或者 Leader 服务器宕机、重启或者网络故障导致不存在过半的服务器与 Leader服务器保持正常通信时,所有进程(服务器)进入崩溃恢复模式。
- 首先,选举产生新的Leader服务器。
-
然后,集群中 Follower 服务器开始与新的 Leader 服务器进行数据同步。
- 当集群中超过半数机器与该Leader服务器完成数据同步之后,退出恢复模式进入消息广播模式,
-
同步:状态同步保证了 Leader 和 Server 具有相同的系统状态。 Leader 服务器开始接收客户端的事务请求,生成事务提案来进行事务请求处理。
Zookeeper 的同步流程?
选完 Leader 以后,Zookeeper 就进入状态同步过程。
- 1、Leader 等待 Server 连接。
- 2、Follower 连接 Leader ,将最大的 zxid 发送给 Leader 。
- 3、Leader 根据 Follower 的 zxid 确定同步点。
- 4、完成同步后通知 Follower 已经成为 update 状态。
- 5、Follower 收到 update 消息后,又可以重新接受 Client 的请求进行服务了。
当然,同步流程并不是像上述描述的这么简单,具体的,还是得看看 《Zookeeper Leader 和 Learner 的数据同步》 。
Zookeeper 的选举过程?
当 Leader 崩溃,或者 Leader 失去大多数的 Follower,这时 Zookeeper 进入恢复模式,恢复模式需要重新选举出一个新的 Leader,让所有的 Server 都恢复到一个正确的状态。
Zookeeper 的选举算法有两种:一种是基于 basic paxos 实现的,另外一种是基于 fast paxos 算法实现的。系统默认的选举算法为 fast paxos 。
相对详细的,可以看看 《【分布式】Zookeeper的Leader选举》 和 《Zookeeper 源码分析 —— Zookeeper Leader 选举算法》 。
- 不同阶段的选举流程
- 服务器启动时期的 Leader 选举。
- 服务器运行时期的 Leader 选举。
- 三种选举算法
- LeaderElection :使用 basic paxos 算法。
- FastLeaderElection :使用 fast paxos 算法。
- AuthFastLeaderElection :在 FastLeaderElection 的基础上,增加认证。
- 最终在 Zookeeper 3.4.0 版本之后,只保留 FastLeaderElection 版本。
-
fast paxos
由于 LeaderElection 收敛速度较慢,所以 Zookeeper 引入了 FastLeaderElection 选举算法,FastLeaderElection 也成了Zookeeper默认的Leader选举算法。
FastLeaderElection 是标准的 Fast Paxos 的实现。它首先向所有 Server 提议自己要成为 Leader ,当其它 Server 收到提议以后,解决 epoch 和 zxid 的冲突,并接受对方的提议,然后向对方发送接受提议完成的消息。重复这个流程,最后一定能选举出Leader。
FastLeaderElection 算法通过异步的通信方式来收集其它节点的选票,同时在分析选票时又根据投票者的当前状态来作不同的处理,以加快 Leader 的选举进程。
Dubbo 基本原理、执行流程
-
基本原理
-
service 层,接口层,给服务提供者和消费者来实现的
-
config 层,配置层,主要是对 dubbo 进行各种配置的
-
proxy 层,服务代理层,无论是 consumer 还是 provider,dubbo 都会给你生成代理,代理之间进行网络通信
-
registry 层,服务注册层,负责服务的注册与发现
-
cluster 层,集群层,封装多个服务提供者的路由以及负载均衡,将多个实例组合成一个服务
-
monitor 层,监控层,对 rpc 接口的调用次数和调用时间进行监控
-
protocal 层,远程调用层,封装 rpc 调用
-
exchange 层,信息交换层,封装请求响应模式,同步转异步
-
transport 层,网络传输层,抽象 mina 和 netty 为统一接口
-
serialize 层,数据序列化层
-
-
执行流程
- 服务容器Container负责启动加载运行服务提供者Provider,根据Provider配置文件根据协议发布服务,完成服务初始化
- 在Provider(服务提供者)启动时,根据配置中的Registry地址连接注册中心,将Provider的服务信息发布到注册中心
- Consumer(消费者)启动时,根据消费者配置文件中的服务引用信息,连接到注册中心,向注册中心订阅自己所需的服务
- 注册中心根据服务订阅关系,返回Provider地址列表给Consumer,如果有变更,注册中心会推送最新的服务地址信息给Consumer
- Consumer调用远程服务时,根据路由策略,先从缓存的Provider地址列表中选择一台进行,跨进程调用服务,假如调用失败,再重新选择其他调用
- 服务Provider和Consumer,会在内存中记录调用次数和调用时间,每分钟发送一次统计数据到Monitor
Dubbo 负载均衡策略、集群策略
负责均衡策略:
- RoundRobinLoadBalance 权重轮询算法,按照公约后的权重设置轮询比例,把来自用户的请求轮流分配给内部的服务器
- LeastActiveLoadBalance 最少活跃调用数均衡算法,活跃数是指调用前后计数差,使慢的机器收到的更少
- ConsistenHashLoadBalance 一致性Hash算法,相同参数的请求总是发到同一个提供者
- RandomLoadBlance 随机均衡算法,按权重设置随机概率,如果每个提供者的权重都相同,那么随机选一个,如果权重不同,则先累加权重值,从0~累加权重值选一个随机数,判断该随机数落在哪个提供者上
集群容错策略:
- FailoverCluster: 失败转移,当出现失败时,重试其他服务器,通常用于读操作,但重试会带来更长延迟,默认集群策略
- FailfastCluster: 快速失败,只发起一次调用,失败立即报错,通常用于非幂等性操作
- FailbackCluster: 失败自动恢复, 对于Invoker调用失败,后台记录失败请求,任务定时重发,通常用于通知
- BroadcastCluster: 广播调用,遍历所有Invokers,如果调用其中某个invoker报错,则catch住异常,这样就不影响其他invoker调用
- AvailableCluster: 获取可用调用,遍历所有Invokers并判断Invoker.isAvalible,只要有一个为true就直接调用返回,不管成不成功
- FailsafeCluster: 失败安全,出现异常时,直接忽略,通常用于写入审计日志等操作
- ForkingCluster: 并且调用,只要一个成功即返回,通常用于实时性要求较高的操作,但需要更多的服务资源
- MergeableCluster: 分组聚合,按组合并返回结果,比如某个服务接口有多种实现,可以用group区分,调用者调用多种实现并将得到的结果合并
注册中心挂了还可以通信吗?
可以。对于正在运行的 Consumer 调用 Provider 是不需要经过注册中心,所以不受影响。并且,Consumer 进程中,内存已经缓存了 Provider 列表。
那么,此时 Provider 如果下线呢?如果 Provider 是正常关闭,它会主动且直接对和其处于连接中的 Consumer 们,发送一条“我要关闭”了的消息。那么,Consumer 们就不会调用该 Provider ,而调用其它的 Provider 。
另外,因为 Consumer 也会持久化 Provider 列表到本地文件。所以,此处如果 Consumer 重启,依然能够通过本地缓存的文件,获得到 Provider 列表。
再另外,一般情况下,注册中心是一个集群,如果一个节点挂了,Dubbo Consumer 和 Provider 将自动切换到集群的另外一个节点上。
dubbo 支持哪些通信协议?支持哪些序列化协议?说一下 Hessian 的数据结构?
序列化,就是把数据结构或者是一些对象,转换为二进制串的过程,而反序列化是将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程。
dubbo 支持不同的通信协议
- dubbo 协议
默认就是走 dubbo 协议,单一长连接,进行的是 NIO 异步通信,基于 hessian 作为序列化协议。使用的场景是:传输数据量小(每次请求在 100kb 以内),但是并发量很高。
为了要支持高并发场景,一般是服务提供者就几台机器,但是服务消费者有上百台,可能每天调用量达到上亿次!此时用长连接是最合适的,就是跟每个服务消费者维持一个长连接就可以,可能总共就 100 个连接。然后后面直接基于长连接 NIO 异步通信,可以支撑高并发请求。
长连接,通俗点说,就是建立连接过后可以持续发送请求,无须再建立连接。
而短连接,每次要发送请求之前,需要先重新建立一次连接。
- rmi 协议
走 Java 二进制序列化,多个短连接,适合消费者和提供者数量差不多的情况,适用于文件的传输,一般较少用。
- hessian 协议
走 hessian 序列化协议,多个短连接,适用于提供者数量比消费者数量还多的情况,适用于文件的传输,一般较少用。
- http 协议
走 json 序列化。
- webservice
走 SOAP 文本序列化。
dubbo 支持的序列化协议
dubbo 支持 hession、Java 二进制序列化、json、SOAP 文本序列化多种序列化协议。但是 hessian 是其默认的序列化协议。
说一下 Hessian 的数据结构
Hessian 的对象序列化机制有 8 种原始类型:
- 原始二进制数据
- boolean
- 64-bit date(64 位毫秒值的日期)
- 64-bit double
- 32-bit int
- 64-bit long
- null
- UTF-8 编码的 string
另外还包括 3 种递归类型:
- list for lists and arrays
- map for maps and dictionaries
- object for objects
还有一种特殊的类型:
- ref:用来表示对共享对象的引用。
如何基于 dubbo 进行服务治理、服务降级、失败重试以及超时重试?
服务治理
- 调用链路自动生成
一个大型的分布式系统,或者说是用现在流行的微服务架构来说吧,分布式系统由大量的服务组成。那么这些服务之间互相是如何调用的?调用链路是啥?说实话,几乎到后面没人搞的清楚了,因为服务实在太多了,可能几百个甚至几千个服务。
那就需要基于 dubbo 做的分布式系统中,对各个服务之间的调用自动记录下来,然后自动将各个服务之间的依赖关系和调用链路生成出来,做成一张图,显示出来,大家才可以看到对吧。
- 服务访问压力以及时长统计
需要自动统计各个接口和服务之间的调用次数以及访问延时,而且要分成两个级别。
- 一个级别是接口粒度,就是每个服务的每个接口每天被调用多少次,TP50/TP90/TP99,三个档次的请求延时分别是多少;
- 第二个级别是从源头入口开始,一个完整的请求链路经过几十个服务之后,完成一次请求,每天全链路走多少次,全链路请求延时的 TP50/TP90/TP99,分别是多少。
这些东西都搞定了之后,后面才可以来看当前系统的压力主要在哪里,如何来扩容和优化啊。
- 其它
- 服务分层(避免循环依赖)
- 调用链路失败监控和报警
- 服务鉴权
- 每个服务的可用性的监控(接口调用成功率?几个 9?99.99%,99.9%,99%)
服务降级
比如说服务 A 调用服务 B,结果服务 B 挂掉了,服务 A 重试几次调用服务 B,还是不行,那么直接降级,走一个备用的逻辑,给用户返回响应。
举个栗子,我们有接口 HelloService
。HelloServiceImpl
有该接口的具体实现。
public interface HelloService { void sayHello(); } public class HelloServiceImpl implements HelloService { public void sayHello() { System.out.println("hello world......"); } } <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd"> <dubbo:application name="dubbo-provider" /> <dubbo:registry address="zookeeper://127.0.0.1:2181" /> <dubbo:protocol name="dubbo" port="20880" /> <dubbo:service interface="com.zhss.service.HelloService" ref="helloServiceImpl" timeout="10000" /> <bean id="helloServiceImpl" class="com.zhss.service.HelloServiceImpl" /> </beans> <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd"> <dubbo:application name="dubbo-consumer" /> <dubbo:registry address="zookeeper://127.0.0.1:2181" /> <dubbo:reference id="fooService" interface="com.test.service.FooService" timeout="10000" check="false" mock="return null"> </dubbo:reference> </beans>
我们调用接口失败的时候,可以通过 mock
统一返回 null。
mock 的值也可以修改为 true,然后再跟接口同一个路径下实现一个 Mock 类,命名规则是 “接口名称+Mock
” 后缀。然后在 Mock 类里实现自己的降级逻辑。
public class HelloServiceMock implements HelloService {
public void sayHello() {
// 降级逻辑
}
}
失败重试和超时重试
所谓失败重试,就是 consumer 调用 provider 要是失败了,比如抛异常了,此时应该是可以重试的,或者调用超时了也可以重试。配置如下:
<dubbo:reference id="xxxx" interface="xx" check="true" async="false" retries="3" timeout="2000"/>
举个栗子。
某个服务的接口,要耗费 5s,你这边不能干等着,你这边配置了 timeout 之后,我等待 2s,还没返回,我直接就撤了,不能干等你。
可以结合你们公司具体的场景来说说你是怎么设置这些参数的:
timeout
:一般设置为200ms
,我们认为不能超过200ms
还没返回。retries
:设置 retries,一般是在读请求的时候,比如你要查询个数据,你可以设置个 retries,如果第一次没读到,报错,重试指定的次数,尝试再次读取。
Spring Cloud 核心功能是什么?
Spring Cloud 主要提供了如下核心的功能:
- Distributed/versioned configuration 分布式/版本化的配置管理
- Service registration and discovery 服务注册与服务发现
- Routing 路由
- Service-to-service calls 端到端的调用
- Load balancing 负载均衡
- Circuit Breakers 断路器
- Global locks 全局锁
- Leadership election and cluster state 选举与集群状态管理
- Distributed messaging 分布式消息
Spring Cloud 有哪些组件?
脑图如下:
由于 Spring Cloud Netflix 要进入维护模式,下面是一些可以替代组件
Netflix | 阿里 | 其它 | |
---|---|---|---|
注册中心 | Eureka | Nacos | Zookeeper、Consul、Etcd |
熔断器 | Hystrix | Sentinel | Resilience4j |
网关 | Zuul1 | 暂无 | Spring Cloud Gateway |
负载均衡 | Ribbon | Dubbo(未来) | spring-cloud-loadbalancer |
Spring Cloud 和 Spring Boot 的区别和关系?
- Spring Boot 专注于快速方便的开发单个个体微服务。
- Spring Cloud 是关注全局的微服务协调整理治理框架以及一整套的落地解决方案,它将 Spring Boot 开发的一个个单体微服务整合并管理起来,为各个微服务之间提供:配置管理,服务发现,断路器,路由,微代理,事件总线等的集成服务。
- Spring Boot 可以离开 Spring Cloud 独立使用,但是 Spring Cloud 离不开 Spring Boot ,属于依赖的关系。
总结:
- Spring Boot ,专注于快速,方便的开发单个微服务个体。
- Spring Cloud ,关注全局的服务治理框架。
有哪些可以作为Spring Cloud的注册中心
在 Spring Cloud 中,能够使用的注册中心,还是比较多的,如下:
spring-cloud-netflix-eureka-server
和spring-cloud-netflix-eureka-client
,基于 Eureka 实现。spring-cloud-alibaba-nacos-discovery
,基于 Nacos 实现。spring-cloud-zookeeper-discovery
,基于 Zookeeper 实现。- … 等等
以上的实现,都是基于 spring-cloud-commons
的 discovery
的 DiscoveryClient 接口,实现统一的客户端的注册发现。
Eureka 如何实现集群?
此处,也很容易引申出一个问题,为什么 Eureka 被设计成 AP 的系统,答案可以看看 《为什么不应该使用 ZooKeeper 做服务发现》 。
聊聊 Eureka 缓存机制?
什么是 Eureka 自我保护机制?
[《Spring Cloud] Eureka 的自我保护模式及相关问题》
说说Spring Cloud 的负载均衡
在 Spring Cloud 中,能够使用的负载均衡,如下:
spring-cloud-netflix-ribbon
,基于 Ribbon 实现。spring-cloud-loadbalancer
,提供简单的负载均衡功能。
以上的实现,都是基于 spring-cloud-commons
的 loadbalancer
的 ServiceInstanceChooser 接口,实现统一的服务的选择。并且,负载均衡组件在选择需要调用的服务之后,还提供调用该服务的功能,具体方法见 LoadBalancerClient 接口的 #execute(...)
方法。
为什么要负载均衡?
简单来说,随着业务的发展,单台服务无法支撑访问的需要,于是搭建多个服务形成集群。那么随之要解决的是,每次请求,调用哪个服务,也就是需要进行负载均衡。
目前负载均衡有两种模式:
- 客户端模式
- 服务端模式
在 Spring Cloud 中,我们使用前者,即客户端模式。
详细的内容,可以看看 《客户端负载均衡与服务端负载均衡》 。
在计算中,负载平衡可以改善跨计算机,计算机集群,网络链接,中央处理单元或磁盘驱动器等多种计算资源的工作负载分布。负载平衡旨在优化资源使用,最大化吞吐量,最小化响应时间并避免任何单一资源的过载。使用多个组件进行负载平衡而不是单个组件可能会通过冗余来提高可靠性和可用性。负载平衡通常涉及专用软件或硬件,例如多层交换机或域名系统服务器进程。
Ribbon 有哪些负载均衡算法?
详细文章可见:《Ribbon 负载均衡策略配置》
其中,默认的负载均衡算法是 Round Robin 算法,顺序向下轮询。
Ribbon 是怎么和 Eureka 整合的?
Ribbon 原理图:
- 首先,Ribbon 会从 Eureka Client 里获取到对应的服务列表。
- 然后,Ribbon 使用负载均衡算法获得使用的服务。
- 最后,Ribbon 调用对应的服务。
另外,此处的 Eureka 仅仅是作为注册中心的举例,也是可以配合其它的注册中心使用,例如 Zookeeper 。可参考 《以 Zookeeper 为注册中心搭建 Spring Cloud 环境》 文章。
Feign 实现原理
Feign的一个关键机制就是使用了动态代理。咱们一起来看看下面的图,结合图来分析:
- 首先,如果你对某个接口定义了
@FeignClient
注解,Feign 就会针对这个接口创建一个动态代理。 - 接着你要是调用那个接口,本质就是会调用 Feign 创建的动态代理,这是核心中的核心。
- Feig n的动态代理会根据你在接口上的
@RequestMapping
等注解,来动态构造出你要请求的服务的地址。 - 最后针对这个地址,发起请求、解析响应。
Feign 和 Ribbon 的区别?
Ribbon 和 Feign 都是使用于调用用其余服务的,不过方式不同。
- 启动类用的注解不同。
- Ribbon 使用的是
@RibbonClient
。 - Feign 使用的是
@EnableFeignClients
。
- Ribbon 使用的是
- 服务的指定位置不同。
- Ribbon 是在
@RibbonClient
注解上设置。 - Feign 则是在定义声明方法的接口中用
@FeignClient
注解上设置。
- Ribbon 是在
- 调使用方式不同。
- Ribbon 需要自己构建 Http 请求,模拟 Http 请求而后用 RestTemplate 发送给其余服务,步骤相当繁琐。
- Feign 采使用接口的方式,将需要调使用的其余服务的方法定义成声明方法就可,不需要自己构建 Http 请求。不过要注意的是声明方法的注解、方法签名要和提供服务的方法完全一致。
Feign 是怎么和 Ribbon、Eureka 整合的?
-
首先,用户调用 Feign 创建的动态代理。
-
然后,Feign 调用 Ribbon 发起调用流程。
-
首先,Ribbon 会从 Eureka Client 里获取到对应的服务列表。
-
然后,Ribbon 使用负载均衡算法获得使用的服务。
-
最后,Ribbon 调用 Feign ,而 Feign 调用 HTTP 库最终调用使用的服务。
因为 Feign 和 Ribbon 都存在使用 HTTP 库调用指定的服务,那么两者在集成之后,必然是只能保留一个。比较正常的理解,也是保留 Feign 的调用,而 Ribbon 更纯粹的只负责负载均衡的功能。
-
想要完全理解,建议胖友直接看如下两个类:
-
LoadBalancerFeignClient ,Spring Cloud 实现 Feign Client 接口的二次封装,实现对 Ribbon 的调用。
-
FeignLoadBalancer ,Ribbon 的集成。
集成的是 AbstractLoadBalancerAwareClient 抽象类,它会自动注入项目中所使用的负载均衡组件。
-
LoadBalancerFeignClient =》调用=》 FeignLoadBalancer 。
Hystrix 隔离策略?
Hystrix 有两种隔离策略:
- 线程池隔离
- 信号量隔离
实际场景下,使用线程池隔离居多,因为支持超时功能。
详细的,可以看看 《Hystrix 的资源隔离策略》 文章。
聊聊 Hystrix 缓存机制?
Hystrix 提供缓存功能,作用是:
- 减少重复的请求数。
- 在同一个用户请求的上下文中,相同依赖服务的返回数据始终保持一致。
详细的,可以看看 《Hystrix 缓存功能的使用》 文章。
什么是 Hystrix 断路器?
Hystrix 断路器通过 HystrixCircuitBreaker 实现。
HystrixCircuitBreaker 有三种状态 :
CLOSED
:关闭OPEN
:打开HALF_OPEN
:半开
其中,断路器处于 OPEN
状态时,链路处于非健康状态,命令执行时,直接调用回退逻辑,跳过正常逻辑。
HystrixCircuitBreaker 状态变迁如下图 :
-
红线:初始时,断路器处于
CLOSED
状态,链路处于健康状态。当满足如下条件,断路器从CLOSED
变成OPEN
状态:
- 周期( 可配,
HystrixCommandProperties.default_metricsRollingStatisticalWindow = 10000 ms
)内,总请求数超过一定量( 可配,HystrixCommandProperties.circuitBreakerRequestVolumeThreshold = 20
) 。
- 周期( 可配,
-
错误请求占总请求数超过一定比例( 可配,
HystrixCommandProperties.circuitBreakerErrorThresholdPercentage = 50%
) 。 -
绿线 :断路器处于
OPEN
状态,命令执行时,若当前时间超过断路器开启时间一定时间(HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds = 5000 ms
),断路器变成HALF_OPEN
状态,尝试调用正常逻辑,根据执行是否成功,打开或关闭熔断器【蓝线】。
什么是 Hystrix 服务降级?
在 Hystrix 断路器熔断时,可以调用一个降级方法,返回相应的结果。当然,降级方法需要配置和编码,如果胖友不需要,也可以不写,也就是不会有服务降级的功能。
具体的使用方式,可以看看 《通过 Hystrix 理解熔断和降级》 。
为什么要网关服务?
使用网关服务,我们实现统一的功能:
- 动态路由
- 灰度发布
- 健康检查
- 限流
- 熔断
- 认证: 如数支持 HMAC, JWT, Basic, OAuth 2.0 等常用协议
- 鉴权: 权限控制,IP 黑白名单,同样是 OpenResty 的特性
- 可用性
- 高性能
详细的,可以看看 《为什么微服务需要 API 网关?》 。