netty学习1:背景和概述

背景和概述

Posted by Chris on October 20, 2019

1 Netty出现的背景

java中网络编程,传统的一般都是利用java的网络io和网络nio有关的API。这些原始的API使用起来有如下的缺点:

  • 使用起来很复杂,不方便用户使用
    如下图所示,使用java.net包下的网络API来构建socket网络编程的过程。 该代码片段反映了java原始的网络API的问题有:
    1. 图中标注(1)的地方,重复写了很多的样板代码,写起来很繁琐
    2. 图中标注(2)的地方,对于每个客户端socket连接,都会创建一个新的线程来处理。每个线程的都需要分配内存,默认值为64KB到1MB。
    3. 当并发量很高时,且标注(3)的地方处理时间较长时,则会有大量的线程处于阻塞状态,只是等待输入或者输出数据;此外,创建大量线程,上下文切换的开销会显得很大。
  • 不方便扩展
    如果之前用户使用的是BIO,如上面的代码片段所示,此时要切换到NIO,则必须重新根据NIO API,再写一份完全不一样的代码。下面是一个使用NIO API的例子。 可以看出,java BIO API和java NIO API使用方式完全不一样,想要从BIO API切换到NIO API不是一件容易的事情。而且,NIO API也存在大量的样板代码,写起来很繁琐。

  • 直接使用java提供的阻塞网络API,在高并发下性能存在问题;使用java提供的nio API,使用繁琐且容易出错
    这一点在上面说过java BIO API在高并发下存在性能问题;NIO API写起来繁琐且易出错,比如ByteBuffer的flip()操作很不友好,导致用户很容易写错。

2 Netty要做的事情

  • 方便用户使用的框架,有简单的API
  • 扩展性更好的框架,方便用户在阻塞IO和非阻塞IO之间切换
  • 规避java BIO API的性能问题

3 Netty是怎么做的

统一的API

  • 封装样板代码,提供统一的API
  • 抽象出组件,组件可以复用

详实的文档和示例

User guide for 4.x

使用Reactor模式提高并发性能

下图是一个典型的多线程的Reactor模式的组件图。 图中有三个关键的组件:mainReactor,subReactor和Thread Pool线程池。我们知道,服务端接受网络请求有几个步骤:接受连接,等待数据传输,处理请求,返回响应数据。这三个组件分别处理不同的阶段。mainReactor负责接收客户端的连接并将其传递给subReactor。subReactor负责处理与客户端的读写请求。具体的业务处理逻辑,由Thread Pool线程池分配出不同的线程来进行处理。

优化内存管理

  • 池化内存
    一个伟大的中间件,看起来必然有一个可以由自己管理内存的内存管理器,例如kafka还有这里的netty。Netty内存池的实现参考了jemalloc的原理。
  • 零拷贝技术
    零拷贝技术是一个可以直接将内核缓冲区A的数据复制到内核缓冲区B的技术,不需要经过应用缓冲区做中转操作。后面的文章会介绍。

4 Netty最终解决了哪些问题

  • 样板代码太多而不方便使用的问题
  • 原始的API不方便扩展的问题
  • 阻塞IO API性能太低的问题;非阻塞IO API不方便使用且容易出错的问题

上面说的那些,都将在后面的文章中具体讲到。

参考