使用Netty框架的原因和入门(八)

今天我们分析使用Netty框架的原因和入门
一、为什么要用 Netty
1 、虽然 JAVA NIO 框架提供了 多路复用 IO 的支持,但是并没有提供上层“信息格式” 的良好封装。例如前两者并没有提供针对 Protocol Buffer JSON 这些信息格式的封装,但是Netty 框架提供了这些数据格式封装(基于责任链模式的编码和解码功能);
2 NIO 的类库和 API 相当复杂,使用它来开发,需要非常熟练地掌握 Selector 、ByteBuffer、 ServerSocketChannel、 SocketChannel 等,需要很多额外的编程技能来辅助使用 NIO, 例如,因 为 NIO 涉及了 Reactor 线程模型,所以必须必须对多线程和网络编程非常熟悉才能写出高质量的 NIO 程序
3 、要编写一个可靠的、易维护的、高性能的 NIO 服务器应用。除了框架本身要兼容实 现各类操作系统的实现外。更重要的是它应该还要处理很多上层特有服务,例如:客户端的 权限、还有上面提到的信息格式封装、简单的数据读取,断连重连,半包读写,心跳等等,这些 Netty 框架都提供了响应的支持。
4 JAVA NIO 框架存在一个 poll/epoll bug Selector doesn t block on Selector.select(timeout),不能 block 意味着 CPU 的使用率会变成 100% (这是底层 JNI 的问题,
上层要处理这个异常实际上也好办)。当然这个 bug 只有在 Linux 内核上才能重现。 这个问题在 JDK 1.7 版本中还没有被完全解决,但是 Netty 已经将这个 bug 进行了处理。 这个 Bug 与操作系统机制有关系的,JDK 虽然仅仅是一个兼容各个操作系统平台的软件, 但在 JDK5 JDK6 最初的版本中(严格意义上来将, JDK 部分版本都是),这个问题并没有 解决,而将这个帽子抛给了操作系统方,这也就是这个 bug 最终一直到 2013 年才最终修复 的原因(JDK7 JDK8 之间 ) 。 Netty的性能很高,按照 Facebook 公司开发小组的测试表明, Netty 最高能达到接近百万的吞吐:
 
二、为什么不用 Netty5
Netty5 已经停止开发了。
三、为什么 Netty 使用 NIO 而不是 AIO
Netty 不看重 Windows 上的使用,在 Linux 系统上, AIO 的底层实现仍使用 EPOLL ,没有 很好实现 AIO ,因此在性能上没有明显的优势,而且被 JDK 封装了一层不容易深度优化。 AIO 还有个缺点是接收数据需要预先分配缓存 , 而不是 NIO 那种需要接收时才需要分配 缓存, 所以对连接数量非常大但流量小的情况 , 内存浪费很多。 据说 Linux AIO 不够成熟,处理回调结果速度跟不上处理需求,有点像外卖员太少, 顾客太多,供不应求,造成处理速度有瓶颈。
作者原话:
Not faster than NIO (epoll) on unix systems (which is true)
There is no daragram suppport
Unnecessary threading model (too much abstraction without usage)
 
四、第一个 Netty 程序
1、EventLoop(Group) Channel
Channel Java NIO 的一个基本构造。 它代表一个到实体(如一个硬件设备、一个文件、一个网络套接字或者一个能够执行一 个或者多个不同的 I/O 操作的程序组件)的开放连接,如读操作和写操作 目前,可以把 Channel 看作是传入(入站)或者传出(出站)数据的载体。因此,它 可以被打开或者被关闭,连接或者断开连接。 EventLoop 暂时可以看成一个线程、 EventLoopGroup 自然就可以看成线程组。
2、事件和 ChannelHandler ChannelPipeline
Netty 使用不同的事件来通知我们状态的改变或者是操作的状态。这使得我们能够基于 已经发生的事件来触发适当的动作。 Netty 事件是按照它们与入站或出站数据流的相关性进行分类的。 可能由入站数据或者相关的状态更改而触发的事件包括: 连接已被激活或者连接失活;数据读取;用户事件;错误事件。 出站事件是未来将会触发的某个动作的操作结果,这些动作包括: 打开或者关闭到远程节点的连接;将数据写到或者冲刷到套接字。 每个事件都可以被分发给 ChannelHandler 类中的某个用户实现的方法。
Netty 提供了大量预定义的可以开箱即用的 ChannelHandler 实现,包括用于各种协议 (如 HTTP SSL/TLS )的 ChannelHandler
3、ChannelFuture
Netty 中所有的 I/O 操作都是异步的。 JDK 预置了 interface java.util.concurrent.Future Future 提供了一种在操作完成时通知 应用程序的方式。这个对象可以看作是一个异步操作的结果的占位符;它将在未来的某个时刻完成,并提供对其结果的访问。但是其所提供的实现,只允许手动检查对应的操作是否已经完成,或者一直阻塞直到它完成。这是非常繁琐的,所以 Netty 提供了它自己的实现—— ChannelFuture,用于在执行异步操作的时候使用。
每个 Netty 的出站 I/O 操作都将返回一个 ChannelFuture
4、引用Netty相关jar包
 <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.28.Final</version>
        </dependency>

5、Netty 服务端代码:

public class EchoServer  {

    private final int port;

    public EchoServer(int port) {
        this.port = port;
    }

    public static void main(String[] args) throws InterruptedException {
        int port = 9999;
        EchoServer echoServer = new EchoServer(port);
        System.out.println("服务器即将启动");
        echoServer.start();
        System.out.println("服务器关闭");
    }

    public void start() throws InterruptedException {
        final EchoServerHandler serverHandler = new EchoServerHandler();
        /**线程组*/
        EventLoopGroup group  = new NioEventLoopGroup();
        try {
            /**服务端启动必备*/
            ServerBootstrap b = new ServerBootstrap();
            b.group(group)
                    .channel(NioServerSocketChannel.class)/**指定使用NIO的通信模式*/
            .localAddress(new InetSocketAddress(port))/**指定监听端口*/
            .childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast(serverHandler);
                }
            });
            ChannelFuture f = b.bind().sync();/**异步绑定到服务器,sync()会阻塞到完成*/
            f.channel().closeFuture().sync();/**阻塞当前线程,直到服务器的ServerChannel被关闭*/
        } finally {
            group.shutdownGracefully().sync();
        }
    }
}

服务端事件类处理:继承 ChannelInboundHandlerAdapter 

@ChannelHandler.Sharable
public class EchoServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf in = (ByteBuf)msg;
        System.out.println("Server accept: "+in.toString(CharsetUtil.UTF_8));
//
        ctx.writeAndFlush(in);
        //ctx.close();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

6、Netty 客户端代码:

ublic class EchoClient {

    private final int port;
    private final String host;

    public EchoClient(int port, String host) {
        this.port = port;
        this.host = host;
    }
    public void start() throws InterruptedException {
        /**线程组*/
        EventLoopGroup group  = new NioEventLoopGroup();
        try {
            /**客户端启动必备*/
            Bootstrap b = new Bootstrap();
            b.group(group)
                    .channel(NioSocketChannel.class)/**指定使用NIO的通信模式*/

                    .remoteAddress(new InetSocketAddress(host,port))/**指定服务器的IP地址和端口*/
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new EchoClientHandler());

                        }
                    });
            ChannelFuture f = b.connect().sync();/**异步连接到服务器,sync()会阻塞到完成*/
            f.channel().closeFuture().sync();/**阻塞当前线程,直到客户端的Channel被关闭*/
        } finally {
            group.shutdownGracefully().sync();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        new EchoClient(9999,"127.0.0.1").start();
    }
}

客户端事件处理:继承 SimpleChannelInboundHandler<ByteBuf>

public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {

    /**读取到网络数据后进行业务处理*/
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
        System.out.println("client Accept"+msg.toString(CharsetUtil.UTF_8));
        //ctx.close();
        ctx.close();
    }

    /**channel活跃后,做业务处理*/
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(Unpooled.copiedBuffer(
                "Hello,Netty",CharsetUtil.UTF_8));
    }
}

7、Netty 程序执行结果:客户端执行结果

服务端执行结果:

到此,Netty 的入门使用分析完成,下篇我们分析Netty 各组件的原理,敬请期待!
 
 
 
 
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寅灯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值