初识Netty:背景、现状与趋势

Netty概述

Netty由Trustin Lee (韩国,Line公司) 2004年开发。

Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients.

本质:网络应用程序框架
实现:异步、事件驱动
特性:高性能、可维护、快速开发
用途:开发服务器和客户端

Netty的结构图
初识Netty:背景、现状与趋势
其结构主要分三个部分:

  • 最底层的核心层:零拷贝的功能丰富的Byte Buffer、通用的通信层API、可扩展的事件模型
  • 支持的传输层服务:TCP-Socket&UDP-Datagram、HTTP Tunnel、In-VM Pipe
  • 支持的协议:HTTP(应用层协议)、WebSocket、Protobuf( 编解码方式)等

Netty之Hello World

Netty的Hello world代码相对来说要复杂一些,但客户端和服务端都包含了NioEventLoopGroup,Pipeline等相同的脚手架代码。

在pom中引入netty的依赖

<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.50.Final</version>
</dependency>

创建Netty的Hello World。下面的示例程序使用netty的构建了一个基本的客户端-服务端的模型,由NioEventLoopGroup、ServerBootstrap、ChannelInitializer等组件构成。

服务端代码:

/**
构建netty Server端启动的bootstrap.
*/
public class EchoServer {
    public static void main(String[] args) throws InterruptedException {
        // 接收客户端连接
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        // 处理客户端连接
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
            .handler(new LoggingHandler(LogLevel.INFO))
            .childHandler(new EchoServerInitializer());

            ChannelFuture channelFuture = serverBootstrap.bind(8030).sync();
            channelFuture.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully(); 
            workerGroup.shutdownGracefully();
        }
    }
}
/**
初始化server端的socket channel pipiline。
*/
public class EchoServerInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        ChannelPipeline pipeline = socketChannel.pipeline();
        pipeline.addLast(new LoggingHandler(LogLevel.INFO));
        pipeline.addLast(new EchoServerHandler());
    }
}
/**
Server handler
*/
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        // 接收到客户端消息后,将消息原样输出给客户端
        ctx.write(msg);
    }
    
    @Override
        public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }
    
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

客户端代码

public class EchoClient {
    public static void main(String[] args) throws InterruptedException {
        // configure the client.
        EventLoopGroup group = new NioEventLoopGroup();
        
        try{
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
            .channel(NioSocketChannel.class)
            .option(ChannelOption.TCP_NODELAY,true)
            .handler(new EchoClientInitializer());
            
            // start the client.
            ChannelFuture channelFuture = bootstrap.connect("127.0.0.1",8030).sync();
            
            // wait util the connection is closed.
            channelFuture.channel().closeFuture().sync();
        }
        finally {
            // shut down the event loop to terminate all threads.
            group.shutdownGracefully();
        }
    }
}
public class EchoClientInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        ChannelPipeline pipeline = socketChannel.pipeline();
        pipeline.addLast(new LoggingHandler(LogLevel.INFO));
        pipeline.addLast(new EchoClientHandler());
    }
}
public class EchoClientHandler extends ChannelInboundHandlerAdapter {
    private final ByteBuf firstMessage;
    
    public EchoClientHandler() {
        firstMessage = Unpooled.wrappedBuffer("I am echo message".getBytes());
    }
    
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(firstMessage);
    }
    
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ctx.write(msg);
    }
    
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        TimeUnit.SECONDS.sleep(3);
        ctx.flush();
    }
    
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

不直接使用JDK NIO

Netty相比于Java Nio

做的更多:

  • 支持常用应用层协议(如http),无需再过多考虑编解码的实现;
  • 解决TCP层传输问题:粘包、半包现象
  • 支持流量整形(流量控制,黑白名单等)
  • 完善的断连、Idle等异常处理

做的更好:

  • 规避JDK NIO bug
  • epoll bug:异常唤醒空转导致CPU 100%
  • IP_TOS参数(IP包的优先级和QoS选项)使用时抛出异常
  • API更友好更强大
  • JDK的NIO一些API不够友好,功能薄弱。如ByteBuffer -> Netty‘s ByteBuf
  • 除NIO外,也提供了其它一些增强:ThreadLocal -> Netty‘s FastThreadLocal
  • 隔离变化、屏蔽细节
  • 隔离JDK NIO的实现变化:nio -> nio2(aio) -> ...
  • 屏蔽JDK NIO的实现细节

选择Netty的原因

业界流行的网络通信框架,但是Java网络编程,只选择Netty。

  • Apache Mina
    netty对MINA进行重构,并解决了已知问题
  • Sun Grizzly
    用的少、文档少、更新少
  • Apple Swift NIO、ACE
    其它语言,不考虑
  • Cindy
    生命周期不长
  • Tomcat、Jetty
    还没有独立出来

Netty发展史

从归属组织上看发展

  • JBoss
    在4.0之前属于JBoss,在包命名管理上可以看出
  • Netty
    4.0之后在Netty社区进行管理

从版本演变上看发展

  • 2004年6月Netty2发布
    声称Java社区中第一个基于事件驱动的易用网络框架
  • 2008年10月Netty3发布
  • 2013年7月Netty4发布
  • 2013年12月发布5.0.0.Alpha1
  • 2015年1月废弃5.0.0
    复杂(使用ForkJoinPool)
    没有证明有明显性能优势
    维护不过来

Netty社区
https://github.com/netty/netty

分支

  • 4.1 master,支持Android(最优版本选择)
  • 4.0 相较于之前版本主要变更有:线程模型优化、包结构、命名(更新缓慢)

一些典型项目

  • 数据库:Cassandra
  • 大数据处理:Spark、Hadoop
  • 消息队列:RocketMQ
  • 检索:Elasticsearch
  • 框架:gRPC、Apache Dubbo、Spring5
  • 分布式协调器:Zookeeper
  • 工具类:async-http-client
  • 其它参考:https://netty.io/wiki/adopters.html

趋势

  • 更多流行协议的支持
    目前已经支持dns、http、https、redis、xml等主流协议
  • 紧跟JDK新功能步伐
  • 更多易用、人性化功能
  • IP地址黑白名单、流量整形等
  • 使用Netty的应用越来越多

初识Netty:背景、现状与趋势

上一篇:servlet和jsp区别


下一篇:【JS】【LeetCode】7. 整数反转