• Netty 源码阅读的思考------耗时业务到底该如何处理


    目录大纲:

    1. 前言
    2. 处理耗时业务的第一种方式-------handler 种加入线程池
    3. 处理耗时业务的第二种方式-------Context 中添加线程池
    4. 总结:两种方式的对比和思考

    前言

    熟悉 Netty 的同学都知道,不能在 Netty 中做耗时的,不可预料的操作,比如数据库,网络请求,这将会严重影响 Netty 对 Socket 的处理速度。而解决方法就是将耗时任务添加到异步线程池中。但就添加线程池这步操作来讲,可以有2种方式,而且这2种方式的区别也蛮大的。今天就好好讲一讲。

    1. 处理耗时业务的第一种方式-------handler 种加入线程池

    以我们之前的 Netty 的 demo 源码例子,在 channelRead 方法种进行异步:

    上图中的 channelRead 方法,我们模拟了一个耗时 10 秒的操作,于是,我们将这个任务提交到了一个自定义的业务线程池中,这样,就不会阻塞 Netty 的 IO 线程。

    这样操作之后,整个程序的逻辑是这样的:

    解释一下上图,当 IO 线程轮询到一个 socket 事件,然后,IO 线程开始处理,当走到耗时 handler 的时候,将耗时任务交给业务线程池。当耗时任务执行完毕再执行 pipeline write 方法的时候(代码中使用的是 context 的 write 方法,上图画的是执行 pipeline 方法),会将任务这个任务交给 IO 线程。

    下面是 write 方法的源码:

    当判定下个 outbound 的 executor 线程不是当前线程的时候,会将当前的工作封装成 task ,然后放入 mpsc 队列中,等待 IO 任务执行完毕后执行队列中的任务。很明显,下个任务的线程肯定是 IO 线程,因为我们没有设置。

    2. 处理耗时业务的第二种方式-------Context 中添加线程池

    第二种方式是 Netty 建议的方式,在添加 pipeline 中的 handler 时候,添加一个线程池:

    而 handler 中的代码不用做任何修改:

    当我们在调用 addLast 方法添加线程池后,handler 将优先使用这个线程池,如果不添加,将使用 IO 线程。

    所以,当走到 AbstractChannelHandlerContext 的 invokeChannelRead 方法的时候,executor.inEventLoop() 是不会通过的,因为当前线程是 IO 线程,Context(也就是 Handler) 的 executor 是业务线程,所以会异步执行,如下:

    这个时候,后面的整个流程就变成和第一个方式一样了。

    总结: 两种方式的对比和思考

    有什么区别呢?第一种方式在 handler 中添加异步,可能更加的自由,比如如果需要访问数据库,那我就异步,如果不需要,就不异步,异步会拖长接口响应时间。因为需要将任务放进 mpscTask 中。如果不凑巧,IO 时间很短,task 很多,可能一个循环下来,都没时间执行整个 task,导致响应时间达不到指标。

    第二种方式是 Netty 建议的,但是,这么做会将整个 handler 都交给业务线程池。不论耗时不耗时,都加入到队列里,不够灵活。

    再回顾一下我们刚开始的图吧:

    个人建议还是使用第一种方式,比较灵活。

  • 相关阅读:
    重新开始学习Linux
    2006:远离windows, 拥抱linux!
    适合做首页的网站: http://www.netvibes.com/
    netvibes出问题了,登陆不进去。
    推荐开源的rss阅读器rssreader 3.0,支持中文
    ubuntu的即时通讯gaim不错, 如何连qq呢?
    linux是否能把显示改为1024x768呢?
    通过IRC学习ubuntu很不错
    Firefox 扩展软件介绍(转载) Mozine 论坛
    几个有意思的网站
  • 原文地址:https://www.cnblogs.com/stateis0/p/9062151.html
Copyright © 2020-2023  润新知