RPC框架(7)—— 容错层的设计

322 阅读2分钟
三个主要考虑的点——超时重试、接口限流、异常返回

超时重试

谨慎使用,尤其是对于一些非幂等性接口。

  • 间隔重试:适用于请求结果没有实时性要求的场景,如消息的消费
  • 立即重试:在RPC集群中调用比较多,但是在慢网络环境下可能会导致一些意外结果出现。

设计思路:在出现异常的情况下,默认重试次数为1,如果不想使用重试机制的话,也可以设置为0

image.png

此处的重试,不需要延时重试,直接发起重试请求就可以了。

异常返回

整个链路中,出现了异常,那么在客户端返回?还是服务端返回呢 ?

如果在服务端返回的话,主要有三大问题:不易区分异常来自于哪个客户端;客户端调用接口得不到预期的错误提示信息;错误信息都保存在服务端,消耗磁盘空间。

所以:将服务端的异常信息采集起来, 统一返回给调用方,并打印堆栈记录。

修改步骤:

  • 数据封装RpcInvocation对象中,增加一个Throwable字段
  • 服务端尝试获取异常,若异常存在,则将其放入e中
  • 客户端拿到信息后,首先判断是否传回异常对象信息,若有,则将对应的堆栈信息打印出来。

可能出现的问题:异常信息太大,会导致第一个触发数据包接口阈值的请求出现请求异常。

解决办法:在协议尾部添加一个分隔符,并自定义设置每次传输的最大数据包的体积。

接口限流

服务端保护

  • 控制业务整体的连接上限:设置一个统一的连接控制,若当前链接数超过了此阈值,就直接拒绝。Netty中采取主从Reactor多线程模型设计,控制链接的主要是mainreactor,可以自定义一个连接器

  • 对单个应用链接进行控制: 可以使用JDK专门用于控制并发访问特定资源线程数的组件,提供了acqurie和tryacquire两个方法。它内部有一个计数器,当计数器为0的时候,就立即处于阻塞状态,或等待一定时间后处于阻塞状态。acqurie:立刻阻塞,当有大量请求处于阻塞状态而留在服务器内存中的时候,容易导致内存上升,从而频繁出现gc异常,问题越来越严重。TryAcquire:不阻塞,直接返回异常,然后发起第二次重试,路由到其他节点,不会存在请求堆积。

针对这部分问题,可以使用前置/后置过滤器来进行处理。