netty入门篇——组件

1、EventLOoop

事件循环对象

EventLoop本质是一个单线程执行器(同时维护一个Selector),里面有run方法处理Channel上源源不断的事件。

它的继承关系

  • 一条线是继承自就j.uc.ScheduledExecutorService因此包含了线程池中所有的方法
  • 另一条是继承自netty自己的OrderedEventExecutor
    • 提供了boolean inEventLoop(Thread thread);方法判断一个线程是否属于此EventLoop
    • 提供了parent()方法来看看自己属于哪个EventExecutorGroup

事件循环组

EventLoopGroup是一组EventLoop,Channel一般会调用EventLoopGroup的register方法来绑定其中一个EventLoop,后续这个Channel上的io事件由此EventLoop来处理(保证了io事件处理时的线程安全)

  • 继承自netty自己的EventExecutorGroup
    • 实现了Iterable接口遍历EventLoop的能力
    • 另有next方法获取集合中下一个EventLoop

 💡 方法使用

public static void main(String[] args) {
//        1、初级事件循环组
        NioEventLoopGroup group = new NioEventLoopGroup(2);  //io 事件,普通任务,定时任务
//        DefaultEventLoopGroup group1 = new DefaultEventLoopGroup(); //普通任务,定时任务

//        2、获取下一个事件循环对象   会循环获取EventLoopGroup组里的EventLoop执行任务
        System.out.println(group.next());

//        3、执行普通任务
        group.next().execute(()->{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.debug("ok");
        });
//        4、执行定时任务(继承了ScheduledExecutorService线程池)
        group.next().scheduleAtFixedRate(()->{
            log.debug("ng");
        },1,3, TimeUnit.SECONDS);

        log.debug("main");
    }

每一个客户端的请求发送过来时,都会和一个EventLoop进行绑定,绑定后,不会更改,当这个请求发送数据时都会是由这个请求来处理数据

EventLoop使用

  • 可以把EventLoop的职责进行细分
public static void main(String[] args) {
        new ServerBootstrap()
//                把职责设置得更加明确,分成boss和worker角色,分别处理不同类型事件
//                  boss负责serverSocketChannel 上的accept事件  worker只负责socketChannel上的读写
                .group(new NioEventLoopGroup(),new NioEventLoopGroup(2))
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new ChannelInboundHandlerAdapter(){
                            @Override                                           //ByteBuf
                            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                               ByteBuf buf = (ByteBuf) msg;
                               log.debug(buf.toString(StandardCharsets.UTF_8));
                            }
                        });
                    }
                }).bind(8080);
    }

 📢 handler执行中如何换人(切换执行的EventLoop)?

关键代码 io.netty.channel.AbstractChannelHandlerContext`

通过inEventLoop()判断下一个handler中的线程,是否和eventLoop是同一个线程,不是就直接切换为下一个handler的线程

2、Channel

channel的主要作用

  • close() 可以用来关闭channel
  • closeFuture() 用来处理channel的关闭
    • sync() 方法作用是同步等待channel关闭
    • addKistener() 方法是异步等待channel关闭
  • pipeliner() 方法添加处理器
  • write() 方法将数据写入,不会立即刷出,会先存储在缓冲区
  • writeAndFlush()方法将数据写入并直接通过网络刷出(发送出去)

ChannelFuture,返回的异步操作结果,因为Netty中的所有IO操作都是异步的,所以不能够保证结果一定会及时返回;只会返回ChannelFuture作为异步返回的信息;

  • ChannelFuture.sync() 阻塞住当前线程,直到nio线程连接建立完毕才会停止阻塞
  • channelFuture.channel() 获取建立连接的Channel
  • channelFuture.addListener() 异步处理,等待nio线程连接建立后调用该方法
  • 带有Future、Promise的类型都是和异步方法配套使用,用来处理结果
public static void main(String[] args) throws InterruptedException {
//        
        ChannelFuture channelFuture = new Bootstrap()
                .group(new NioEventLoopGroup())
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<NioSocketChannel>() {
                    @Override //在连接建立后被调用
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new StringEncoder()); //把字符串编码成ByteBuffer发送
                    }
                })
//                5、连接到服务器
//                      异步非阻塞方法,main 发起调用,真正执行connect 是nio线程
                .connect(new InetSocketAddress("localhost", 8080));

/*//        1、同步等待 connect去执行完毕(建立连接)才继续向下运行
        channelFuture.sync();
//          无阻塞执行线程
        Channel channel = channelFuture.channel();
        log.info("----{}",channel);
//                6、向远程服务器发送数据
        channel.writeAndFlush("雄安吗 world");*/

//        2、使用addListener 方法异步处理结果
        channelFuture.addListener(new ChannelFutureListener() {
            @Override
//            在nio线程连接建立好之后,会调用operationComplete
            public void operationComplete(ChannelFuture future) throws Exception {
                Channel channel = future.channel();
                log.info("----{}", channel);
                channel.writeAndFlush("雄安吗 world");
            }
        });
        
        System.out.println("");
    }

closeFuture关闭后需要执行操作;

public static void main(String[] args) throws InterruptedException {
        NioEventLoopGroup group = new NioEventLoopGroup();
        ChannelFuture channelFuture = new Bootstrap()
                .group(group)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
                        ch.pipeline().addLast(new StringEncoder());
                    }
                }).connect(new InetSocketAddress("localhost",8080));

        Channel channel = channelFuture.sync().channel();
        log.debug("channel对象:{}",channel);
        new Thread(()->{
            Scanner scanner = new Scanner(System.in);
            while (true){
                String line = scanner.nextLine();
                if ("q".equals(line)){
                    channel.close();
                    log.debug("channel对象内{}",channel);
                    break;
                }
                channel.writeAndFlush(line);
            }
        },"input").start();

       ChannelFuture closeFuture = channel.closeFuture();
/*         log.info("执行关闭");
        closeFuture.sync();    //可以保证下面的操作一定是在执行关闭之后--同步执行
        log.debug("处理关闭之后的操作");*/

//        异步执行关闭closeFuture后的操作;
        closeFuture.addListener((ChannelFutureListener) future-> {
                log.debug("处理关闭之后的操作");
//                把group进行优雅的关闭掉
                group.shutdownGracefully();
        });
    }

3、Future & Promise

jdk Future ← netty Future ←promise 三者是继承扩展关系

  • jdk Future 只能同步等待任务结束(或成功、或失败)才能得到结果
  • netty Future 可以同步等待任务结束得到结果,也可以异步方式得到结果,但都是要等任务结束
  • netty Promise不仅有netty Future的功能,而且脱离了任务独立存在,只作为两个线程间传递结果的容器

📢 方法对比:

jdk future

public static void main(String[] args) throws ExecutionException, InterruptedException {
//        1、线程池
        ExecutorService service = Executors.newFixedThreadPool(2);
//        2、提交任务
        Future<Integer> future = service.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                log.debug("执行计算");
                Thread.sleep(1000);
                return 50;
            }
        });
//        3、主线程通过future 来获取结果
        log.debug("等待结果");
        log.debug("结果是{}",future.get());
    }

Netty Future

public static void main(String[] args) throws ExecutionException, InterruptedException {
        NioEventLoopGroup group = new NioEventLoopGroup();
        EventLoop eventLoop = group.next();
        Future<Integer> future = eventLoop.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                log.debug("执行计算");
                Thread.sleep(1000);
                return 70;
            }
        });
//        3、主线程通过future 来获取结果
//              同步获取结果
        log.debug("等待结果");
//        log.debug("结果是{}",future.get());
//              异步获取结果
        future.addListener(new GenericFutureListener<Future<? super Integer>>() {
            @Override
            public void operationComplete(Future<? super Integer> future) throws Exception {
                log.debug("接收结果{}",future.getNow());

            }
        });
    }

Netty Promise

public static void main(String[] args) throws ExecutionException, InterruptedException {
//        1、线程池
        ExecutorService service = Executors.newFixedThreadPool(2);
//        2、提交任务
        Future<Integer> future = service.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                log.debug("执行计算");
                Thread.sleep(1000);
                return 50;
            }
        });
//        3、主线程通过future 来获取结果
        log.debug("等待结果");
        log.debug("结果是{}",future.get());
    }

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值