分布式微服务---面经
分布式
CAP
C:consistency 一致性 分布式系统能够同时访问同一份数据副本
A:availability 可用性 非故障节点能够在合理时间内获得合理的结果
P:Partition Tolerance 分区容错性 分布式系统当发生网络分区时,服务仍然可用
网络分区:分布式网络中,由于某些节点故障,导致系统分成了几个区域。比如调用链A->B->C,其中B节点故障,那么A,B分成了两个分区。
而是在P为条件下,只能实现A或者C。也就是说,在发生网络分区后,我们只能保证一致性或者可用性。
当没有发生网络分区时,也就是没有P为条件,那么自然可以同时满足A和C。
zookeeper:保证CP,zookeeper有leader节点,写操作只能由leader来做,Follower只能处理读操作,这样就保证数据的一致性。
Eureka:保证AP,Eureka各个服务器节点都是平等的,只要有至少一个节点,就能够保证可用。
Nacos:保证AP和CP,默认支持AP,通过命令进行切换为CP。
BASE理论
-
基本可用(Basically Available)
当系统发生不可预知的故障时,允许损失部分可用性。
- 响应时间的损失:原先0.5s就能返回的结果,允许2s内返回
- 系统功能的损失:可以损失非核心功能,而保证核心功能的可用
-
软状态(Soft State)
允许存在中间状态,允许在多个节点的数据副本存在时延
-
最终一致性(Eventually Consistent)
在一个时间期限后,要保证数据的最终一致性
分布式ID
对于单个数据库我们使用id自增就能保证,id不重复。
对于两个数据库,我们分别设置起始值和步长就能避免id重复,但是这时在添加一个数据库,则要重新修改初始值和步长。
号段模式:我们可以先从数据库中取出一段数据,比如id为100到200,当需要使用时,在本地直接提取使用,使用完这100个id后,再从数据库中取200到300的id。
redis:通过redis,在redis中使用incr自增,也能够保证id的不重复。
雪花算法:对分布式id的bit进行分配,比如64bit的一个id,从左到右,第一位符号位,接下来41bit为时间戳,接下来10bit为工作机器id,接下来12bit为序列号。这样就能够保证id的不重复。
限流算法
固定窗口计数机算法
规定一分钟之内只能有100个请求,如果多了则丢弃该请求。该方法无法防止突增的请求,比如前30秒只有1个请求,后30秒突然进来99个请求,无法保证限流的速率。
滑动窗口计数机算法
将一分钟划分成60个格子,大小为30的窗口不断移动,每秒的请求放到格子中,如果窗口中的请求超过阈值,就不再处理其他请求。
漏桶算法
输出的水流是一定的,这样就能保证输出速率稳定。把请求比作水,当请求来时,则将水输入到水桶上方,即使上方的水是突然激增的,那输出也仍然是稳定的。如果木桶大小固定,上方水满了,则水将溢出,也就是说请求丢弃。
缺点是,我们总是以固定的速率流出,我们希望当请求少时,固定速率流出自然没问题,但是当请求到峰值,我们希望流出速率大一些。
令牌桶算法
仍然是一个固定大小的桶,我们以固定速率生产令牌,当请求来时,对于大的请求,消耗多点的令牌,小请求就少点令牌。如果桶内令牌没有了,则丢弃请求。这个算法就能解决动态去做“流出速率”,峰值时流出速度快,平时呢流出速率平稳。
RPC
做为远程调用框架,目的是希望调用远程方法就像本地方法一样简单。
客户端:通过简单注解或者使用类调用远程方法
客户端代理类:通过动态代理,让客户端执行的方法最终让代理类来执行。代理类拿到服务名/方法名,参数等信息封装到request中,然后通过网络调用发送给服务端
网络调用:最简单的可以用socket,当然netty更流行,编写Netty的客户端与服务端
服务端:Netty中拿到request,知道了方法名,可以通过反射创建实现了该方法的类,然后调用之,得到结果后,再通过Netty服务端传送给客户端
注册中心:我们可以用zk,创建节点,节点为服务名,节点下数据为机器ip地址。服务端启动时,首先将自己能够提供的服务注册到zk上。客户端根据服务名从zk中找到具体的ip地址,然后根据ip地址发送访问。
网关
微服务下多个服务,对于权限管理,流量控制,日志,监控等和业务无关的东西提取出来,统一管理,因此就有网关。
网关可以做:鉴权,限流,请求路由,日志,监控。
微服务
服务注册与发现:Eureka,nacos
微服务有很多消费者,提供者,我们不希望将他们之间的调用写死,因为针对某个服务,可能有多个机器去运行他,那么我们希望有一个人能够统一管理,那么注册中心可以解决我们的问题。服务提供者将自己的服务注册到注册中心,消费者只需根据服务名从注册中心拿到提供者地址。并且对于网关,负载均衡,熔断降级,消息队列等等组件,他们都希望自己能够获得注册在注册中心的某些信息,从而进行操作。
比如监控功能,就需要拿到注册中心的所有提供者消费者信息,来判断他们的健康情况。
总之,注册中心统一对服务的管理,当我们需要使用或者查看这些服务状态时,只需访问注册中心即可。
负载均衡:Ribbon
nacos自己集成了Ribbon,要使用负载均衡,首先我们要讲restTemplate通过@Bean注解注册到spring中,然后或者使用注解,或者通过代码指定负载均衡策略即可,然后调用restTemplate.getForObject或者restTemplate.postForObject即可调用另一个服务的方法。
Openfeign:有人觉得直接写restTemplate.postForObject,这样不美观,我就是想调用另一个服务就跟调用本地方法一样,那么这个Openfeign就可以帮助我们。我们写一个接口,接口中方法指明另一个服务的接口,然后我们在controller调用的时候,加个注解,直接调用接口的方法,看起来多像在本地调用方法啊!