蚂蚁金服近期开源了研发多年的SOFA一篮子框架,其中就有一个很是核心的RPC框架,它叫SOFA-BOLT。小编今天花了近一天的时间仔细阅读研究它的源码,阅读过程当中遇到了很多问题,蚂蚁金服的相关技术人员都很是耐心的及时解答了个人疑难。这里将我从中学到的知识点一并分享给你们。程序员
SOFA-BOLT基于开源的Netty框架,同时提供了服务器和客户端的实现。它的源码很是值得一读,结构简单,考虑周全,毫不是一个普通的玩具。它没有滥用设计模式,源码阅读起来比较直接,没有太多绕来绕去的复杂结构。算法
一个节点既能够同时既是RPC服务器又是客户端,做为客户端该节点须要其它节点提供服务,做为服务器它能够为其它节点提供服务。不过上面这张图并非合理的结构,由于两个服务相互耦合了,我须要你,你也须要我,就成了鸡蛋问题。比较合理的结构通常以下图所示,它们之间不构成环。设计模式
通信协议是客户端和服务器之间交流的语言,SOFA定义了本身的一套通信协议,它的编码解码分为二层,第一层是消息体对象的二进制序列化,这部分默认由开源的Hession协议库序列化完成,第二层是负责给序列化的消息体增长一系列包装字段,造成一个完整的消息。包括请求ID、消息体的长度、协议版本号和CRC32校验位等等安全
若是但愿进一步优化网络性能,SOFA还提供了Snappy压缩协议,能够在现有的两层协议基础上增长第三层,能显著下降网络传输负担。压缩是时间换空间,提高网络性能的同时,它也会加剧CPU计算,因此在使用时须要适当进行权衡。bash
客户端和服务器之间通常须要创建多个链接,可是也不能每一个请求都创建一个链接。通常是经过维护一个链接池,限定最大链接数。客户端经过有限的链接来和服务器进行通讯。服务器
咱们在使用Jedis客户端和Redis服务器进行通讯时,也是经过链接池来获取链接的。Jedis的链接必须是线程独占的,由于它不是线程安全的。从链接池中获取链接时,其它线程就暂时拿不到这个链接了,待当前线程处理完毕后,要将链接归还给线程池,这样其它线程才能够继续使用这个链接。网络
Redis的客户端请求和应答是顺序性的,一问一答,因此请求和应答不须要惟一ID就能够创建起关联。多线程
Bolt不同,它的问答是乱序的,问和答之间是必须经过请求的惟一ID来创建起关联。Bolt的客户端是线程安全的,它能够同时传递多个请求,链接对象会维护一个正在处理的RPC请求对象字典。当客户端想要发起RPC请求时,它不是从链接池中摘出一个独占链接,而是随意选择一个链接来传递本身的请求,这个链接也能够被其它线程同时使用。app
客户端提供了多种复杂均衡的实现,阿里默认使用带权重的随机算法(RandomLoadBalancer),此外还有负载均衡
服务器采用传统netty多线程模型,一个acceptor线程专门用来接收链接,而后扔给io线程处理读消息并解码成请求对象,最后扔给业务线程池进行处理。
客户端和服务器之间会有定时心跳检测链接的存活,默认30s来一次。tcp的关闭是经过FIN包来通知对方的,若是由于网络问题,对方连FIN包都收不到,那么即便一边关闭了套接字,另外一边可能还觉得链接正常。因此心跳检测存活机制在长链接应用里很是广泛。若是客户端连续发了三次心跳都没有收到服务器的回复,那么就认为链接已经关闭。服务器也会有链接存活检测,若是一个客户端链接90s内没有任何消息进来,那么也认为该链接已经断开。服务器不会主动发送心跳消息。
RPC通常是由客户端向服务器发起一个请求,而后收到服务器的应答。Bolt的RPC是双工通讯,服务器也能够向客户端主动发起请求,它们共享一个TCP链接。TCP链接自己就是双工的,因此这也不算什么奇迹。只是服务器在什么业务场景须要向客户端主动发起请求,这个蚂蚁并无进行详细说明。
客户端做为主动链接方,它要负责重连和发起心跳消息。服务器做为被动方,它不须要处理重连,若是链接断开,它就直接将链接从集合中移除就行,不须要作特殊的处理,可是它会检测心跳消息,若是在必定时间内链接通道没有任何消息到来,它就会主动关闭。
客户端的重连策略是一个单独的模块,有两个地方会成为重连的入口。一个是正常链接断开触发channelInActive回调,另外一个就是重连链接不能创建成功时须要进行重试。Bolt有一个单独的重连线程,全部须要重连的链接会被包装成一个任务塞进这个线程的任务队列,该线程不断地从队列里拿任务进行重连处理,若是重连失败会尝试再将任务从新包装进队列延后继续处理。默认是1s钟处理一个重连任务。
RPC链接是延迟创建的,它在第一次客户端发送RPC请求时尝试进行链接,若是链接失败,它会当即继续重连最多默认两次。若是三次尝试链接后仍是没有创建成功,就向上层爆出异常。它不须要包装一个重连任务塞进ReconnectManager,由于后续客户端请求会继续触发链接。
RPC一般是一应一答,客户端能够同步等待响应,也能够提供回调接口等待结果通知。Bolt除了提供应答模式以外,还提供了oneway单向消息,这种消息服务器收到后不用回复,客户端发送请求以后就当即返回了也不须要等待结果。
oneway消息通常用于不那么重要的日志类消息,它不能保证服务器必定能收到,因此此种业务消息应该是那种容许丢失的消息,形式上相似于UDP,它在牺牲可靠性的前提下能大幅提高消息的吞吐量。
Bolt提供了回调接口,方便监控系统能够对请求的调用情况进行分析。监控的客户端能够经过实现该接口,注册进RPC的客户端和服务器进行打点收集日志,而后发送到日志分析系统。
interface Tracer {
void startRpc(SofaRequest request);
void serverReceived(SofaRequest request);
void serverSend(SofaRequest request, SofaResponse response, Throwable throwable);
void clientReceived(SofaRequest request, SofaResponse response, Throwable throwable);
...
}
复制代码
Bolt是一个成熟的比较复杂的RPC系统,这篇小文章只讲解了其中一部分,内部还有大量的实现细节有待去挖掘。
阅读相关文章,请关注公众号「码洞」,SOFA开源系列代码是Java程序员的大宝藏,它有太多的项目和代码会让不少用户感到望而生畏,后续我会陆续放出SOFA开源系列其它模块的分析文章,帮助你们轻松理解SOFA项目。