10-Netty 高性能架构设计-任务队列(普通任务, 定时任务)

TaskQueue 任务队列

任务队列中的Task有三种典型使用场景

  1. 用户程序自定义的普通任务 [举例说明]
  2. 用户自定义定时任务
  1. 非当前Reactor线程调用Channel的各种方法
    1. 例如在推送系统的业务线程里面, 根据用户的标识, 找到对应的Channel引用,然后调用Write类方法向该用户推送消息, 就会进入到该场景,最终的write会提交到任务队列中后被异步消费

问题

如果在读取或者操作时碰到非常耗时的业务, 那么他就会阻塞在业务执行的地方,我们希望他可以异步执行,而不是阻塞事件, 我们可以提交到该Channel对应的NioEventLoop的taskQueue中

修改NettyServerHandler的ChannelRead方法

Thread.sleep(10 * 1000);
ctx.writeAndFlush(Unpooled.copiedBuffer("Hello 客户端 我读取完成喽", StandardCharsets.UTF_8));
System.out.println("go on....");

这样测试,我们服务器端,channelRead方法就会阻塞10秒才返回

用户自定义普通任务

使用用户自定义普通任务解决

ctx.channel().eventLoop().execute(new Runnable() {
    @Override
    public void run() {
        try {
            Thread.sleep(10 * 1000);
            ctx.writeAndFlush(Unpooled.copiedBuffer("Hello 客户端 我读取完成喽", StandardCharsets.UTF_8));
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println("发生异常");
        }
    }
});

通过上下文对象获取Channel,获取EventLoop,并提交一个任务

测试可以,这样的话,ChannelRead事件不会阻塞,并且任务是在10秒后返回的

但是这里如果是多任务那么时间是累加的

// 应为是任务队列所以第一个任务返回是延迟10秒
ctx.channel().eventLoop().execute(new Runnable() {
    @Override
    public void run() {
        try {
            Thread.sleep(10 * 1000);
            ctx.writeAndFlush(Unpooled.copiedBuffer("Hello 客户端 我读取完成喽", StandardCharsets.UTF_8));
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println("发生异常");
        }
    }
});
// 第二个任务返回是 上面的 10 + 下面的 20 = 30 秒
ctx.channel().eventLoop().execute(new Runnable() {
    @Override
    public void run() {
        try {
            Thread.sleep(20 * 1000);
            ctx.writeAndFlush(Unpooled.copiedBuffer("Hello 客户端 我读取完成喽", StandardCharsets.UTF_8));
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println("发生异常");
        }
    }
});

用户自定义定时任务

// 用户自定义定时任务
ctx.channel().eventLoop().schedule(() -> {
    try {
        Thread.sleep(10 * 1000);
        ctx.writeAndFlush(Unpooled.copiedBuffer("Hello 客户端 我读取完成喽", StandardCharsets.UTF_8));
    } catch (InterruptedException e) {
        e.printStackTrace();
        System.out.println("发生异常");
    }
}, 5, TimeUnit.SECONDS);

延时5秒

执行测试 ok

那么普通任务和延时任务并存,那么他的执行时间如何呢?

经过测试一个普通任务10秒,一个定时任务5秒+执行10秒, 总共会在20秒执行完成,在10秒返回一个, 在20秒返回一个

预测普通任务和延时任务同时开始, 普通任务开始执行,延时任务开始倒计时,在延时任务倒计时5秒后进入等待,而普通任务10秒执行完成后延时任务直接开始执行,所以在20秒返回

方案再说明

  1. Netty抽象出两组线程池, BOSSGroup专门负责接收客户端连接, WorkerGroup专门负责网络读写操作
  2. NioEventLoop表示一个不断循环执行处理任务的线程, 每个NioEventLoop都有一个Selector, 用于监听绑定在其上的Socket网络通道
  1. NioEventLoop内部采用串行化设计, 从消息的读取->解码->处理->编码->发送,始终由IO线程NioEventLoop负责
  2. NioEventLoopGroup 下包含多个NioEventLoop
    1. 每个NioEventLoop中都包含一个Selector,和一个TaskQueue
    2. 每个NioEventLoop的selector上可以注册监听多个NioChannel
    1. 每个NioChannel只会绑定在唯一的NioEventLoop上
    2. 每个NioChannel都绑定有一个自己的ChannelPipeline
posted @ 2022-01-21 14:35  彼岸舞  阅读(330)  评论(0编辑  收藏  举报