Netty -12- 任务加入异步线程池

介绍

  1. 在 Netty 中做耗时的,不可预料的操作,比如数据库,网络请求,会严重影响 Netty 对 Socket 的处理速度。
  2. 而解决方法就是将耗时任务添加到异步线程池中。但就添加线程池这步操作来讲,可以有2种方式,而且这2种方式实现的区别也蛮大的。
    1. 处理耗时业务的第一种方式—handler 中加入线程池
    2. 处理耗时业务的第二种方式—Context 中添加线程池
      • 当我们使用addLast方法添加线程池后,handler将优先使用这个线程池,如果不添加,将使用IO线程

 流程


方式一

server
public class Server {
    public static void main(String[] args) throws IOException {
        //创建BossGroup和WorkerGroup
        //bossGroup和workerGroup含有的子线程的个数,默认为cpu核数*2
        EventLoopGroup bossGroup=new NioEventLoopGroup(5);
        EventLoopGroup workerGroup=new NioEventLoopGroup();

        //创建服务器端的启动对象,配置参数
        ServerBootstrap  bootstrap=new ServerBootstrap();

        try {
            //使用链式编程来进行设置
            bootstrap.group(bossGroup,workerGroup)  //设置两个线程
                .channel(NioServerSocketChannel.class)  //使用NioServerSocketChannel,作为服务器的通道实现
                .option(ChannelOption.SO_BACKLOG,128)  //设置线程队列里连接个数
                .childOption(ChannelOption.SO_KEEPALIVE,true)//设置保持连接状态
                .childHandler(new ChannelInitializer<SocketChannel>() {  //handler对应的是boosGruop,childHandler对应的是workerGroup
                    //给pipeline设置处理器
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        //可以使用一个集合管理SocketChannel,在推送消息时,可以将业务加入到各个channel对应的
                        // NioEventLoop的taskQueue或者scheduleTaskQueue中
                        System.out.println("客户端socketChannel hashcode="+socketChannel.hashCode());
                        socketChannel.pipeline().addLast(new ServerHandler());
                    }
                });  //给我们的workerGroup的EventLoop对应的管道设置处理器
            System.out.println("服务器 is ready ...");

            //绑定一个端口并且同步,生成一个ChannelFuture对象
            ChannelFuture cf = bootstrap.bind(6668).sync();

            //给 cf 注册监听器,监控我们关心的事件
            cf.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    if(future.isSuccess()){
                        System.out.println("监听端口成功");
                    }else{
                        System.out.println("监听端口失败");
                    }
                }
            });

            //对关闭通道进行监听
            cf.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

    }
}

serverHandler
public class ServerHandler extends ChannelInboundHandlerAdapter {

    static final EventExecutorGroup group=new DefaultEventExecutorGroup(16);

    private final DateTimeFormatter formatter=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");



    /**
    * @Description 读取数据事件(这里我们可以读取客户端发送的消息)
    * @date 2020/7/23 10:33
    * @param ctx  上下文对象,含有:管道(pipeline),通道(channel),地址
    * @param msg  客户端发送的数据
    * @return void
    */
    @Override
    public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf=(ByteBuf)msg;
        System.out.println("接收到的消息:"+buf.toString(CharsetUtil.UTF_8)+"  "+ LocalDateTime.now().format(formatter) +"  当前线程----"+Thread.currentThread().getName());

        //任务
        ctx.channel().eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("服务端发送消息1  "+LocalDateTime.now().format(formatter)+"  当前线程----"+Thread.currentThread().getName());
                ctx.writeAndFlush(Unpooled.copiedBuffer("hello 客户端1\t",CharsetUtil.UTF_8));
            }
        });

        ctx.channel().eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("服务端发送消息2  "+LocalDateTime.now().format(formatter)+"  当前线程----"+Thread.currentThread().getName());
                ctx.writeAndFlush(Unpooled.copiedBuffer("hello 客户端2\t",CharsetUtil.UTF_8));
            }
        });

        //定时任务
        ctx.channel().eventLoop().schedule(()->{
            System.out.println("服务端发送消息3  "+LocalDateTime.now().format(formatter)+"  当前线程----"+Thread.currentThread().getName());
            ctx.writeAndFlush(Unpooled.copiedBuffer("hello 客户端4\t",CharsetUtil.UTF_8));
        },10,TimeUnit.SECONDS);


        //将任务提交到group线程池
        group.submit(new Callable<Object>() {
            @Override
            public Object call() throws Exception {
                System.out.println("服务端发送消息4  "+LocalDateTime.now().format(formatter)+"  当前线程----"+Thread.currentThread().getName());
                ctx.writeAndFlush(Unpooled.copiedBuffer("hello 客户端4\t",CharsetUtil.UTF_8));
                return null;
            }
        });

        System.out.println("发送消息...");
    }

    /**
    * @Description 数据读取完毕
    * @date 2020/7/23 10:46
    * @param ctx  
    * @return void
    */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        //将数据写入到缓冲并刷新
        //一般来讲,我们队发送的数据进行编码
        ctx.writeAndFlush(Unpooled.copiedBuffer(new Date().toString()+":"+"hello,客户端",CharsetUtil.UTF_8));
    }

    /**
    * @Description 处理异常,一般是需要管理通道
    * @date 2020/7/23 11:13
    * @param ctx
    * @param cause
    * @return void
    */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.channel().close();
    }
}

核心代码
//将任务提交到group线程池
group.submit(new Callable<Object>() {
    @Override
    public Object call() throws Exception {
        System.out.println("服务端发送消息4  "+LocalDateTime.now().format(formatter)+"  当前线程----"+Thread.currentThread().getName());
        ctx.writeAndFlush(Unpooled.copiedBuffer("hello 客户端4\t",CharsetUtil.UTF_8));
        return null;
    }
});

运行


方式二

server
public class Server {

    static final EventExecutorGroup group=new DefaultEventExecutorGroup(2);

    public static void main(String[] args) throws IOException {
        //创建BossGroup和WorkerGroup
        //bossGroup和workerGroup含有的子线程的个数,默认为cpu核数*2
        EventLoopGroup bossGroup=new NioEventLoopGroup(5);
        EventLoopGroup workerGroup=new NioEventLoopGroup();

        //创建服务器端的启动对象,配置参数
        ServerBootstrap  bootstrap=new ServerBootstrap();

        try {
            //使用链式编程来进行设置
            bootstrap.group(bossGroup,workerGroup)  //设置两个线程
                .channel(NioServerSocketChannel.class)  //使用NioServerSocketChannel,作为服务器的通道实现
                .option(ChannelOption.SO_BACKLOG,128)  //设置线程队列里连接个数
                .childOption(ChannelOption.SO_KEEPALIVE,true)//设置保持连接状态
                .childHandler(new ChannelInitializer<SocketChannel>() {  //handler对应的是boosGruop,childHandler对应的是workerGroup
                    //给pipeline设置处理器
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        //可以使用一个集合管理SocketChannel,在推送消息时,可以将业务加入到各个channel对应的
                        // NioEventLoop的taskQueue或者scheduleTaskQueue中
                        System.out.println("客户端socketChannel hashcode="+socketChannel.hashCode());

                        //如果我们在addLast添加handler,前面有指定EventExecutorGroup,name该handler会优先加入到该线程池中
                        socketChannel.pipeline().addLast(group,new ServerHandler());
                    }
                });  //给我们的workerGroup的EventLoop对应的管道设置处理器
            System.out.println("服务器 is ready ...");

            //绑定一个端口并且同步,生成一个ChannelFuture对象
            ChannelFuture cf = bootstrap.bind(6668).sync();

            //给 cf 注册监听器,监控我们关心的事件
            cf.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    if(future.isSuccess()){
                        System.out.println("监听端口成功");
                    }else{
                        System.out.println("监听端口失败");
                    }
                }
            });

            //对关闭通道进行监听
            cf.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

    }
}

serverHandler
public class ServerHandler extends ChannelInboundHandlerAdapter {

    private final DateTimeFormatter formatter=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");



    /**
    * @Description 读取数据事件(这里我们可以读取客户端发送的消息)
    * @date 2020/7/23 10:33
    * @param ctx  上下文对象,含有:管道(pipeline),通道(channel),地址
    * @param msg  客户端发送的数据
    * @return void
    */
    @Override
    public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf=(ByteBuf)msg;
        System.out.println("接收到的消息:"+buf.toString(CharsetUtil.UTF_8)+"  "+ LocalDateTime.now().format(formatter) +"  当前线程----"+Thread.currentThread().getName());

        //任务
        ctx.channel().eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("服务端发送消息1  "+LocalDateTime.now().format(formatter)+"  当前线程----"+Thread.currentThread().getName());
                ctx.writeAndFlush(Unpooled.copiedBuffer("hello 客户端1\t",CharsetUtil.UTF_8));
            }
        });

        ctx.channel().eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("服务端发送消息2  "+LocalDateTime.now().format(formatter)+"  当前线程----"+Thread.currentThread().getName());
                ctx.writeAndFlush(Unpooled.copiedBuffer("hello 客户端2\t",CharsetUtil.UTF_8));
            }
        });

        //定时任务
        ctx.channel().eventLoop().schedule(()->{
            System.out.println("服务端发送消息3  "+LocalDateTime.now().format(formatter)+"  当前线程----"+Thread.currentThread().getName());
            ctx.writeAndFlush(Unpooled.copiedBuffer("hello 客户端4\t",CharsetUtil.UTF_8));
        },10,TimeUnit.SECONDS);


        System.out.println("服务端发送消息4  "+LocalDateTime.now().format(formatter)+"  当前线程----"+Thread.currentThread().getName());
        ctx.writeAndFlush(Unpooled.copiedBuffer("hello 客户端4\t",CharsetUtil.UTF_8));

        System.out.println("发送消息...");
    }

    /**
    * @Description 数据读取完毕
    * @date 2020/7/23 10:46
    * @param ctx  
    * @return void
    */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        //将数据写入到缓冲并刷新
        //一般来讲,我们队发送的数据进行编码
        ctx.writeAndFlush(Unpooled.copiedBuffer(new Date().toString()+":"+"hello,客户端",CharsetUtil.UTF_8));
    }

    /**
    * @Description 处理异常,一般是需要管理通道
    * @date 2020/7/23 11:13
    * @param ctx
    * @param cause
    * @return void
    */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.channel().close();
    }
}

核心代码
//如果我们在addLast添加handler,前面有指定EventExecutorGroup,name该handler会优先加入到该线程池中
socketChannel.pipeline().addLast(group,new ServerHandler());

运行


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值