命名服务
在brpc中,NamingService用于获得服务名对应的所有节点。一个直观的做法是定期调用一个函数以获取最新的节点列表。但这会带来一定的延时(定期调用的周期一般在若干秒左右),作为通用接口不太合适。特别当命名服务提供事件通知时(比如zk),这个特性没有被利用。所以我们反转了控制权:不是我们调用用户函数,而是用户在获得列表后调用我们的接口,对应。当然我们还是得启动进行这一过程的函数,对应NamingService::RunNamingService。下面以三个实现解释这套方式:
- file:列表即文件。合理的方式是在文件更新后重新读取。该实现使用关注文件的修改时间,当文件修改后,读取并调用NamingServiceActions::ResetServers告诉框架。
- list:列表就在服务名里(逗号分隔)。在读取完一次并调用NamingServiceActions::ResetServers后就退出了,因为列表再不会改变了。如果用户需要建立这些对象仍然是不够方便的,因为总是需要一些工厂代码根据配置项建立不同的对象,鉴于此,我们把工厂类做进了框架,并且是非常方便的形式:
这套方式是可扩展的,实现了新的NamingService后在global.cpp中依葫芦画瓢注册下就行了,如下图所示:
看到这些熟悉的字符串格式,容易联想到ftp:// zk:// galileo://等等都是可以支持的。用户在新建Channel时传入这类NamingService描述,并能把这些描述写在各类配置文件中。
负载均衡
brpc中LoadBalancer从多个服务节点中选择一个节点,目前的实现见。
和NamingService类似,我们使用字符串来指代一个load balancer,在global.cpp中注册:
健康检查
传统的做法是使用一个线程做所有连接的健康检查,brpc简化了这个过程:为需要的连接动态创建一个bthread专门做健康检查(Socket::HealthCheckThread)。这个线程的生命周期被对应连接管理。具体来说,当Socket被SetFailed后,健康检查线程就可能启动(如果SocketOptions.health_check_interval为正数的话):
- 定期连接直到远端机器被连接上,在这个过程中,如果Socket析构了,那么该线程也就随之退出了。
- 连上后复活Socket(Socket::Revive),这样Socket就又能被其他地方,包括LoadBalancer访问到了(通过Socket::Address)。