Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

netty-rpc 项目功能详解 #11

Open
xuweilin2014 opened this issue Nov 18, 2022 · 0 comments
Open

netty-rpc 项目功能详解 #11

xuweilin2014 opened this issue Nov 18, 2022 · 0 comments

Comments

@xuweilin2014
Copy link
Owner

xuweilin2014 commented Nov 18, 2022

netty-rpc 项目功能详解

0x01 优雅停机

优雅停机需要满足以下两个要求:

  • 关闭服务提供者:不再接受服务请求,而对于正在执行的服务请求,等待其执行完毕之后再下线
  • 关闭服务消费者:不再发送服务请求,而对于已经发送了的请求,需要等待其响应

netty-rpc 优雅停机的总流程如下所示:

netty-rpc 中实现的优雅停机机制主要包含 6 个步骤:

  1. 收到kill -9进程退出信号,会触发注册在 JVM 中的 ShutdownHook 来执行优雅停机操作
  2. 注销注册中心:会取消注册在 ZK 端的服务元数据信息,并且取消注册在 ZK 端的监听器,最后关闭掉和 ZK 端的连接。因此 ZK 端会通知客户端,服务已经下线,这就实现了服务端不再接受新的服务请求
  3. 注销 Protocol:
    1. Provider 端会关闭掉 HeaderExchangeServer 中的心跳检测,并且会发送 readonly 事件报文通知 consumer 当前服务不可用,同样实现了服务端不再接受新的服务请求。
    2. Provider 端会关闭掉 NettyServer,不过会等待 NettyServer 中的线程池中正在执行的任务执行完毕,这就实现了对于正在执行的服务请求,等待其执行完毕之后再下线。
    3. Provider 端会关闭掉 jmx 服务器以及 http 服务器
    4. Consumer 端会首先将 ReferenceCountExchangeClient 中的引用计数减一,然后关闭掉 HeaderExchangeClient 中的心跳检测,并且在 HeaderExchangeClient 中,会检查 DefaultFuture 中是否还有当前 channel,如果有的话,说明还有请求在等待响应,就等待一段时间,这就实现了对于已经发送了的请求,等待其响应。接着关闭到服务端的连接,不会再发送新的请求到服务端。

0x02 实时监控服务调用情况

有两个关键字 <nettyrpc:service/> 中的 monitor 以及 <nettyrpc:application/> 中的 metrics,其中 metrics 用来表示是否开启全局的监控,默认是开启状态。而 monitor 用来表示是否开启对某一个方法的监控,默认也为 true,也就是开启状态。

服务监控是以类中的方法为单位的,会显示这个方法的调用次数、调用成功的次数、方法被调用的平均耗时、最大耗时以及最小耗时、调用失败的次数、方法调用失败的堆栈信息、上一次调用失败的时间。

对服务调用状态的监控是通过服务端拦截器 MonitorFilter 完成的。当对服务端的方法进行调用时,就会调用这个 MonitorFilter。在这个 MonitorFilter 中,监控的最小单位是类中的方法,并且类中方法的调用数据保存在一个 MetricsVisitor 对象中,这个对象内部保存了各个监控数据。在 MonitorFilter 中首先会让方法继续调用,获取到方法调用的结果,然后就根据类名和方法签名获取到对应的 MetricsVisitor 对象。接下来步骤是:

  • 更新方法的调用次数
  • 根据结果中是否有异常,来判断方法调用成功与否
    • 如果调用成功,那么就更新方法调用成功的次数、更新方法调用最大耗时、最小耗时以及平均耗时;
    • 如果调用失败就更新调用失败的次数、方法调用失败的堆栈信息、上一次调用失败的时间。

在获取 MetricsVisitor 对象时,涉及到并发控制的问题,因为可能会有多个线程同时想要获取到 Visitor 对象。具体的逻辑是使用了一个队列 waiters 来使得多个线程有序地,一次一个地进入到临界区域中,获取 visitor。

  • enter 方法(进入临界区之前执行)具体的逻辑是只有 waiters 队列中的队头元素可以进入临界区,其余的线程只能阻塞等待,即首先将当前线程加入到队列中,然后循环判断当前线程是否是队列的头元素,如果不是的话,就阻塞等待。如果是的话,就执行完方法,进入临界区。
  • exit 方法(退出临界区之后执行)具体逻辑就是将当前线程移出 waiters 队列头元素,并且 waiters 队列不为空的话,唤醒队列的头元素。

另外,在更新 MetricsVisitor 中的各个字段时,也会涉及到并发控制的问题,这里是通过 CAS 的方式来解决的。

0x03 注册中心缓存机制

调用者需要通过注册中心(例如:ZK)上的注册信息,获取提供者,但是如果频繁往 ZK 获取信息,肯定会存在单点故障问题,所以提供了将提供者信息缓存在本地的方法。当消费者无法往注册中心上注册监听器时(也就无法获取提供者的任何信息),就会通过缓存获取提供者的 url 信息,然后更新自己保存的 invoker 集合,消费者通过这些 invoker 集合发起 rpc 调用。

在订阅注册中心的回调处理逻辑当中,也就是当第一次把监听器注册到注册中心对应的目录上时或者之后目录下的结点发生了变更,那么就会返回这个目录下的全量数据。接着会把这个消费者订阅的某一个方法下所有 provider 返回的 url 全都合并起来(一个 RegistryDirectory 只能注册监听一个方法服务),并且以空格作为分隔符,然后保存这些服务提供者信息先保存到内存中的 properties 对象中,key 为 ServiceName,也就是服务的接口名。最后保存到本地缓存文件当中(同步/异步两种方式)。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant