请选择 进入手机版 | 继续访问电脑版

默认
打赏 发表评论 0
写给初学者:Java高性能NIO框架Netty的学习方法和进阶策略

本文由李林锋分享于InfoQ公众号,感谢作者。


1、引言


1.1Netty 框架的特点


Netty 的一个特点就是入门相对比较容易,但是真正掌握并精通是非常困难的。

原因有如下几个:

  • 1)涉及的知识面比较广:Netty 作为一个高性能的 NIO 通信框架,涉及到的知识点包括网络通信、多线程编程、序列化和反序列化、异步和同步编程模型、SSL/TLS 安全、内存池、HTTP、MQTT 等各种协议栈,这些知识点在 Java 语言中本身就是难点和重点,如果对这些基础知识掌握不扎实,是很难真正掌握好 Netty 的;
  • 2)调试比较困难:因为大量使用异步编程接口,以及消息处理过程中的各种线程切换,相比于传统同步代码,调试难度比较大;
  • 3)类继承层次比较深:有些代码很晦涩(例如内存池、Reactor 线程模型等),对于初学者而言,通过阅读代码来掌握 Netty 难度还是比较大的;
  • 4)代码规模庞大:目前,Netty 的代码规模已经非常庞大,特别是协议栈部分,提供了对 HTTP/2、MQTT、WebSocket、SMTP 等多种协议的支持,相关代码非常多。如果学习方式不当,抓不住重点,全量阅读 Netty 源码,既耗时又很难吃透,很容易半途而废;
  • 5)资料比较零散,缺乏实践相关的案例:网上各种 Netty 的资料非常多,但是以理论讲解为主,Netty 在各行业中的应用、问题定位技巧以及案例实践方面的资料很少,缺乏系统性的实践总结,也是 Netty 学习的一大痛点。

1.2初学者常见问题


对于很多初学者,在学习过程中经常会遇到如下几个问题:

  • 1)相关领域知识的储备不足:想了解学习 Netty 需要储备哪些技能,掌握哪些知识点,有什么学习技巧可以更快的掌握 Netty。由于对 Java 多线程编程、Socket 通信、TCP/IP 协议栈等知识掌握不扎实,后续在学习 Netty 的过程中会遇到很多困难;
  • 2)理论学习完,实践遇到难题:学习完理论知识之后,想在实际项目中使用,但是真正跟具体项目结合在一起解决实际问题时,又感觉比较棘手,不知道自己使用的方式是否最优,希望能够多学一些案例实践方面的知识,以便更好的在业务中使用 Netty;
  • 3)出了问题不会定位:在项目中遇到了问题,但是由于对 Netty 底层细节掌握不扎实,无法有效的定位并解决问题,只能靠网上搜索相关案例来参考,问题解决效率比较低,甚至束手无策。

2、关于作者


a.jpg
李林锋:2007年毕业于东北大学,2008年进入华为公司从事高性能通信软件的设计和开发工作,有6年NIO设计和开发经验,精通Netty、Mina等NIO框架。Netty中国社区创始人,《Netty权威指南》作者。联系方式:新浪微博 Nettying 微信:Nettying。

3、相关文章



4、Netty 学习策略


Netty 入门相对简单,但是要在实际项目中用好它,出了问题能够快速定位和解决,却并非易事。只有在入门阶段扎实的学好 Netty,后面使用才能够得心应手。

4.1入门知识准备


【Java NIO 类库】:

需要熟悉和掌握的类库主要包括:

  • 1)缓冲区 Buffer;
  • 2)通道 Channel;
  • 3)多路复用器 Selector。

首先介绍缓冲区(Buffer)的概念,Buffer 是一个对象,它包含一些要写入或者要读出的数据。在 NIO 类库中加入 Buffer 对象,体现了新库与原 I/O 的一个重要区别。在面向流的 I/O 中,可以将数据直接写入或者将数据直接读到 Stream 对象中。在 NIO 库中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的;在写入数据时,写入到缓冲区中。任何时候访问 NIO 中的数据,都是通过缓冲区进行操作。

缓冲区实质上是一个数组。通常它是一个字节数组(ByteBuffer),也可以使用其他种类的数组。但是一个缓冲区不仅仅是一个数组,缓冲区提供了对数据的结构化访问以及维护读写位置(limit)等信息。

最常用的缓冲区是 ByteBuffer,一个 ByteBuffer 提供了一组功能用于操作 byte 数组。

比较常用的就是 get 和 put 系列方法,如下所示:
1.jpg
▲ 图 1 ByteBuffer 常用接口定义

Channel 是一个通道,可以通过它读取和写入数据,它就像自来水管一样,网络数据通过 Channel 读取和写入。通道与流的不同之处在于通道是双向的,流只是在一个方向上移动(一个流必须是 InputStream 或者 OutputStream 的子类),而且通道可以用于读、写或者同时用于读写。因为 Channel 是全双工的,所以它可以比流更好地映射底层操作系统的 API。特别是在 UNIX 网络编程模型中,底层操作系统的通道都是全双工的,同时支持读写操作。

比较常用的 Channel 是 SocketChannelServerSocketChannel

其中 SocketChannel 的继承关系如下图所示:
2.jpg
▲ 图 2 SocketChannel 继承关系

Selector 是 Java NIO 编程的基础,熟练地掌握 Selector 对于掌握 NIO 编程至关重要。多路复用器提供选择已经就绪的任务的能力。简单来讲,Selector 会不断地轮询注册在其上的 Channel,如果某个 Channel 上面有新的 TCP 连接接入、读和写事件,这个 Channel 就处于就绪状态,会被 Selector 轮询出来,然后通过 SelectionKey 可以获取就绪 Channel 的集合,进行后续的 I/O 操作。

【 Java 多线程编程】:

作为异步事件驱动、高性能的 NIO 框架,Netty 代码中大量运用了 Java 多线程编程技巧,熟练掌握多线程编程是掌握 Netty 的必备条件。

需要掌握的多线程编程相关知识包括:

  • 1)Java 内存模型;
  • 2)关键字 synchronized;
  • 3)读写锁;
  • 4)volatile 的正确使用;
  • 5)CAS 指令和原子类;
  • 6)JDK 线程池以及各种默认实现。

以关键字 synchronized 为例,它可以保证在同一时刻,只有一个线程可以执行某一个方法或者代码块。同步的作用不仅仅是互斥,它的另一个作用就是共享可变性,当某个线程修改了可变数据并释放锁后,其它的线程可以获取被修改变量的最新值。如果没有正确的同步,这种修改对其它线程是不可见的。

下面我们就通过对 Netty 的源码进行分析,看看 Netty 是如何对并发可变数据进行正确同步的。以 AbstractBootstrap 为例进行分析,首先看它的 option 方法:
2.jpg

这个方法的作用是设置 ServerBootstrap 或 Bootstrap 的 Socket 属性,它的属性集定义如下:
private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();

由于是非线程安全的 LinkedHashMap, 所以如果多线程创建、访问和修改 LinkedHashMap 时,必须在外部进行必要的同步。由于 ServerBootstrap 和 Bootstrap 被调用方线程创建和使用,无法保证它的方法和成员变量不被并发访问。因此,作为成员变量的 options 必须进行正确的同步。由于考虑到锁的范围需要尽可能的小,所以对传参的 option 和 value 的合法性判断不需要加锁,保证锁的范围尽可能的细粒度。

Netty 加锁的地方非常多,大家在阅读代码的时候可以仔细体会下,为什么有的地方要加锁,有的地方有不需要?如果不需要,为什么?当你对锁的原理理解以后,对于这些锁的使用时机和技巧理解起来就相对容易了。

4.2Netty 源码学习


Netty的源代码(在线阅读版),大此链接进入:http://www.52im.net/thread-113-1-1.html

【关键类库学习】:

Netty 的核心类库可以分为 5 大类,需要熟练掌握:

1、ByteBuf 和相关辅助类:ByteBuf 是个 Byte 数组的缓冲区,它的基本功能应该与 JDK 的 ByteBuffer 一致,它提供以下几类基本功能:

  • a. 7 种 Java 基础类型、byte 数组、ByteBuffer(ByteBuf)等的读写;
  • b. 缓冲区自身的 copy 和 slice 等;
  • c. 设置网络字节序;
  • d. 构造缓冲区实例;
  • e. 操作位置指针等方法;
  • f. 动态的扩展和收缩。

从内存分配的角度看,ByteBuf 可以分为两类:堆内存(HeapByteBuf)字节缓冲区:特点是内存的分配和回收速度快,可以被 JVM 自动回收;缺点就是如果进行 Socket 的 I/O 读写,需要额外做一次内存复制,将堆内存对应的缓冲区复制到内核 Channel 中,性能会有一定程度的下降。直接内存(DirectByteBuf)字节缓冲区:非堆内存,它在堆外进行内存分配,相比于堆内存,它的分配和回收速度会慢一些,但是将它写入或者从 Socket Channel 中读取时,由于少了一次内存复制,速度比堆内存快。

2、Channel 和 Unsafe:io.netty.channel.Channel 是 Netty 网络操作抽象类,它聚合了一组功能,包括但不限于网路的读、写,客户端发起连接、主动关闭连接,链路关闭,获取通信双方的网络地址等。它也包含了 Netty 框架相关的一些功能,包括获取该 Chanel 的 EventLoop,获取缓冲分配器 ByteBufAllocator 和 pipeline 等。Unsafe 是个内部接口,聚合在 Channel 中协助进行网络读写相关的操作,它提供的主要功能如下表所示:
4.jpg
▲ Unsafe API 功能列表

3、ChannelPipeline 和 ChannelHandler:Netty 的 ChannelPipelineChannelHandler 机制类似于 Servlet 和 Filter 过滤器,这类拦截器实际上是职责链模式的一种变形,主要是为了方便事件的拦截和用户业务逻辑的定制。Servlet Filter 是 JEE Web 应用程序级的 Java 代码组件,它能够以声明的方式插入到 HTTP 请求响应的处理过程中,用于拦截请求和响应,以便能够查看、提取或以某种方式操作正在客户端和服务器之间交换的数据。

拦截器封装了业务定制逻辑,能够实现对 Web 应用程序的预处理和事后处理。过滤器提供了一种面向对象的模块化机制,用来将公共任务封装到可插入的组件中。

这些组件通过 Web 部署配置文件(web.xml)进行声明,可以方便地添加和删除过滤器,无须改动任何应用程序代码或 JSP 页面,由 Servlet 进行动态调用。通过在请求 / 响应链中使用过滤器,可以对应用程序(而不是以任何方式替代)的 Servlet 或 JSP 页面提供的核心处理进行补充,而不破坏 Servlet 或 JSP 页面的功能。由于是纯 Java 实现,所以 Servlet 过滤器具有跨平台的可重用性,使得它们很容易地被部署到任何符合 Servlet 规范的 JEE 环境中。

Netty 的 Channel 过滤器实现原理与 Servlet Filter 机制一致,它将 Channel 的数据管道抽象为 ChannelPipeline,消息在 ChannelPipeline 中流动和传递。ChannelPipeline 持有 I/O 事件拦截器 ChannelHandler 的链表,由 ChannelHandler 对 I/O 事件进行拦截和处理,可以方便地通过新增和删除 ChannelHandler 来实现不同的业务逻辑定制,不需要对已有的 ChannelHandler 进行修改,能够实现对修改封闭和对扩展的支持。ChannelPipeline 是 ChannelHandler 的容器,它负责 ChannelHandler 的管理和事件拦截与调度:
5.jpg
▲ 图 3 ChannelPipeline 对事件流的拦截和处理流

Netty 中的事件分为 inbound 事件和 outbound 事件。inbound 事件通常由 I/O 线程触发,例如 TCP 链路建立事件、链路关闭事件、读事件、异常通知事件等。

Outbound 事件通常是由用户主动发起的网络 I/O 操作,例如用户发起的连接操作、绑定操作、消息发送等操作。ChannelHandler 类似于 Servlet 的 Filter 过滤器,负责对 I/O 事件或者 I/O 操作进行拦截和处理,它可以选择性地拦截和处理自己感兴趣的事件,也可以透传和终止事件的传递。基于 ChannelHandler 接口,用户可以方便地进行业务逻辑定制,例如打印日志、统一封装异常信息、性能统计和消息编解码等。

4、EventLoop:Netty 的 NioEventLoop 并不是一个纯粹的 I/O 线程,它除了负责 I/O 的读写之外,还兼顾处理以下两类任务:

  • a. 普通 Task:通过调用 NioEventLoop 的 execute(Runnable task) 方法实现,Netty 有很多系统 Task,创建它们的主要原因是:当 I/O 线程和用户线程同时操作网络资源时,为了防止并发操作导致的锁竞争,将用户线程的操作封装成 Task 放入消息队列中,由 I/O 线程负责执行,这样就实现了局部无锁化;
  • b. 定时任务:通过调用 NioEventLoop 的 schedule(Runnable command, long delay, TimeUnit unit) 方法实现。

Netty 的线程模型并不是一成不变的,它实际取决于用户的启动参数配置。通过设置不同的启动参数,Netty 可以同时支持 Reactor 单线程模型、多线程模型和主从 Reactor 多线层模型。

它的工作原理如下所示:
6.jpg
▲ 图 4 Netty 的线程模型

通过调整线程池的线程个数、是否共享线程池等方式,Netty 的 Reactor 线程模型可以在单线程、多线程和主从多线程间切换,这种灵活的配置方式可以最大程度地满足不同用户的个性化定制。

为了尽可能地提升性能,Netty 在很多地方进行了无锁化的设计,例如在 I/O 线程内部进行串行操作,避免多线程竞争导致的性能下降问题。表面上看,串行化设计似乎 CPU 利用率不高,并发程度不够。但是,通过调整 NIO 线程池的线程参数,可以同时启动多个串行化的线程并行运行,这种局部无锁化的串行线程设计相比一个队列—多个工作线程的模型性能更优。

它的设计原理如下图所示:
7.jpg
▲ 图 5 NioEventLoop 串行执行 ChannelHandler

5、Future 和 Promise:在 Netty 中,所有的 I/O 操作都是异步的,这意味着任何 I/O 调用都会立即返回,而不是像传统 BIO 那样同步等待操作完成。异步操作会带来一个问题:调用者如何获取异步操作的结果?ChannelFuture 就是为了解决这个问题而专门设计的。下面我们一起看它的原理。ChannelFuture 有两种状态:uncompleted 和 completed。当开始一个 I/O 操作时,一个新的 ChannelFuture 被创建,此时它处于 uncompleted 状态——非失败、非成功、非取消,因为 I/O 操作此时还没有完成。

一旦 I/O 操作完成,ChannelFuture 将会被设置成 completed,它的结果有如下三种可能:

  • a. 操作成功;
  • b. 操作失败;
  • c. 操作被取消。

ChannelFuture 的状态迁移图如下所示:
8.jpg
▲ 图 6 ChannelFuture 状态迁移图

Promise 是可写的 Future,Future 自身并没有写操作相关的接口,Netty 通过 PromiseFuture 进行扩展,用于设置 I/O 操作的结果,它的接口定义如下:
9.jpg
▲ 图 7 Netty 的 Promise 接口定义

【关键流程学习】:

需要重点掌握 Netty 服务端和客户端的创建,以及创建过程中使用到的核心类库和 API、以及消息的发送和接收、消息的编解码。

Netty 服务端创建流程如下:
10.jpg
▲ 图 8 Netty 服务端创建流程

Netty 客户端创建流程如下:
11.jpg
▲ 图 9 Netty 客户端创建流程

5、Netty 项目实践


实践主要分为两类,如果项目中需要用到 Netty,则直接在项目中应用,通过实践来不断提升对 Netty 的理解和掌握。如果暂时使用不到,则可以通过学习一些开源的 RPC 或者服务框架,看这些框架是怎么集成并使用 Netty 的。以 gRPC Java 版为例,我们一起看下 gRPC 是如何使用 Netty 的。

5.1gRPC 服务端


gRPC 通过对 Netty HTTP/2 的封装,向用户屏蔽底层 RPC 通信的协议细节,Netty HTTP/2 服务端的创建流程如下:
11.jpg
▲ 图 10 Netty HTTP/2 服务端创建流程

服务端 HTTP/2 消息的读写主要通过 gRPC 的 NettyServerHandler 实现,它的类继承关系如下所示:
22.jpg
▲ 图 11 gRPC NettyServerHandler 类继承关系

从类继承关系可以看出,NettyServerHandler 主要负责 HTTP/2 协议消息相关的处理,例如 HTTP/2 请求消息体和消息头的读取、Frame 消息的发送、Stream 状态消息的处理等,相关接口定义如下:
33.jpg
▲ 图 12 NettyServerHandler 处理 HTTP/2 协议消息相关接口

5.2gRPC 客户端


gRPC 的客户端调用主要包括基于 Netty 的 HTTP/2 客户端创建、客户端负载均衡、请求消息的发送和响应接收处理四个流程。

gRPC 的客户端调用总体流程如下图所示:
44.jpg
▲ 图 13 gRPC 客户端总体调用流程

gRPC 的客户端调用流程如下:

  • 1)客户端 Stub(GreeterBlockingStub) 调用 sayHello(request),发起 RPC 调用;
  • 2)通过 DnsNameResolver 进行域名解析,获取服务端的地址信息(列表),随后使用默认的 LoadBalancer 策略,选择一个具体的 gRPC 服务端实例;
  • 3)如果与路由选中的服务端之间没有可用的连接,则创建 NettyClientTransport 和 NettyClientHandler,发起 HTTP/2 连接;
  • 4)对请求消息使用 PB(Protobuf)做序列化,通过 HTTP/2 Stream 发送给 gRPC 服务端;
  • 5)接收到服务端响应之后,使用 PB(Protobuf)做反序列化;
  • 6)回调 GrpcFuture 的 set(Response) 方法,唤醒阻塞的客户端调用线程,获取 RPC 响应。

需要指出的是,客户端同步阻塞 RPC 调用阻塞的是调用方线程(通常是业务线程),底层 Transport 的 I/O 线程(Netty 的 NioEventLoop)仍然是非阻塞的。

5.3线程模型


gRPC 服务端线程模型整体上可以分为两大类:

  • 1)网络通信相关的线程模型,基于 Netty4.1 的线程模型实现;
  • 2)服务接口调用线程模型,基于 JDK 线程池实现。

gRPC 服务端线程模型和交互图如下所示:
55.jpg
▲ 图 14 gRPC 服务端线程模型

其中,HTTP/2 服务端创建、HTTP/2 请求消息的接入和响应发送都由 Netty 负责,gRPC 消息的序列化和反序列化、以及应用服务接口的调用由 gRPC 的 SerializingExecutor 线程池负责。

gRPC 客户端的线程主要分为三类:

  • 1)业务调用线程;
  • 2)客户端连接和 I/O 读写线程;
  • 3)请求消息业务处理和响应回调线程。

gRPC 客户端线程模型工作原理如下图所示(同步阻塞调用为例):
66.jpg
▲ 图 15 客户端调用线程模型

客户端调用主要涉及的线程包括:

  • 1)应用线程,负责调用 gRPC 服务端并获取响应,其中请求消息的序列化由该线程负责;
  • 2)客户端负载均衡以及 Netty Client 创建,由 grpc-default-executor 线程池负责;
  • 3)HTTP/2 客户端链路创建、网络 I/O 数据的读写,由 Netty NioEventLoop 线程负责;
  • 4)响应消息的反序列化由 SerializingExecutor 负责,与服务端不同的是,客户端使用的是 ThreadlessExecutor,并非 JDK 线程池;
  • 5)SerializingExecutor 通过调用 responseFuture 的 set(value),唤醒阻塞的应用线程,完成一次 RPC 调用。

gRPC 采用的是网络 I/O 线程和业务调用线程分离的策略,大部分场景下该策略是最优的。但是,对于那些接口逻辑非常简单,执行时间很短,不需要与外部网元交互、访问数据库和磁盘,也不需要等待其它资源的,则建议接口调用直接在 Netty /O 线程中执行,不需要再投递到后端的服务线程池。避免线程上下文切换,同时也消除了线程并发问题。

例如提供配置项或者接口,系统默认将消息投递到后端服务调度线程,但是也支持短路策略,直接在 Netty 的 NioEventLoop 中执行消息的序列化和反序列化、以及服务接口调用。

减少锁竞争优化:当前 gRPC 的线程切换策略如下:
77.jpg
▲ 图 16 gRPC 线程锁竞争

优化之后的 gRPC 线程切换策略:
88.jpg
▲ 图 17 gRPC 线程锁竞争优化

通过线程绑定技术(例如采用一致性 hash 做映射), 将 Netty 的 I/O 线程与后端的服务调度线程做绑定,1 个 I/O 线程绑定一个或者多个服务调用线程,降低锁竞争,提升性能。

6、Netty 故障定位技巧


尽管 Netty 应用广泛,非常成熟,但是由于对 Netty 底层机制不太了解,用户在实际使用中还是会经常遇到各种问题,大部分问题都是业务使用不当导致的。Netty 使用者需要学习 Netty 的故障定位技巧,以便出了问题能够独立、快速的解决。‘’

6.1接收不到消息


如果业务的 ChannelHandler 接收不到消息,可能的原因如下:

  • 1)业务的解码 ChannelHandler 存在 BUG,导致消息解码失败,没有投递到后端;
  • 2)业务发送的是畸形或者错误码流(例如长度错误),导致业务解码 ChannelHandler 无法正确解码出业务消息;
  • 3)业务 ChannelHandler 执行了一些耗时或者阻塞操作,导致 Netty 的 NioEventLoop 被挂住,无法读取消息;
  • 4)执行业务 ChannelHandler 的线程池队列积压,导致新接收的消息在排队,没有得到及时处理;
  • 5)对方确实没有发送消息。

定位策略如下:

  • 1)在业务的首个 ChannelHandler 的 channelRead 方法中打断点调试,看是否读取到消息;
  • 2)在 ChannelHandler 中添加 LoggingHandler,打印接口日志;
  • 3)查看 NioEventLoop 线程状态,看是否发生了阻塞;
  • 4)通过 tcpdump 抓包看消息是否发送成功。

6.2内存泄漏


通过 jmap -dump:format=b,file=xx pid 命令 Dump 内存堆栈,然后使用 MemoryAnalyzer 工具对内存占用进行分析,查找内存泄漏点,然后结合代码进行分析,定位内存泄漏的具体原因。

示例如下所示:
99.jpg
▲ 图 18 通过 MemoryAnalyzer 工具分析内存堆栈

6.3性能问题


如果出现性能问题,首先需要确认是 Netty 问题还是业务问题,通过 jstack 命令或者 jvisualvm 工具打印线程堆栈,按照线程 CPU 使用率进行排序(top -Hp 命令采集),看线程在忙什么。

通常如果采集几次都发现 Netty 的 NIO 线程堆栈停留在 select 操作上,说明 I/O 比较空闲,性能瓶颈不在 Netty,需要继续分析看是否是后端的业务处理线程存在性能瓶颈。

如下图所示:
1010.jpg
▲ 图 19 Netty NIO 线程运行堆栈

如果发现性能瓶颈在网络 I/O 读写上,可以适当调大 NioEventLoopGroup 中的 work I/O 线程数,直到 I/O 处理性能能够满足业务需求。

附录:更多相关文章


[1] 网络编程基础资料:
TCP/IP详解 - 第11章·UDP:用户数据报协议
TCP/IP详解 - 第17章·TCP:传输控制协议
TCP/IP详解 - 第18章·TCP连接的建立与终止
TCP/IP详解 - 第21章·TCP的超时与重传
技术往事:改变世界的TCP/IP协议(珍贵多图、手机慎点)
通俗易懂-深入理解TCP协议(上):理论基础
通俗易懂-深入理解TCP协议(下):RTT、滑动窗口、拥塞处理
理论经典:TCP协议的3次握手与4次挥手过程详解
理论联系实际:Wireshark抓包分析TCP 3次握手、4次挥手过程
计算机网络通讯协议关系图(中文珍藏版)
UDP中一个包的大小最大能多大?
P2P技术详解(一):NAT详解——详细原理、P2P简介
P2P技术详解(二):P2P中的NAT穿越(打洞)方案详解
P2P技术详解(三):P2P技术之STUN、TURN、ICE详解
通俗易懂:快速理解P2P技术中的NAT穿透原理
高性能网络编程(一):单台服务器并发TCP连接数到底可以有多少
高性能网络编程(二):上一个10年,著名的C10K并发连接问题
高性能网络编程(三):下一个10年,是时候考虑C10M并发问题了
高性能网络编程(四):从C10K到C10M高性能网络应用的理论探索
高性能网络编程(五):一文读懂高性能网络编程中的I/O模型
高性能网络编程(六):一文读懂高性能网络编程中的线程模型
不为人知的网络编程(一):浅析TCP协议中的疑难杂症(上篇)
不为人知的网络编程(二):浅析TCP协议中的疑难杂症(下篇)
不为人知的网络编程(三):关闭TCP连接时为什么会TIME_WAIT、CLOSE_WAIT
不为人知的网络编程(四):深入研究分析TCP的异常关闭
不为人知的网络编程(五):UDP的连接性和负载均衡
不为人知的网络编程(六):深入地理解UDP协议并用好它
不为人知的网络编程(七):如何让不可靠的UDP变的可靠?
网络编程懒人入门(一):快速理解网络通信协议(上篇)
网络编程懒人入门(二):快速理解网络通信协议(下篇)
网络编程懒人入门(三):快速理解TCP协议一篇就够
网络编程懒人入门(四):快速理解TCP和UDP的差异
网络编程懒人入门(五):快速理解为什么说UDP有时比TCP更有优势
网络编程懒人入门(六):史上最通俗的集线器、交换机、路由器功能原理入门
网络编程懒人入门(七):深入浅出,全面理解HTTP协议
网络编程懒人入门(八):手把手教你写基于TCP的Socket长连接
网络编程懒人入门(九):通俗讲解,有了IP地址,为何还要用MAC地址?
技术扫盲:新一代基于UDP的低延时网络传输层协议——QUIC详解
让互联网更快:新一代QUIC协议在腾讯的技术实践分享
现代移动端网络短连接的优化手段总结:请求速度、弱网适应、安全保障
聊聊iOS中网络编程长连接的那些事
移动端IM开发者必读(一):通俗易懂,理解移动网络的“弱”和“慢”
移动端IM开发者必读(二):史上最全移动弱网络优化方法总结
IPv6技术详解:基本概念、应用现状、技术实践(上篇)
IPv6技术详解:基本概念、应用现状、技术实践(下篇)
从HTTP/0.9到HTTP/2:一文读懂HTTP协议的历史演变和设计思路
脑残式网络编程入门(一):跟着动画来学TCP三次握手和四次挥手
脑残式网络编程入门(二):我们在读写Socket时,究竟在读写什么?
脑残式网络编程入门(三):HTTP协议必知必会的一些知识
脑残式网络编程入门(四):快速理解HTTP/2的服务器推送(Server Push)
脑残式网络编程入门(五):每天都在用的Ping命令,它到底是什么?
脑残式网络编程入门(六):什么是公网IP和内网IP?NAT转换又是什么鬼?
以网游服务端的网络接入层设计为例,理解实时通信的技术挑战
迈向高阶:优秀Android程序员必知必会的网络基础
全面了解移动端DNS域名劫持等杂症:技术原理、问题根源、解决方案等
美图App的移动端DNS优化实践:HTTPS请求耗时减小近半
>> 更多同类文章 ……

[2] NIO异步网络编程资料:
Java新一代网络编程模型AIO原理及Linux系统AIO介绍
有关“为何选择Netty”的11个疑问及解答
开源NIO框架八卦——到底是先有MINA还是先有Netty?
选Netty还是Mina:深入研究与对比(一)
选Netty还是Mina:深入研究与对比(二)
NIO框架入门(一):服务端基于Netty4的UDP双向通信Demo演示
NIO框架入门(二):服务端基于MINA2的UDP双向通信Demo演示
NIO框架入门(三):iOS与MINA2、Netty4的跨平台UDP双向通信实战
NIO框架入门(四):Android与MINA2、Netty4的跨平台UDP双向通信实战
Netty 4.x学习(一):ByteBuf详解
Netty 4.x学习(二):Channel和Pipeline详解
Netty 4.x学习(三):线程模型详解
Apache Mina框架高级篇(一):IoFilter详解
Apache Mina框架高级篇(二):IoHandler详解
MINA2 线程原理总结(含简单测试实例)
Apache MINA2.0 开发指南(中文版)[附件下载]
MINA、Netty的源代码(在线阅读版)已整理发布
解决MINA数据传输中TCP的粘包、缺包问题(有源码)
解决Mina中多个同类型Filter实例共存的问题
实践总结:Netty3.x升级Netty4.x遇到的那些坑(线程篇)
实践总结:Netty3.x VS Netty4.x的线程模型
详解Netty的安全性:原理介绍、代码演示(上篇)
详解Netty的安全性:原理介绍、代码演示(下篇)
详解Netty的优雅退出机制和原理
NIO框架详解:Netty的高性能之道
Twitter:如何使用Netty 4来减少JVM的GC开销(译文)
绝对干货:基于Netty实现海量接入的推送服务技术要点
Netty干货分享:京东京麦的生产级TCP网关技术实践总结
新手入门:目前为止最透彻的的Netty高性能原理和框架架构解析
写给初学者:Java高性能NIO框架Netty的学习方法和进阶策略
>> 更多同类文章 ……

即时通讯网 - 即时通讯开发者社区! 来源: - 即时通讯开发者社区!

上一篇:全面了解移动端DNS域名劫持等杂症:原理、根源、HttpDNS解决方案等下一篇:Android程序员必知必会的网络通信传输层协议——UDP和TCP

本帖已收录至以下技术专辑

推荐方案
打赏楼主 ×
使用微信打赏! 使用支付宝打赏!

返回顶部