Netty 简介

da6076f445844910a61943e436f0c9c6.jpg一、Netty 简介

 

Netty 是基于 Java NIO 的异步事件驱动的网络应用框架,使用 Netty 可以快速开发网络应用,Netty 提供了高层次的抽象来简化 TCP 和 UDP 服务器的编程,但是你仍然可以使用底层的 API。

 

Netty 的内部实现是很复杂的,但是 Netty 提供了简单易用的API从网络处理代码中解耦业务逻辑。Netty 是完全基于 NIO 实现的,所以整个 Netty 都是异步的。

 

Netty 是最流行的 NIO 框架,它已经得到成百上千的商业、商用项目验证,许多框架和开源组件的底层 rpc 都是使用的 Netty,如 Dubbo、Elasticsearch 等等。下面是官网给出的一些 Netty 的特性:

 

设计方面

 

对各种传输协议提供统一的 API(使用阻塞和非阻塞套接字时候使用的是同一个 API,只是需要设置的参数不一样)。

基于一个灵活、可扩展的事件模型来实现关注点清晰分离。

高度可定制的线程模型——单线程、一个或多个线程池。

真正的无数据报套接字(UDP)的支持(since 3.1)。

易用性

 

完善的 Javadoc 文档和示例代码。

不需要额外的依赖,JDK 5 (Netty 3.x) 或者 JDK 6 (Netty 4.x) 已经足够。

性能

 

更好的吞吐量,更低的等待延迟。

更少的资源消耗。

最小化不必要的内存拷贝。

安全性

 

完整的 SSL/TLS 和 StartTLS 支持

对于初学者,上面的特性我们在脑中有个简单了解和印象即可, 下面开始我们的实战部分。

 

二、一个简单 Http 服务器

开始前说明下我这里使用的开发环境是 IDEA+Gradle+Netty4,当然你使用 Eclipse 和 Maven 都是可以的,然后在 Gradle 的 build 文件中添加依赖 compile 'io.netty:netty-all:4.1.26.Final',这样就可以编写我们的 Netty 程序了,正如在前面介绍 Netty 特性中提到的,Netty 不需要额外的依赖。

 

第一个示例我们使用 Netty 编写一个 Http 服务器的程序,启动服务我们在浏览器输入网址来访问我们的服务,便会得到服务端的响应。功能很简单,下面我们看看具体怎么做?

 

首先编写服务启动类

 

复制代码

public class HttpServer {

    public static void main(String[] args) {

        //构造两个线程组

        EventLoopGroup bossrGroup = new NioEventLoopGroup();

        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {

            //服务端启动辅助类

            ServerBootstrap bootstrap = new ServerBootstrap();

 

            bootstrap.group(bossGroup, workerGroup)

            .channel(NioServerSocketChannel.class)

            .childHandler(new HttpServerInitializer());

 

            ChannelFuture future = bootstrap.bind(8080).sync();

            //等待服务端口关闭

            future.channel().closeFuture().sync();

        } catch (InterruptedException e) {

            e.printStackTrace();

        }finally {

            // 优雅退出,释放线程池资源

            bossGroup.shutdownGracefully();

            workerGroup.shutdownGracefully();

        }

    }

}

复制代码

 

 

在编写 Netty 程序时,一开始都会生成 NioEventLoopGroup 的两个实例,分别是 bossGroup 和 workerGroup,也可以称为 parentGroup 和 childGroup,为什么创建这两个实例,作用是什么?可以这么理解,bossGroup 和 workerGroup 是两个线程池, 它们默认线程数为 CPU 核心数乘以 2,bossGroup 用于接收客户端传过来的请求,接收到请求后将后续操作交由 workerGroup 处理。

 

在这里我向大家推荐一个架构学习交流群。交流学习群号:747981058 里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。

 

接下来我们生成了一个服务启动辅助类的实例 bootstrap,boostrap 用来为 Netty 程序的启动组装配置一些必须要组件,例如上面的创建的两个线程组。channel 方法用于指定服务器端监听套接字通道 NioServerSocketChannel,其内部管理了一个 Java NIO 中的ServerSocketChannel实例。

 

channelHandler 方法用于设置业务职责链,责任链是我们下面要编写的,责任链具体是什么,它其实就是由一个个的 ChannelHandler 串联而成,形成的链式结构。正是这一个个的 ChannelHandler 帮我们完成了要处理的事情。

 

接着我们调用了 bootstrap 的 bind 方法将服务绑定到 8080 端口上,bind 方法内部会执行端口绑定等一系列操,使得前面的配置都各就各位各司其职,sync 方法用于阻塞当前 Thread,一直到端口绑定操作完成。接下来一句是应用程序将会阻塞等待直到服务器的 Channel 关闭。

 

启动类的编写大体就是这样了,下面要编写的就是上面提到的责任链了。如何构建一个链,在 Netty 中很简单,不需要我们做太多,代码如下:

 

复制代码

public class HttpServerInitializer extends ChannelInitializer<SocketChannel> {

    protected void initChannel(SocketChannel sc) throws Exception {

        ChannelPipeline pipeline = sc.pipeline();

        //处理http消息的编解码

        pipeline.addLast("httpServerCodec", new HttpServerCodec());

        //添加自定义的ChannelHandler

        pipeline.addLast("httpServerHandler", new HttpServerHandler());

    }

}

复制代码

 

 

我们自定义一个类 HttpServerInitializer 继承 ChannelInitializer 并实现其中的 initChannel方法。

 

ChannelInitializer 继承 ChannelInboundHandlerAdapter,用于初始化 Channel 的 ChannelPipeline。通过 initChannel 方法参数 sc 得到 ChannelPipeline 的一个实例。

 

当一个新的连接被接受时, 一个新的 Channel 将被创建,同时它会被自动地分配到它专属的 ChannelPipeline。

 

ChannelPipeline 提供了 ChannelHandler 链的容器,推荐读者仔细自己看看 ChannelPipeline 的 Javadoc,文章后面也会继续说明 ChannelPipeline 的内容。

 

Netty 是一个高性能网络通信框架,同时它也是比较底层的框架,想要 Netty 支持 Http(超文本传输协议),必须要给它提供相应的编解码器。

 

所以我们这里使用 Netty 自带的 Http 编解码组件 HttpServerCodec 对通信数据进行编解码,HttpServerCodec 是 HttpRequestDecoder 和 HttpResponseEncoder 的组合,因为在处理 Http 请求时这两个类是经常使用的,所以 Netty 直接将他们合并在一起更加方便使用。所以对于上面的代码:

 

pipeline.addLast("httpServerCodec", new HttpServerCodec())

 

 

我们替换成如下两行也是可以的。

 

pipeline.addLast("httpResponseEndcoder", new HttpResponseEncoder());

pipeline.addLast("HttpRequestDecoder", new HttpRequestDecoder());

 

 

通过 addLast 方法将一个一个的 ChannelHandler 添加到责任链上并给它们取个名称(不取也可以,Netty 会给它个默认名称),这样就形成了链式结构。在请求进来或者响应出去时都会经过链上这些 ChannelHandler 的处理。

 

最后再向链上加入我们自定义的 ChannelHandler 组件,处理自定义的业务逻辑。下面就是我们自定义的 ChannelHandler。

 

复制代码

public class HttpServerChannelHandler0 extends SimpleChannelInboundHandler<HttpObject> {

    private HttpRequest request;

 

    @Override

    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {

        if (msg instanceof HttpRequest) {

            request = (HttpRequest) msg;

            request.method();

            String uri = request.uri();

            System.out.println("Uri:" + uri);

        }

        if (msg instanceof HttpContent) {

 

            HttpContent content = (HttpContent) msg;

            ByteBuf buf = content.content();

            System.out.println(buf.toString(io.netty.util.CharsetUtil.UTF_8));

 

            ByteBuf byteBuf = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);

            FullHttpResponse response = new Defaul

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值