面试题:分布式,微服务相关

1.CAP

  1. C:一致性,所有节点在同一时间的数据要完全一致,强一致性
  2. A:可用性:服务一直可用,不出现用户操作失败或者超时等影响用户体验的情况
  3. P:分区容错性:分布式系统遇到某个节点或网络分区故障的时候,仍然能够对外提供满足一致性和可用性的服务
  4. CP和AP:分区容错是必须保证的,当发生网络分区时,要继续服务,那么强一致性和可用性只能二选一

2.BASE:即使无法做到强一致性,但每个应用可以根据自身业务特点,采取适当的方式实现弱一致性

  1. BA:基本可用:响应时间上的损失或者系统功能上的损失
  2. S:软状态:数据同步允许一定的延迟
  3. E:最终一致性:系统中的数据副本,经过一段时间,最终能达到一致性

3.负载均衡算法、类型

   1.随机
   2.轮询
   3.加权轮询
   4.最小连接数(最小访问量)
   5.源地址哈希
   6.加权随机

4.分布式架构下session共享有什么方案:

1.redis存储token
2.服务器间session同步
3.ip绑定策略,nginx中可以设置同一个ip只能在同一个机器访问,这样失去了负载均衡的意义

5.分布式Id生成方案

   1.uuid:生成快,性能好,无序,长度长,字符串
   2.数据库自增id:依赖db,存在单点问题
   3.分布式数据库自增id:每次获取id都要请求一次数据库,而且维护困难
   4.leaf-segment:每次获取一个id段,比如1-100的id段,存到本地备用,只会请求一次数据库
   5.双buffer:将获取一个号段,变成获取两个号段,先获取了100个id,然后用到10%的时候,再异步获取一百个
   6.基于redis,mongo,zk等中间件
   7.雪花算法:64为的全局id,引入时间戳和保持自增
       1.最高位为符号位,位0
       2.41位的时间戳,精确到毫秒级
       3.10位机器标识,最多可支持到1024台机器
       4.12位的计数序列号,支持每毫秒4096个Id生成

6.分布式锁的解决方案

  1.zookeeper分布式锁
       1.zk通过临时节点,解决了死锁问题,因为节点挂掉,那么这个临时节点也会自动删除掉,客户端就能获取到锁
   2.redis分布式锁
       1.setnx,如果key不存在,则返回1并设置,存在则返回0
       2.设置超时时间,解决死锁问题
       4.加锁和设置锁的超时时间并不是原子操作,所以可能产生死锁,可以set命令
       5.高并发场景下,锁的超时时间设置可能产生问题,比如进程A加锁,但是还没释放锁就自动过期了,线程B恰好又进来加锁,结果A释放的是B的锁
         所以set的值最好为一个Uuid,释放的时候判断是否是这个uuid
       6.redission加锁,看门狗机制,自动刷新超时时间,lua原子操作,可重入性和锁续期
       7.redis分布式锁最大问题是集群模式下,master节点宕机,锁丢失

7.分布式事务解决方案:

  1.两种分布式事务:
       1.在一个Method中操作了两种数据库,用@Trancational没有用,可以用JTA解决
       2.A节点调用B节点,A节点调用DBA,B节点调用DBB
   2.XA规范:分布式事务规范,定义了分布式事务模型
       1.四个角色:事务管理器(协调者TM)、资源管理器(参与者RM)、应用程序AP、通信资源管理器CRM
       2.全局事务:一个横跨多个数据库的事务,要么同时提交,要么同时回滚
   3.两阶段协议
       1.第一阶段:每个参与者执行本地事务但不提交,进入ready状态,并通知协调者已经准备就绪
       2.第二阶段:当协调者确认每个参与者都ready后,通知参与者进行commit操作,如果有参与者fall,则发送rollback命令,各参与者回滚
       3.问题
           1.单点故障:一旦事务管理器出现故障,整个系统不可用(参与者都会阻塞住),并且出现一系列问题
       4.项目:这种方案比较适合用于单应用里面,比如一个方法调用了两个数据库,效率很低,不适合高并发,用spring + JTA就能实现,一般我们
           很少用,其实一个服务操作两个不同的库,是不合规的,微服务基本上要求一个服务只能操作一个库,然后想要用这个服务就通过rest或者rpc调用
   4.三阶段协议:主要解决两阶段的单点故障问题
       1.第一阶段:发送cancommit消息,确认数据库环境正常
       2.第二阶段:发送precommit消息,完成sql语句操作,但未提交事务
       3.第三阶段:发送docommit消息,完成事务的提交和回滚
       4.超时机制:如果precommit成功了,但一定时间内还未收到docommit消息,则认为协调者挂了,则自己会执行docommit操作
       4.项目:三阶段协议其实和两阶段差不多,只不过多了个确认数据库环境是否异常的阶段,并且增加了超时重试的机制,也不用
   5.TCC补偿事务(Try,Confirm,Cancel)
       1.try:做业务检查和资源预留
       2.confirm:做业务确认
       3.cancel:实现一个与try相反的回滚操作,是业务上的回滚,不是数据库的回滚,比如你try加了100元钱,那么这个回滚就需要减少100元钱
       4.TM首先发起所有分支的try操作,一旦有一个失败,TM会发起所有事务的cancel操作,全部成功,则发起confirm
       5.问题
           1.对业务侵入性很大
       6.项目:这个方式用的也比较小,一般先检查数据的条数或者关键数据,然后调用其他库的服务时,如果抛出异常,就硬编码去回滚,比如某个数量 + 1,
           那么出现问题就得手动调用减1的服务,代码量巨大,而且承受不住并发,难以维护
   6.消息队列的事务
       1.发送prepare消息到中间件
       2.发送成功后,执行本地事务
           1.执行成功,则commit,消息中间件将消息下发至消费端
           2.如果失败,则回滚,消息中间件删除消息
       3.消费端接收到消息进行消费,如果消费失败,则不断重试
   7.阿里开源的分布式事务解决方案seata
   8.本地消息表:字段有,id,业务id,消息状态,消息内容,重试次数等
       1.消息生产方,需要额外建一个消息表,并且记录消息的发送状态。消息表和业务数据要在一个库里面
       2.消息经过mq发送给消费方,发送失败则自动重试
       3.消息消费方处理这个消息,完成业务逻辑,然后将是否成功的消息通过mq发送给消息生产方
       4.生产方监听消费方是否消费成功,再修改本地消息表中的状态
       5.如果消息在mq中,或者网络中发送失败,则在保证接口幂等的情况下,引入一个定时任务,读取消息发送失败的状态
       6.本地消息表中失败的消息,就重新读取发送到mq中重试,直到重试次数阈值之前成功,若不成功,则短信告警,人工介入或者回滚
       7.缺点:与业务耦合,难以做通用性,高并发下有瓶颈

8.接口幂等性

    1.根据具体业务来判断怎么处理
       1.token + redis 机制(举例订单支付业务)
           1.当到了支付的场景时,生成一个全局唯一的token,存入到redis中,并且返回给客户端
           2.发起支付操作的时候附带这个token
           3.接口处理:
               1.获取分布式锁(处理并发情况)
               2.判断redis中是否存在token
               3.存在,执行支付业务逻辑,执行后删除token,否则返回订单已支付
               4.释放分布式锁
       2.CAS保证接口幂等
           1.状态机制幂等(举例订单支付状态 0 待支付 1 支付中)
           2.update order set status = 1 where status = 0 and orderId = ?
           3.要进行支付,上来先更新一下订单的支付状态
       3.乐观锁方案,版本控制
           1.大部分订单系统都是分布式部署的,订单和库存业务独立部署,由于网络原因,可能请求延迟了,重新发起请求,就可能导致幂等性问题
           2.update t_goods set count = count - 1 where good_id = 2
           3.如果请求两次,没保证幂等,则库存减了两个
           4.借鉴数据库乐观锁或者版本控制:update t_goods set count = count - 1,version = version + 1 where good_id = 2 and version = 1
       4.防重表
           1.数据库建立防重表,加唯一索引,订单有状态控制,则可以支付成功后删除订单号,没有的话也可以不删除

9.高并发预约系统设计:疫情期间,政府免费发放口罩的预约服务,服务升级

   1.硬件参数:
       1.一台16核,64g内存的服务器
       2.mysql服务器,单台
   2.产生问题:
       1.系统qps四五百多
       2.预约页面在高峰时期页面加载卡顿,url访问缓慢,系统崩溃掉 (解决方法7,5)
       3.netstat 查看当前80端口连接数  netstat -ant|awk '/^tcp/ {++S[$NF]} END {for(a in S) print (a,S[a])}' (解决方法8)
           1.状态是established的连接数量为500多
           2.状态是time_wait的连接数量为???(这个值需要深思熟虑一下)
               1.TIME_WAIT出现的原因是客户端请求到linux,会占用一个端口。如果请求了,服务端没返回数据,就回出现TIME_WAIT
       4.服务中调用了三个接口,并且都是顺序执行的,比如身份信息校验接口,失信人接口之类的,必须获取到数据之后才能进行下一步操作
         三个接口的调用大概花费2-3s,原因是网络环境复杂,接口数据需要经过两三家公司的层层封装调用返回(解决方法9)
       5.会有人在预约开始之前一分钟就不断地对服务发起请求,有点像黄牛用代码写的程序,毕竟当时疫情刚开始,口罩是很值钱的(解决方法3)
       6.没有采用缓存,已经预约的人还能再次请求,查询数据库之后才返回(解决方法5.4)
       7.oracle的连接数有到了800多,查询变得缓慢,有些日志中报超时错误(解决方法2)
       8.出现超约现象,原本是定只能约500人,但是多约了3个(解决方法5.5)
       9.页面的所有资源加起来大小有3m,还有两张300k的图片,每次加载页面需要大约4m的带宽(解决方法7)
       10.记录预约成功或者失败的日志到oracle中,产生压力(解决方法5.3)
       11.预约的时候从预约,到预约成功,中间经过了5s左右(解决方法5.6,5.7)
   3.当时的解决方案:
       1.软件方面
           1.采用redis做缓存
           2.建立数据库主从复制,读写分离
           3.nginx配置对某个ip请求频率高的或者请求次数多的拦截
           4.已预约的手机号码防止再次进行预约,将预约时段和手机号码作为Key,存入redis
           5.预约主要业务逻辑
               1.缓存预热,页面上需要展示的一些通过接口获取的数据,库存预热,预约的时段和数量存入redis
               2.mq异步,削峰,预约成功则扣减redis数量,redis新增一条预约的人员的Key,值为1(预约状态,0表示预约失败,1表示正在预约中,2表示预约成功),
                 值大于1就拦截,防止重复预约,然后发送一mq消息到业务处理逻辑
               3.日志消息发送到mq中收集
               4.mq处理成功,则redis的预约人员的key设置为2,处理失败,则预约状态变为0,redis库存+1
               5.整个预约的主要逻辑加上redision分布式锁,锁的是预约时间段,并且设置超时时间
               6.返回一个正在预约中的状态给前端,前端每隔1s发起一个短轮询,ajax请求,获取是否预约成功,获取到的值为0则表示失败,数据库查询失败原因返回
               7.是否预约成功的逻辑是直接查询redis的时间段和手机号的key,成功则返回,查询不到则重试5次自动失败
           6.nginx作负载均衡,部署三台服务,分散系统压力
           7.nginx反向代理前端代码,开启gzip压缩技术,静态资源缓存
           8.解决tcp连接time_wait问题
               1.linux可以tcp端口数开放到65535个
               2.linux可以开启端口快速重用
           9.新增全局线程池,设置核心40线程,最大线程 2  核心线程,多线程同时获取3个接口数据,CountDownLatch控制返回
       2.硬件方面
           1.mysql主从集群,分散读并发压力
           2.增加两台 16核,64g内存的服务器
   4.优化后的系统
       1.QPS:5000多
   5.现在的解决方案(目的是解决更大的并发与流量,基于以上的解决方案新增以下几点):
       1.引入Springcloud Gateway 网关服务统一鉴权与服务间负载均衡
       2.引入Springcloud Alibaba Sentinal,作服务间的限流与熔断,降级,隔离,保证高可用与集群的正常运行
       3.redis作redis-cluster集群,主从模式,保证数据的高可用性,独写分离,解决内存瓶颈

你可能感兴趣的:(java面试,分布式,微服务)