代码先锋网 代码片段及技术文章聚合

Netty案例(二)之耗时任务的处理

netty版本

  1. 使用的netty版本是io.netty:netty-all:4.1.33.Final

Netty耗时任务的处理

  1. Netty中EventLoop用来处理IO线程,因此handler中的耗时任务(比如数据库连接、远程调用等)不能在EventLoop里面执行。如果有耗时任务,需要将耗时任务添加到业务线程池中执行。
  2. 处理耗时任务的两种方式
    • Handler中加入线程池
    • Context中添加线程池
  3. 两种方式的对比
    • Handler中自定义业务线程池更加灵活,每个Handler都可以自己控制自己的业务线程池,对于非耗时的任务可以不使用业务线程池,直接在EventLoop线程中执行。
    • Context中添加线程池(Netty建议的方式),在pipeline中添加Handler的时候,添加一个业务线程池。这种方式Handler中的代码不需要做任何修改,但是整个Handler都交给业务线程池,无论是否是耗时任务都会加入到队列里,控制的粒度比较粗。
  4. 如果业务ChannelHandler处理逻辑比较简单,执行时间是受控的,业务I/O线程的负载也不重,在这种应用场景下,业务ChannelHandler可以和I/O操作共享同一个线程。使用这种线程模型会带来两个优势:
    • 开发简单,开发业务ChannelHandler的不需要关注Netty的线程模型
    • 性能更高:因为减少了一次线程上下文切换,所以性能会更高。
  5. Netty线程池的特点
    • ChannelEventLoopN:1的关系:一个Channel生命周期内只注册一个EventLoop,一个EventLoop可能会给分配给多个Channel,所以如果一个Channel阻塞可能会导致其他在同一个EventLoop上的Channel都阻塞。
    • 一个EventLoopGroup包含一个或者多个EventLoop,一个EventLoop在其生命周期内只与一个Thread绑定(EbeddedEventLoop除外)EventLoopThread负责处理该事件循环内所有的IO事件

代码案例

Handler自定义业务线程池

  1. Handler中自定义业务线程池

        @ChannelHandler.Sharable
        public class EchoServerHandler extends SimpleChannelInboundHandler<String> {
            public static final ChannelHandler INSTANCE = new EchoServerHandler();
            private static final String LINE = System.getProperty("line.separator");
        
            private EchoServerHandler() {
            }
            protected static ExecutorService newFixedThreadPool() {
                final ThreadFactory threadFactory = new ThreadFactoryBuilder()
                        .setNameFormat("netty-business-%d")
                        .setDaemon(false)
                        .build();
                return new ThreadPoolExecutor(200, 200,
                        0L, TimeUnit.MILLISECONDS,
                        new LinkedBlockingQueue<Runnable>(10000),
                        threadFactory);
            }
            final static ListeningExecutorService service = MoreExecutors.listeningDecorator(newFixedThreadPool());
            @Override
            public void channelRead0(ChannelHandlerContext ctx, String msg) {
                service.submit(new Runnable() {
                    @Override
                    public void run() {
                        //模拟耗时任务
                        try {
                            Thread.sleep(5000);
                            logger.info("execute time 5s");
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                        }
                        ctx.writeAndFlush(msg + LINE).addListener(ChannelFutureListener.CLOSE);
                    }
                });
        
            }
        }
    
  2. Server启动代码

        public class EchoServer {
        
            private final int port;
        
            public EchoServer(int port) {
                this.port = port;
            }
        
            public void start() throws Exception {
                NioEventLoopGroup boss = new NioEventLoopGroup(1);
                NioEventLoopGroup worker = new NioEventLoopGroup();
                try {
                    ServerBootstrap b = new ServerBootstrap();
                    b.group(boss, worker)
                            .channel(NioServerSocketChannel.class)
                            .localAddress(new InetSocketAddress(port))
                            .childHandler(new ChannelInitializer<SocketChannel>() {
                                @Override
                                public void initChannel(SocketChannel ch) {
                                    ChannelPipeline pipeline = ch.pipeline();
                                    pipeline.addLast(new LineBasedFrameDecoder(1024));
                                    pipeline.addLast(new StringDecoder());
                                    pipeline.addLast(new StringEncoder());
                                    pipeline.addLast(EchoServerHandler.INSTANCE);
        
        
                                }
                            });
                    ChannelFuture f = b.bind().sync();
                    System.out.println(String.format("%s started and listen on %s", EchoServer.class.getName(), f.channel().localAddress()));
                    f.channel().closeFuture().sync();
                } finally {
                    boss.shutdownGracefully().sync();
                    worker.shutdownGracefully().sync();
                }
            }
        
            public static void main(String[] args) throws Exception {
                new EchoServer(8080).start();
            }
        }
    
  3. 使用多个telnet连接并请求,输出结果,可以看出执行任务使用的是我们自己定义的线程池

        netty-business-0 execute time 5s
        netty-business-1 execute time 5s
    

Context中添加线程池

  1. Handler代码无需任何的改动,不需要启动任何额外线程来处理任务

        @ChannelHandler.Sharable
        public class EchoServerHandler extends SimpleChannelInboundHandler<String> {
            public static final ChannelHandler INSTANCE = new EchoServerHandler();
            private static final String LINE = System.getProperty("line.separator");
        
            private EchoServerHandler() {
            }
            @Override
            public void channelRead0(ChannelHandlerContext ctx, String msg) {
                //模拟耗时任务
                try {
                    Thread.sleep(5000);
                    System.out.printf("%s execute time 5s \n", Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                ctx.writeAndFlush(msg + LINE).addListener(ChannelFutureListener.CLOSE);
            }
        }
        
    
  2. 启动代码

        public class EchoServer {
           private final int port;
           public EchoServer(int port) {
               this.port = port;
           }
           public void start() throws Exception {
               final ThreadFactory threadFactory = new ThreadFactoryBuilder()
                   .setNameFormat("netty-context-business-%d")
                   .setDaemon(false)
                   .build();
               NioEventLoopGroup boss = new NioEventLoopGroup(1);
               NioEventLoopGroup worker = new NioEventLoopGroup();
               NioEventLoopGroup business = new NioEventLoopGroup(200,threadFactory);
               try {
                   ServerBootstrap b = new ServerBootstrap();
                   b.group(boss, worker)
                           .channel(NioServerSocketChannel.class)
                           .localAddress(new InetSocketAddress(port))
                           .childHandler(new ChannelInitializer<SocketChannel>() {
                               @Override
                               public void initChannel(SocketChannel ch) {
                                   ChannelPipeline pipeline = ch.pipeline();
                                   pipeline.addLast(new LineBasedFrameDecoder(1024));
                                   pipeline.addLast(new StringDecoder());
                                   pipeline.addLast(new StringEncoder());
                                   pipeline.addLast(business, EchoServerHandler.INSTANCE);
       
       
                               }
                           });
                   ChannelFuture f = b.bind().sync();
                   System.out.println(String.format("%s started and listen on %s", EchoServer.class.getName(), f.channel().localAddress()));
                   f.channel().closeFuture().sync();
               } finally {
                   boss.shutdownGracefully().sync();
                   worker.shutdownGracefully().sync();
                   business.shutdownGracefully().sync();
               }
           }
       
           public static void main(String[] args) throws Exception {
               new EchoServer(8080).start();
           }
       }
       
    
    
  3. 使用多个telnet连接并请求,输出结果

        netty-context-business-0 execute time 5s 
        netty-context-business-1 execute time 5s 
    
版权声明:本文为usagoole原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/usagoole/article/details/88025243

智能推荐

PyQt之科学使用线程处理耗时任务以及多线程通信注意事项

目录 前言 PyQt线程科学用法 非科学用法样例 科学用法 线程类 线程通信 线程类在主界面实例化与使用 开启线程 结语 参考文章   前言  本文主要讲解PyQt使用多线程模块QThread解决PyQt界面程序执行耗时操作时,程序卡顿出现的无响应以及界面输出无法实时显示的问题。有时候,我们写的程序界面出现未响应,是因为把需要长时间运行的代码放在了主线程,阻塞了事件循环。 Qt...

一个轻量级的处理耗时任务的Android库

  Create your peanut. and Schedule it Dependency Add it in your root build.gradle at the end of repositories: Add the dependency...

swoole中使用task进程异步的处理耗时任务

我们知道,swoole中有两大进程,分别是 master 主进程和 manager 管理进程。 其中 master 主进程中会有一个主 reactor 线程和多个 reactor 线程,主要的作用就是用来维护TCP连接,处理网络IO,收发数据。 而 manager 管理进程,作用则是 fork 和管理 worker 和 task 进程。 worker 进程的作用是接收 reactor 线程传递的数...

尚硅谷2019年Netty教程 netty netty 耗时任务加入线程池 ----目标netty---step5.03

针对耗时操作,将任务加入队列中 https://www.bilibili.com/video/BV1jK4y1s7GV?p=107 —15 https://www.bilibili.com/video/BV1jK4y1s7GV?p=108 —20 https://www.bilibili.com/video/BV1jK4y1s7GV?p=109 —15 针对耗时...

基于swoole实现多进程处理耗时任务

swoole提供了创建进程的简便方式,同时如果有进程池的需求,也是可以快速上手,因公司swoole较老,swoole原生提供的进程池不能使用,这里就用swoole_process实现了多进程处理耗时任务的需求。 这次的背景是这样的,有个每分钟1次的定时任务,这个定时任务启动后,会循环请求多个agent上的数据,最初的实现是通过for循环去请求各个agent的数据进行处理,这种模式有个固有缺陷,即单...

猜你喜欢

Android处理异步耗时任务,AsyncTask使用教程

原文链接:http://www.jufanshare.com/content/124.html 本教程附Demo:https://gitee.com/qwertiddler/AsyncTask.git 在Android中耗时任务是不能在主线程中执行的,否则会造成线程阻塞,阻塞超过5秒则会出现ANR导致应用崩溃。所以在这时候我们就需要去专门去异步线程处理耗时任务。 Android如何处理耗时任务? ...

SpringBoot使用线程池处理耗时任务

场景分析 在post请求接口中,由于耗时任务处理时间过长,接口返回不可能一直等待业务逻辑处理完全才返回给前端,这时必须使用线程池来处理耗时任务,然后接口直接返回。通过异步处理的方式处理数据。 线程池选择 由于是SpringBoot项目,最终决定使用线程池ThreadPoolExecutor,可以利用Spring提供的对ThreadPoolExecutor封装的线程池ThreadPoolTaskEx...

android 使用service处理数据、后台处理耗时任务或定时任务方法

最近新加了一项功能,要实时的刷新后台的数据,让前端用户不在使用点击事件来获取相应数据!! 思路:使用service在后台持续申请获取数据,然后将数据返回至前端 实现: 方式一:采用Handler的postDelayed(Runnable, long)方法 方式二:采用timer及TimerTask结合的方法 方式三:采用IntentService方法 方式四:采用AlarmManager机制(推荐...