并发编程 | Netty - [taskQueue & scheduledTaskQueue]

§1 作用

Netty 是一个以 性能至上 为第一原则的框架
但 Netty 实际上还是一个基于 NIO 的框架,它依然只是 非阻塞 而不是 异步

这里说的 异步 与否,是指系统底层 IO 模型是否阻塞,而不是接口调用方式
常说的 Netty 是异步非阻塞其实都是其接口调用方式说的,参考 基础 | NIO - [IO 模式的演进]
Netty 在实现时并没有广泛而成熟的使用 AIO (NIO 2.0)
这是因为 linux 上 AIO 技术依然不够成熟,不能良好的支持所有场景

同步意味着依然会在系统进程中阻塞,因此 Netty 会通过轮询的方式挑出可处理的业务进行处理
达到了 除了必要的系统阻塞开销外,尽量避免其他阻塞 的目的,如下图

  • 可以在基于 NIO 的模式下,系统阻塞是无法避免的阻塞,是真实处理 IO 就绪的时间
  • 通过轮询,可以是处理任务的线程忽略卡在系统阻塞的任务,而是处理可以被处理任务
  • 这么做的核心思路是:用户线程便宜行事,能处理谁处理谁,能处理多少处理多少,尽量压榨用户线程的所有时间
  • 再结合接口异步,达到了 伪异步 的接口手感和性能

在这里插入图片描述
但是,Netty 中处理连接 NioSocketChannel 中业务时,可能遇到 业务造成的阻塞,比如业务处理时间本身比较长
这相当于在必要的系统阻塞(仅限于系统读写的必要时间)外,额外引入了新的阻塞
为了维护并实践 Netty 效率第一的原则,这种业务通过 taskQueue / scheduledTaskQueue 转为异步处理

  • taskQueue
    • 用于异步处理
    • NioEventLoop 中,继承自 SingleThreadEventExecutor
    • 是一个队列 Queue<Runnable>
  • scheduledTaskQueue
    • 用于异步处理
    • NioEventLoop 中,继承自 AbstractScheduledEventExecutor
    • 是一个优先级队列 PriorityQueue<ScheduledFutureTask<?>>

而 taskQueue / scheduledTaskQueue 的存在,变相证明 Netty 是非异步的。非异步,意味着即使从操作系统层面而言,也是几乎所有的 CPU 时间都没有浪费。因此,塞与非阻塞对于应用的吞吐量已经没有什么影响了,也因此,没有必要加入任务队列

§2 使用场景

用户程序自定义普通任务

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    ctx.channel().eventLoop().execute(()->{
        try {
            TimeUnit.SECONDS.sleep(10);
            ByteBuf buf = (ByteBuf) msg;
            System.out.println(buf.toString(CharsetUtil.UTF_8));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });
    System.out.println("client read done...");
}

用户程序已定义定时任务

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    ctx.channel().eventLoop().schedule(()->{
        try {
            TimeUnit.SECONDS.sleep(10);
            ByteBuf buf = (ByteBuf) msg;
            System.out.println(buf.toString(CharsetUtil.UTF_8));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    },3,TimeUnit.SECONDS);
    System.out.println("client read done...");
}

非当前 Reactor 线程调用 Channel 的各种方法
如推送业务:

  • 客户端连接服务端时,服务端记录客户端的 hashcode
  • 根据 hashcode 维护 SocketChannel 容器
  • 按用户关注或喜好生成推送内容,连同对应的 hashcode 推送到 taskQueue
  • 按 hashcode 从 SocketChannel 容器中找到对应的 SocketChannel,发送内容
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值