Thrift RPC改进—更加准确的超时管理

前言:

之前我们组内部使用Thrift搭建了一个小型的RPC框架,具体的实现细节可以参考我之前的一篇技术文章:https://www.cnblogs.com/kaiblog/p/9507642.html

相关代码的下载地址:https://github.com/zhangkai253/simpleRPC

在我们的RPC框架中,通过设置socket的timeout属性来控制超时时间,但是这个参数的设置无法保证对超时时间的准确控制,因为只要在超时时间范围内,该socket接收到了1个字节的数据,TimeoutException就不会被抛出。

在我们的日常业务中,竟然会出现设置了300ms超时时间,结果耗时3秒的情况。于是,我们开始寻找更好的解决方案。

方案一:

使用线程池设置超时时间,该方案简单直接,不过多进行叙述。

方案二:

修改底层的TTransport.readAll方法,在其中设置超时时间

Thrift中的TTransport负责进行数据的传输,其基本的类结构图如下所示:

Thrift RPC改进—更加准确的超时管理

我们本次的改进任务主要关心如下几个类:

Thrift RPC改进—更加准确的超时管理

我们的RPC框架底层使用了TFastFramedTransport来执行传输任务,这个类是TFramedTransport类的改进版本,两个类在消息格式上面是一致的,都是通过读取4个字节的消息头来解决拆包和粘包的问题。

只是底层buffer的使用方式上面有所不同,在读取数据的时候TFramedTransport类使用的是TMemoryInputTransport,而TFastFramedTransport则使用的是AutoExpandingBufferReadTransport。

Thrift RPC改进—更加准确的超时管理

通过上面的流程,我们可以看到数据的传输主要是通过在Tsocket中重写TTransport的readAll方法,可以有效地控制超时时间。

方案三:

使用TAsyncClient来替代现在的client,利用TAsyncClientManager来进行超时时间管理,下面我们看看TAsyncClientManager是如何管理请求的。这个问题的答案,我们可以直接从相关的源码中获得,

    public void run() {       while (running) {         try {           try {               // 当Thrift请求发送的时候,如果请求设置了超时时间,则会被放到               // timeoutWatchSet中             if (timeoutWatchSet.size() == 0) {               // 如果没有设置了超时时间的请求,则无限期低等待下去               selector.select();             } else {               // 从timeoutWatchSet中取出距离当前最近的过期时间               long nextTimeout = timeoutWatchSet.first().getTimeoutTimestamp();               long selectTime = nextTimeout - System.currentTimeMillis();               if (selectTime > 0) {                 // 如果过期时间大于当前时间,则等待剩余时间                 selector.select(selectTime);               } else {                 // 如果过期时间小于等于当前时间,则立即返回                 selector.selectNow();               }             }           } catch (IOException e) {             LOGGER.error("Caught IOException in TAsyncClientManager!", e);           }           transitionMethods();           timeoutMethods();           startPendingMethods();         } catch (Exception exception) {           LOGGER.error("Ignoring uncaught exception in SelectThread", exception);         }       }         try {         selector.close();       } catch (IOException ex) {         LOGGER.warn("Could not close selector. This may result in leaked resources!", ex);       }     }

上述三种方案都可以很好地实现RPC框架对超时时间的控制。大家可以根据自己的使用场景选择合适的解决方案。

更新补充:

最后给大家留一个小问题吧,在第3种方案中我们使用了异步的方案来实现对超时时间的控制,我们如何阻塞等待异步请求的结果呢?

如果想进一步沟通和讨论的小伙伴,可以加群聊或者微信进一步交流哈。

Thrift RPC改进—更加准确的超时管理           Thrift RPC改进—更加准确的超时管理

发表评论

评论已关闭。

相关文章