• nio之netty5应用


      1、netty5和netty4的区别不是很大,但是与netty3差别还是有的。这里不介绍netty4,因为和netty5的方式都差不多。netty5的复杂性相对于netty3要多很多了。基本上架构都被重构了。所以这里主要是介绍一些属性和用法。

      2、核心的变化主要有:

      支持Android
     
      提供了:
    •  移动设备变成更加强大
    • 通过Ice Cream Sandwich解决了在ADK中最著名的与NIO和SSLEngine相关的问题,且
    • 用户显然想要重用他们应用中的的编解码和处理器代码。
      我们决定官方支持Android(4.0及以上版本)
     
      简化处理器层次
     
      ChannelInboundHandler和ChannelOutboundHandler整合为ChannelHandler。ChannelHandler现在包含输入和输出的处理方法。
     
      ChannelInboundHandlerAdapter,ChannelOutboundHandlerAdapter和ChannelDuplexHandlerAdapter已被废弃,由 ChannelHandlerAdapter代替。
     
      由于现在无法区分处理器(handler) 是输入还是输出的处理器,CombinedChannelDuplexHandler现在由 ChannelHandlerAppender代替。
     
      更多相关变化,可参考https://github.com/netty/netty/pull/1999
     
      channelRead0() → messageReceived()
     
      我知道。这是一个愚蠢的错误。如果你使用了SimpleChannelInboundHandler,你需要把channelRead0()重命名为messageReceived()。
     
      废弃中移除的
     
      Channel.deregister()已被移除。不再生效和被使用。取而代之的,我们将允许Channel被充注册到不同的事件循环。
     
      ChannelHandlerContext.attr(..) == Channel.attr(..)
     
      Channel和ChannelHandlerContext类都实现了AttributeMap接口,使用户可以在其上关联一个或多个属性。有时会让用户感到困惑的是Channel和ChannelHandlerContext都有其自己的存储用户定义属性的容器。例如,即使你通过Channel.attr(KEY_X).set(valueX)给属性'KEY_X’赋值,你却无法通过ChannelHandlerContext.attr(KEY_X).get()方法获取到值。反之亦是如此。这种行为不仅仅令人不解而且还浪费内存。
     
      为了解决这个问题,我们决定每个Channel内部仅保留一个map。AttributeMap总是用AttributeKey作为它的key。AttributeKey确保键的唯一性,因此每个Channel中如果存在一个以上的属性容易是多余的。只要用户把他自己的AttributeKey定义成ChannelHandler的private static final变量,就不会有出现重复key的风险。
     
      更简单更精确的缓冲区泄漏追踪
     
     
      之前,查找缓冲区泄漏是很困难的,并且泄漏的警告信息也不是很有帮助。现在我们有了增强的泄漏报告机制,该机制会在增长超过上限时触发。
     
      更多的信息可查看:http://netty.io/wiki/reference-counted-objects.html 。由于该特性十分重要,所以也移植入了4..0.14.Final版中。
     
      PooledByteBufAllocator成为默认的allocator
     
      在4.x版本中,UnpooledByteBufAllocator是默认的allocator,尽管其存在某些限制。现在PooledByteBufAllocator已经广泛使用一段时间,并且我们有了增强的缓冲区泄漏追踪机制,所以是时候让PooledByteBufAllocator成为默认了。
     
      全局唯一的Channel ID
      每个Channel现在有了全局唯一的ID,其生成的依据是:
     
       * MAC地址(EUI-48或是EUI-64),最好是全局唯一的,
       * 当前进程的ID
       * System#currentTimeMillis()
       * System#nanoTime()
       * 随机的32位整数,以及
       * 系列递增的32位整数
     
      可通过Channel.id()方法获取Channel的ID。
     
      更灵活的线程模型
     
     
      增加了新的ChannelHandlerInvoker接口,用于使用户可以选择使用哪个线程调用事件处理方法。替代之前的在向ChannelPipeline添加 ChannelHandler时指定一个EventExecutor的方式,使用该特性需要指定一个用户自定义的 ChannelHandlerInvoker实现。
     
     
      EmbeddedChannel的易用性
     
      EmbeddedChannel中的readInbound()和readOutbound()方法返回专门类型的参数,因此你不必在转换他们的返回值。这可以简化你的测试用例代码。
    EmbeddedChannel ch = ...;
    
    // BEFORE:
    FullHttpRequest req = (FullHttpRequest) ch.readInbound();
    
    // AFTER:
    FullHttpRequest req = ch.readInbound();
    
    
     
      使用Executor代替ThreadFactory
     
      有些应用要求用户使用Executor运行他们的任务。4.x版本要求用户在创建事件循环(event loop)时指定ThreadFacotry,现在不再是这样了。
     
      关于该变化的更多信息,可参考:https://github.com/netty/netty/pull/1762
     
      Class loader友好化
     
      一些类型,如AttributeKey对于在容器环境下运行的应用是不友好的,现在不是了。
     
      编解码和处理器(handlers)
     
       * XmlFrameDecoder支持流式的XML文档
     
       * 二进制的memcache协议编解码
       * 支持SPDY/3.1 (也移植到了4.x版本)
       * 重构了HTTP多部分的编解码
     

      3、这里写了一个简单的实现过程,来区分netty5和netty3的写法

      1)服务类和处理类

    import io.netty.bootstrap.ServerBootstrap;
    import io.netty.channel.*;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.nio.NioServerSocketChannel;
    import io.netty.handler.codec.string.StringDecoder;
    import io.netty.handler.codec.string.StringEncoder;
    
    public class Server {
    
        public static void main(String[] args) {
            //服务类
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            //声明两个线程池
            EventLoopGroup boss = new NioEventLoopGroup();
            EventLoopGroup work = new NioEventLoopGroup();
    
            try {
                //设置线程组
                serverBootstrap.group(boss,work);
                //设置服务socket工厂
                serverBootstrap.channel(NioServerSocketChannel.class);
                //设置管道
                serverBootstrap.childHandler(new ChannelInitializer<Channel>() {
                    protected void initChannel(Channel channel) throws Exception {
                        channel.pipeline().addLast(new StringDecoder());
                        channel.pipeline().addLast(new StringEncoder());
                        channel.pipeline().addLast(new ServerHandler());
                    }
                });
                //设置服务器连接数
                serverBootstrap.option(ChannelOption.SO_BACKLOG,2048);
                //设置tcp延迟状态
                serverBootstrap.option(ChannelOption.TCP_NODELAY,true);
                //设置激活状态,2小时清除
                serverBootstrap.option(ChannelOption.SO_KEEPALIVE,true);
                //监听端口
                ChannelFuture channelFuture = serverBootstrap.bind(9000);
                //等待服务器关闭
                channelFuture.channel().closeFuture().sync();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                //关闭线程池
                boss.shutdownGracefully();
                work.shutdownGracefully();
            }
    
        }
    
    }
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.SimpleChannelInboundHandler;
    
    public class ServerHandler extends SimpleChannelInboundHandler<String> {
    
        //接收消息并处理
        protected void messageReceived(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
            System.out.println(s);
            channelHandlerContext.writeAndFlush("hello client");
        }
    }

      2)客户端和处理类

    import io.netty.bootstrap.Bootstrap;
    import io.netty.channel.*;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.nio.NioSocketChannel;
    import io.netty.handler.codec.string.StringDecoder;
    import io.netty.handler.codec.string.StringEncoder;
    
    import java.net.InetSocketAddress;
    import java.util.Scanner;
    
    //单个客户端
    public class Client {
    
        public static void main(String[] args) {
    
            //服务类
            Bootstrap bootstrap = new Bootstrap();
            //线程池
            EventLoopGroup client = new NioEventLoopGroup();
    
            try {
                //设置线程组
                bootstrap.group(client);
                //设置socket工厂
                bootstrap.channel(NioSocketChannel.class);
                //设置管道处理
                bootstrap.handler(new ChannelInitializer<Channel>() {
                    protected void initChannel(Channel channel) throws Exception {
                        channel.pipeline().addLast(new StringEncoder());
                        channel.pipeline().addLast(new StringDecoder());
                        channel.pipeline().addLast(new ClientHandler());
                    }
                });
                //连接服务器
                ChannelFuture channelFuture = bootstrap.connect(new InetSocketAddress("localhost", 9000));
                //输入信息测试
                Scanner scanner = new Scanner(System.in);
                while (true) {
                    String msg = scanner.nextLine();
                    channelFuture.channel().writeAndFlush(msg);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                //关闭线程池
                client.shutdownGracefully();
            }
        }
    }
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.SimpleChannelInboundHandler;
    
    public class ClientHandler extends SimpleChannelInboundHandler<String>{
    
        //接收消息
        protected void messageReceived(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
            System.out.println(s);
        }
    }

      3)多个客户端

    import io.netty.bootstrap.Bootstrap;
    import io.netty.channel.Channel;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.EventLoopGroup;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.nio.NioSocketChannel;
    import io.netty.handler.codec.string.StringDecoder;
    import io.netty.handler.codec.string.StringEncoder;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Scanner;
    import java.util.concurrent.atomic.AtomicInteger;
    
    //多个客户端连接
    public class MClient {
    
        //服务类
        private Bootstrap bootstrap = new Bootstrap();
        //因为线程组里面是线程安全的,所以这里采用数组的形式
        private List<Channel> channels = new ArrayList<Channel>();
        //原子类,用于计数
        private AtomicInteger atomicInteger = new AtomicInteger();
        //初始化操作
        public void init(int count) {
            //声明线程池
            EventLoopGroup client = new NioEventLoopGroup();
            //设置线程组
            bootstrap.group(client);
            //设置socket工厂
            bootstrap.channel(NioSocketChannel.class);
            //设置管道
            bootstrap.handler(new ChannelInitializer<Channel>() {
                protected void initChannel(Channel channel) throws Exception {
                    channel.pipeline().addLast(new StringEncoder());
                    channel.pipeline().addLast(new StringDecoder());
                    channel.pipeline().addLast(new ClientHandler());
                }
            });
            //初始化连接
            for (int i = 0; i < count; i++) {
                ChannelFuture channelFuture = bootstrap.connect("10.0.20.154", 9000);
                channels.add(channelFuture.channel());
            }
        }
    
        //获取下一个channel
        public Channel nextChannel() {
            return this.getChannel(0);
        }
    
        private Channel getChannel(int n) {
            //平均获取其中的channel
            Channel channel = channels.get(Math.abs(atomicInteger.getAndIncrement() % channels.size()));
            //判断是否连接
            if (!channel.isActive()) {
                //如果没有连接,选择激活替换
                channels.set(channels.indexOf(channel), bootstrap.connect("10.0.20.154", 9000).channel());
                //超出总长度,直接抛异常
                if (n>=channels.size()) {
                    throw new RuntimeException("no use channel");
                }
                //递归,目的是获取下一个,直到数组没有channel
                return getChannel(n+1);
            }
            return channel;
        }
    
        public static void main(String[] args) {
            MClient mClient = new MClient();
            mClient.init(5);
            Scanner scanner = new Scanner(System.in);
            while (true) {
                try {
                    String msg = scanner.nextLine();
                    mClient.nextChannel().writeAndFlush(msg);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
  • 相关阅读:
    Kubernetes中Pod之间使用虚拟二层网络连接
    ResourceQuota和LimitRange实践指南
    Namespace集群环境的共享与隔离
    K8s生产架构
    K8s常用命令整理+名词解析
    K8s中的pv&&pvc
    Kubernetes保证集群内节点和网络安全
    Kubernetes中网络相关知识
    kubectl常用命令
    物理ceph集群+K8s
  • 原文地址:https://www.cnblogs.com/ll409546297/p/8031551.html
Copyright © 2020-2023  润新知