服务发现的作用就是实时感知集群 IP 的变化,实现接口跟服务集群节点 IP 的映射。
在超大规模集群实战中,我们更多需要考虑的是保证最终一致性
终极的解决方案是让调用方实时感知到节点的状态变化,这样他们才能做出正确的选择
画外音:就是心跳检查,这里面有什么特别地方吗?我确实想不到有什么注意地方。
在进一步讲解服务健康检测之前,我想先和你分享一个我曾经遇到过的线上问题
接口调用某台机器的时候已经出现不能及时响应了,
那为什么 RPC 框架还会继续把请求发到这台有问题的机器上呢?
RPC 框架还会把请求发到这台机器上,也就是说从调用方的角度看,它没有觉得这台服务器有问题。”
我们发现,其实更大的问题是我们的服务检测机制有问题,有的服务本来都已经病危了,但我们还以为人家只是个感冒。
我又发现了新的麻烦:
和同事讨论之后,我们找到了可用率这个突破口,应该相对完美了。
可用率的计算方式是某一个时间窗口内接口调用成功次数的百分比(成功次数 / 总调用次数)。
当可用率低于某个比例就认为这个节点存在问题,把它挪到亚健康列表,
这样既考虑了高低频的调用接口,也兼顾了接口响应时间不同的问题。
关键字:RPC 中的路由策略
服务提供方是以集群的方式对外提供服务,那就要考虑一些实际问题。
要知道我们每次上线应用的时候都不止一台服务器会运行实例,那上线就涉及到变更,只要变更就可能导致原本正常运行的程序出现异常,尤其是发生重大变动的时候,导致我们应用不稳定的因素就变得很多。
那对于我们的 RPC 框架来说,有什么的办法可以减少上线变更导致的风险吗?
这就不得不提路由在 RPC 中的应用。具体好在哪里,怎么实现,我们接着往下看。
其核心思想都是一样的,就是让请求按照我们设定的规则发送到目标节点上, 从而实现流量隔离的效果。
其关键点就是调用端收集服务端每个节点的指标数据,再根据各方面的指标数据进行计算打分,最后根据每个节点的分数,将更多的流量打到分数较高的节点上
我们可以考虑这样一个场景。
我们发起一次 RPC 调用,去调用远程的一个服务,比如用户的登录操作,我们会先对用户的用户名以及密码进行验证,验证成功之后会获取用户的基本信息。
当我们通过远程的用户服务来获取用户基本信息的时候,恰好网络出现了问题,比如网络突然抖了一下,导致我们的请求失败了,而这个请求我们希望它能够尽可能地执行成功,那这时我们要怎么做呢?
我们需要重新发起一次 RPC 调用,那我们在代码中该如何处理呢?
是在代码逻辑里 catch 一下,失败了就再发起一次调用吗?这样做显然不够优雅吧。
这时我们就可以考虑使用 RPC 框架的重试
机制。
重试机制是在设置的超时时间到了之后没有返回结果或者服务端出现异常后服务调用端进行再次调用。 首先,不是所有接口都适合重试,如果一个服务是不等幂,那么不适合重试的机制,因为会存在重复提交的问题
在使用 RPC 框架的时候,我们要确保被调用的服务的业务逻辑是幂等的,
这样我们才能考虑根据事件情况开启 RPC 框架的异常重试功能。
这一点你要格外注意,这算是一个高频误区了。
只有符合重试条件的异常才能触发重试,比如网络超时异常、网络连接异常等等。
画外音:网络异常 请求没有发送成功
根据异常触发重试,重新通过负载均衡选择一个节点发送请求消息,并且记录请求的重试次数,
当重试次数达到用户配置的重试次数的时候,就返回给调用端动态代理一个失败异常,否则就一直重试下去
有没有想到连续重试对请求超时时间的影响?
继续考虑这样一个场景:我把调用端的请求超时时间设置为 5s,结果连续重试 3 次,每次都耗时 2s,那最终这个请求的耗时是 6s,那这样的话,调用端设置的超时时间是不是就不准确了呢?
在每次重试后都重置一下请求的超时时间
画外音:
那如果这个服务业务逻辑不是幂等的,比如插入数据操作,那触发重试的话会不会引发问题呢?会的。
分布式服务接口的幂等性如何设计(比如不能重复扣款)?
从这个问题开始,面试官就已经进入了实际的生产问题的面试了。
一个分布式系统中的某个接口,该如何保证幂等性?这个事儿其实是你做分布式系统的时候必须要考虑的一个生产环境的技术问题。啥意思呢?
你看,假如你有个服务提供一些接口供外部调用,这个服务部署在了 5 台机器上,接着有个接口就是付款接口。然后人家用户在前端上操作的时候,不知道为啥,总之就是一个订单不小心发起了两次支付请求,然后这俩请求分散在了这个服务部署的不同的机器上,好了,结果一个订单扣款扣两次。
或者是订单系统调用支付系统进行支付,结果不小心因为网络超时了,然后订单系统走了前面我们看到的那个重试机制,咔嚓给你重试了一把,好,支付系统收到一个支付请求两次,而且因为负载均衡算法落在了不同的机器上,尴尬了。。。
所以你肯定得知道这事儿,否则你做出来的分布式系统恐怕容易埋坑。
这个不是技术问题,这个没有通用的一个方法,这个应该结合业务来保证幂等性。
所谓幂等性,就是说一个接口,多次发起同一个请求,你这个接口得保证结果是准确的,比如不能多扣款、不能多插入一条数据、不能将统计值多加了 1。这就是幂等性。
其实保证幂等性主要是三点:
实际运作过程中,你要结合自己的业务来,比如说利用 Redis,用 orderId 作为唯一键。只有成功插入这个支付流水,才可以执行实际的支付扣款。
要求是支付一个订单,必须插入一条支付流水,order_id 建一个唯一键 unique key
。你在支付一个订单之前,先插入一条支付流水,order_id 就已经进去了。你就可以写一个标识到 Redis 里面去, set order_id payed
,下一次重复请求过来了,先查 Redis 的 order_id 对应的 value,如果是 payed
就说明已经支付过了,你就别重复支付了。