netty核心流程(二):客户端与服务端的读写过程

news/2024/11/15 22:56:33/文章来源:https://www.cnblogs.com/aries-laosi/p/18333496

连接成功建立后,客户端是如何向服务端发送请求的?

由于内部源码的调用过于复杂,我们只分析有代表性的代码。在 AbstractChannel 类中我们可以看到:

 

wirte() 方法最后会把发送的数据 msg 放入 addMessage() 方法中,这个方法是做什么的呢?

 

原来把要发送数据放入一个缓冲链表中,当要发送一个新的数据时,并不是直接把数据发送出去,而是会把这个数据放入一个叫做 unflushedEntry 缓冲链表的尾部,等待发送。

那么,真正发送时,肯定是要把发送数据从缓冲链表中拿出来的,我们再往下看。

 

flush() 方法中的 outboundBuffer.addFlush() 才是从缓冲链表中读取数据请求的地方。

类 NioSocketChannel:

 

可以看到 addFlush() 方法会从 unflushedEntry 里面取出请求数据,然后再把请求数据放入 flushedEntry 缓冲链表里。也就是说,发送数据时需要两个链表缓冲来支撑。

 

最后一步就简单了,其实就是调用 Java NIO 底层写数据的操作。

我们通过流程图再回顾一下以上流程:

 

只看跟上述流程相关的左下脚的红字和虚线。客户端把要发送的请求数据放入一个缓冲链表,然后拿到缓冲列表的元素来做具体的数据发送。

Netty 客户端是如何接收服务端发来的响应的

我们还是跟踪相关的代码:

 

走到这步其实就是调用我们在 demo 中自己定义的处理读的逻辑,代码如下:

 

好了,我们通过流程图再回顾一下以上流程:

 

只看跟上述流程相关红字和虚线。首先,服务端通过线程 NioEventLoop 把响应数据放入 SocketChannel 中,并发送给客户端。客户端线程 NioEventLoop 对应的 Selector 监听到 OP_READ 事件后,客户端对应的 NioEventLoop 会把响应数据读出来,然后再调用用户自己定义好的处理逻辑来处理读出来的数据。

Netty 工作流程及设计思想简介

Netty 的整个流程都详细地介绍完了。接下来给介绍一下 Netty 的工作流程及设计思想。首先讲解一下 Netty 工作流程。

Netty 工作流程图

前面已经详细地画了 Netty 处理网络事件的流程图。但是这些图比较详细复杂,不利于同学们理解 Netty 内部组件的工作机制。所以,画了个能够更好体现的 Netty 工作机制的流程图,如下:

 

分为如下几步:

  1. 服务端对外暴露端口,供客户端连接。
  2. 客户端向服务端对应的接口发出连接的请求。
  3. 服务端收到客户端的连接请求后,Boss 线程池会用一个线程去利用 ServerSocketChannel 完成三次握手,从而连接建立。
  4. 建立完连接后再创建一个 SocketChannel 实例,这个 SocketChannel 实例就代表这个客户端和服务端的连接,以后所有的读写操作都会发生在这个 SocketChannel 实例里。
  5. 连接建立后,把连接分配给 Worker 线程池中的一个线程,同时会把这个 SocketChannel 上的读事件注册到线程中的 Selector 上。
  6. 当客户端向服务端发送数据后,Worker 线程中的 selector 会监听到读事件,然后 Worker 线程把数据读出来。
  7. Worker 线程把读出来的数据交给自定义业务线程池中的一个线程处理业务逻辑。
  8. 业务逻辑处理完后,业务线程把响应返还给 Worker 线程。
  9. 最终,worker 线程把响应写入 SocketChannel 进而把响应发给客户端。

这里的 Boss 线程池和 Worker 线程池都是我们实例化的 EventLoopGroup,即事件轮询组。事件轮询组是来轮询处理网络事件的一组线程,不同的事件轮询组处理的网络事件有可能不同,比如 Boss 线程池专门用来处理连接事件的,而 worker 线程池是专门处理读写事件的。

也就是说,我们在 Netty 编程里,应该有三种线程。

  • Boss 线程:负责实现 TCP 三次握手,实现客户端和服务端的连接,并把创建好的连接交给 worker 线程。
  • Worker 线程:监听读写事件,并实现网络读写的线程,一个线程可以负责多个连接上的读写。
  • 自定义业务线程:主要工作是非网络 IO 的工作,比如数据的编码、解码、业务逻辑处理、访问数据库,等等。

下面讲解 Netty 编程时两个重要的参数 BackLog 和 KeepAlive,先看看这两参数是在哪里配置的:

 

BackLog

要想讲清楚 BackLog 这个参数,我们需要重温 TCP 连接三次握手,如下图所示:

 

我解释一下执行步骤:

  1. 客户端初始状态为 Closed,这时客户端会给服务端发送 syn 请求来告知服务端有 TCP 连接请求,并把客户端的状态改为 SYN-SENT。

  2. 服务端收到客户端发来的 syn 请求后,向客户端发送包含 syn 和 ack 的数据包,syn 表示服务端也要发起连接,ack 是确认已收到客户端发来的 syn 请求,并把服务端的状态改为 SYN-RCVD,服务端会认为这时是半连接,并把半连接放入 syn_queue 库中。

  3. 客户端收到服务端的 ack 确认后,会把自己的状态改为 ESTABLISHED,然后向服务端发送 ack 确认数据包。

  4. 服务端收到 ack 确认后,这时服务端任务连接建立成功了,于是把 syn_queue 库中对应的半连接拿出来放入 accept_queue 中,这时执行 accept() 方法,成功后把连接从 accept_queue 库中删除。

BackLog 这个参数会影响到 accept_queue 里连接数量,accept_queue 表示的是已经建立好但还没有被上层代码获取连接数。也就是说,这个连接数不能太多,太多了会影响性能。

KeepAlive 参数的解释

接下来,我们来看一下 KeepAlive 参数是用来做什么用的。

其实,这是个保活参数,当 TCP 的两端长期没有数据交互的时候,有可能对方已经挂了,于是活着的一端会向另一端尝试发送 KeepAlive 保活请求,如果有 ACK,那么我们认为连接正常;如果没有,我们就认为连接断了,并关闭还存活的这一端的 socket,这样能够节省系统的 TCP 连接资源。

但是,Netty 默认并没有打开 KeepAlive,这是为什么呢?主要有下面几个原因:

  1. 有时候会出现短暂的连接故障,而探活会把连接误杀
  2. 如果一个设备有大量的 TCP 连接,但是不活跃的 TCP 连接特别多的话,探活请求就会浪费大量的带宽

TCP 的 KeepAlive 和 HTTP 的 Keep-Alive 是什么关系?

答案是没有任何关系。HTTP 的 Keep-Alive 设置为 true 后表示一个请求响应过后,HTTP 不会关闭 TCP 连接,这个 TCP 连接会被后面的请求响应复用。而 TCP 的 KeepAlive 则是前面说的 TCP 探活。

总结

本章讲解了连接成功建立后,客户端是如何向服务端发送请求的,以及 Netty 客户端是如何接收服务端发来的响应的。讲解完流程后,又简单介绍了 Netty 的设计思想,包括 Netty 流程图。最后,讲解了两个 Netty 上重要的参数 BackLog 与 KeepAlive。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/774429.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

P1168 中位数题解

题目链接:https://www.luogu.com.cn/problem/P1168 题目描述:给定一个长度为 N 的非负整数序列 A,对于前奇数项求中位数。 思路讲解:最简单的想法就是每次取我们要的那段数组然后排序在输出中位数。确实可以,但是太慢了,直接超时。那就是说要缩短时间,那就可以直接用到堆…

Qt/C++音视频开发80-ffmpeg实现srt推拉流/实时性非常好/音视频同步/支持格式众多

一、前言 目前互联网上的视频直播有两种,一种是基于RTMP协议的直播,这种直播方式上行推流使用RTMP协议,下行播放使用RTMP,HTTP+FLV或者HLS,直播延时一般大于3秒,广泛应用秀场、游戏、赛事和事件直播,满足了对交互要求不高的场景;另一种是WebRTC协议的直播,这种直播方式…

一款基于Fluent设计风格、现代化的WPF UI控件库

前言 今天大姚给大家分享一款基于Fluent设计风格、开源(MIT License)、现代化的WPF UI控件库,它提供直观的设计、主题、导航和全新的沉浸式控件,全部都是原生且无缝地集成在一起:WPF UI。WPF介绍 WPF 是一个强大的桌面应用程序框架,用于构建具有丰富用户界面的 Windows 应…

很幸运,AppStore审核一遍过!——深海记词

极速过审 从2023年9月开始学习iOS开发,终于在24年6月正式开发完成了第一款用于学英语、背单词的APP《深海记词》。 最近在上海出差,闲暇之余开始着手制作商店页海报,做完后就急不可耐的提交了审核。 我是在周六晚上提交的审核,周天早上显示受理审核,下午就显示通过了审核�…

异或哈希

理论基础异或哈希是个很神奇的算法,利用了异或操作的特殊性和哈希降低冲突的原理,可以用于快速找到一个组合是否出现、序列中的数是否出现了k次https://blog.csdn.net/notonlysuccess/article/details/130959107 https://codeforces.com/blog/entry/85900 CF1175Fhttps://cod…

dotnet X11 的多屏触摸行为测试

故事的背景是我在给 Avalonia 加上触摸尺寸的支持时,代码审查过程中大佬提出了在多屏上的 X11 行为问题,为此我找了两个触摸屏进行测试 X11 的多屏触摸行为。由于我的设备有限,本文只记录我所测试到的行为给 Avalonia 加上触摸尺寸支持的功能的代码: https://github.com/Av…

WPF 不带 TargetPlatformVersion 显示 Win10 的 Toast 通知的方法

本文将告诉大家如何在 WPF 不安装 WindowsAppSDK 包,且不在 TargetFramework 带上 TargetPlatformVersion 而弹出 Win10 的 Toast 通知的方法本文这里的 TargetPlatformVersion 指的是在 TargetFramework 里面的内容,如下面的代码里的 10.0.17763.0 就是 TargetPlatformVersi…

读零信任网络:在不可信网络中构建安全系统05网络代理

网络代理1. 网络代理 1.1. 安全策略在认证和授权环节都充分地利用了多个因子,综合考虑了用户及其使用的设备的信息 1.1.1. 允许员工通过企业发放的工作笔记本电脑提交源代码,但是禁止员工使用手机进行类似操作 1.1.2. 用户必须使用可信终端提交代码,并且这个终端必须属于用户…

Activity创建与跳转

layout目录下新建activity_main2.xml<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.…

Linux工作原理14桌面和打印简介

14桌面和打印简介 本章简要介绍了典型 Linux 桌面系统中的组件。在 Linux 系统的各种软件中,桌面是最狂野、最丰富多彩的领域之一,因为有如此众多的环境和应用程序可供选择,而且大多数发行版都能让你比较容易地试用它们。 与 Linux 系统的其他部分(如存储和网络)不同,创建…

NDT论文翻译

The Normal Distributions Transform: A New Approach to Laser Scan Matching 正态分布变换:激光扫描匹配的新方法 摘要:匹配 2D 范围扫描是许多定位和建图算法的基本组成部分。大多数扫描匹配算法需要找到所使用的特征(即点或线)之间的对应关系。我们提出了范围扫描的替代…