系列连载 | 带你玩转Netty之原理篇

1,087 阅读5分钟
原文链接: mp.weixin.qq.com

作者:大招

本文为原创文章,转载请注明作者及出

背景

为了支持 CCtalk 在 Web 端观看视频直播,以及实时聊天等功能,传统的短连接无法实现实时推送的目的,需要建立长连接,而 Web 端用户建立连接的成本很低,长连接的资源消耗较大,这里就需要框架对大并发有足够的支持。满足大并发,又支持长连接, Netty + Websocket 是一个不错的解决方案,所以接下来会通过三篇文章,详细介绍一下这块内容。

  1. 原理篇

  2. 应用篇

  3. WebSocket

导语

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

Netty简介

上面的导语是 Netty 官方的一个介绍,Netty 其实就是基于事件驱动,快速开发高并发,高可靠性的服务和客户端的一个开源框架。主要特性有以下几点。

设计

  • 统一的 API,适用于不同的协议(阻塞和非阻塞)

  • 基于灵活、可扩展的事件驱动模型

  • 高度可定制的线程模型

  • 可靠的无连接数据 Socket 支持(UDP)

性能

  • 更好的吞吐量,低延迟

  • 更省资源

  • 尽量减少不必要的内存拷贝

安全

  • 完整的 SSL/TLS 和 STARTTLS 的支持

易用

  • 完善的 Java doc,用户指南和样例,社区很活跃

  • 简洁简单

  • 仅依赖于 JDK1.5

Netty框架

核心部分

  • 零拷贝

    “Zero-copy” describes computer operations in which the CPU does not perform the task of copying data from one memory area to another.

    上面是 wiki 的一描述,它通常是指计算机在网络上发送文件时,不需要将文件内容拷贝到用户空间(User Space)而直接在内核空间(Kernel Space)中传输到网络的方式,所谓的两个空间只是说明两个不一样的内存区域,通过减少不同内存区域的拷贝,减低 CPU 的消耗。下面有两张对比图:

    1、非零拷贝模式

       2、零拷贝模式

Netty 的零拷贝与实际定义还是有点出入,Java 是基于虚拟机的,其实都是用户空间,Netty 中的零拷贝,更多的是一种数据的优化操作,比如多包合并处理上,Netty是将各个包的地址记录下来,在逻辑上合成一个整体,实际存储还是独立的,这样减少内存拷贝,降低 CPU 消耗。

  • 统一通讯 API

    Netty 提供了命名为 channel 统一异步 IO 接口,主要是为了解决 Java OIO , NIO API 不兼容,简化业务层的工作。

  • 可扩展的事件模型

    Netty has a well-defined event model focused on I/O. It also allows you to implement your own event type without breaking the existing code because each event type is distinguished from another by a strict type hierarchy. This is another differentiator against other frameworks.

Netty 是通过 ChannelEvent 作为事件流载体,通过 ChannelHander 来做事件逻辑处理,channelHander 又可分为 INBoundHandler 和 OUTBoundHandler 分别处理流入数据和流出数据,Event 则通过 sendDownStream 和 sendUpStream 在每个 handler 中流转,ChannelPipeline 作为 handler 的容器,用户自定义的 handler 只要添加到   pipe,就可以截取数据流做业务处理。

所有的数据流转,和执行过程都是在一个线程(EventLoop)上执行的,这种串行化的处理方式,让 Netty 无锁化,无需处理数据竞争问题,提高了执行效率。

高级组件

  • 编解码框架

    Netty 提供里提供一套 EncodeHandler 和 DecodeHandler,将业务逻辑从编解码分离,大家可以了解一下,有时间会对这块仔细说明

  • SSL/TLS Support

    在 Netty 中使用 SSL 也是很方便的,使用 SSLEngine,SslHandler 就可以

  • Http/WebSocket 支持

Netty线程模型

Netty 支持三种线程模型,分别是单线程模型,多线程模型,以及主从线程模型,重点会介绍主从多线程模型

  • 单线程模型

从 accpet 连接到分发到 handler 处理业务,都在单线程中完成,模型简单,适合简单场景,不适合大并发场景。

  • 多线程模型

  1. Acceptor 单独线程接受 accpet 连接请求,创建 Channel,并移交给 IO 线程池

  2. IO 线程池分配线程读取 Channel 数据,并分发 handler 处理相关业务

  3. Accpetor 是单线程,如果期间执行鉴权,登陆等操作,出现拥堵,会有单点问题。

  • 主从多线程模型

  1. Acceptor 线程池(NioEventLoopGroup)分配一个 NioEventLoop 接受连接请求,并创建 Channel,并将 Channel 移交给 IO 线程池

  2. I/O 线程池((NioEventLoopGroup)分配一个 NioEventLoop 处理 Channel 数据,从 Channel 读数据,并将数据交给 handler 处理,handler 处理完后,向 Channel 写数据

  3. I/O 线程池还可以处理定时任务,和系统任务。

这边多次提到 NioEventLoop,许多个 NioEventLoop 构成 NioEventLoopGroup,其主要的职能如下:

  1. 作为 Acceptor 线程,负责处理客户端的请求接入

  2. 作为 Connecor 线程,负责注册监听连接操作位,用于判断异步连接结果

  3. 作为 IO 线程,监听网络读操作位,负责从 SocketChannel 中读取写报文

这张图很好的说明 NioEventLoop 的作用,以及 Netty 串行化的处理链。

  • 一个客户端 Channel 只能由一个 NioEventLoop 处理,避免并发资源竞争问题。

  • NioEventLoop 的事件是分发给 Netty 的事件模型,这个模型可以参考上面的说明。

总结

本文主要介绍了 Netty 的原理,重点对 Netty 的串行化处理链,线程模型,数据模型做了说明,绕过了细节,希望对大家在大层面去理解 Netty 有帮助,更多的实践,以及细节,留在后续文章再说。

资料

  1. Netty 零拷贝知识(http://www.cnblogs.com/xys1228/p/6088805.html)

  2. Netty user guide(http://netty.io/3.5/guide/)

  3. Netty 教程 (https://www.gitbook.com/book/waylau/essential-netty-in-action/details)

推荐系统那些事儿

一个关于 nolock 的故事

交易系统 - 领域驱动设计浅析

基于 Electron 的爬虫框架 Nightmare

翻译 | Android O 中的 seccomp 过滤器