高性能 PyTorch 训练:性能瓶颈的调查与分析

PyTorch

在 2014 年之前,神经网络与深度学习还没有大规模地应用于工业界。研究者们开发了一些基本而有效的工具包,来搭建神经网络。其中的代表就是 Caffe、Torch 和 Theano。由于当时的研究主流方向是卷积神经网络 (CNN) 在计算机视觉 (CV) 中的应用,所以这些框架主要关注的是 layers。这种设计完全可以满足研究者们拼接不同卷积层、了解不同神经网络结构效果的目的。

而在之后,随着循环神经网络 (RNN) 和自然语言处理 (NLP) 的兴起,以 layers 为“first class citizen”的工具包们就开始力不从心了。而工业界也开始对模型构建、训练以及部署的效率提出了新的要求。随着以 Google、Microsoft、Facebook、Amazon 等巨头的加入,以数据流 (Data Flow) 为中心的体系被提出,TensorFlow、CNTK、MXNet、PyTorch 等新一代的深度学习框架应运而生。

PyTorch 是一个基于 Torch 库的开源机器学习库,用于计算机视觉和自然语言处理等应用,主要由 Facebook 的人工智能研究实验室 (FAIR) 开发。它是在修改后的 BSD 2.0 许可协议下发布的免费开源软件。

正如同它名字的前缀一般,PyTorch 主要采用 Python 语言接口。与 TensorFlow 1.x 相比,PyTorch 的编写方式更简单自然,API 更 pythonic,对 debug 也更加友好。因此,PyTorch 在学术界赢得了更多的拥趸,近年来顶级会议中,PyTorch 的代码提交量遥遥领先于第二名的 TensorFlow (Keras)。而在工业界,PyTorch 后来居上,已经逐渐和 TensorFlow 分庭抗礼。

很多学术界最新的成果都是以 PyTorch 构建的,并被作者开源在了 GitHub。但也有很多声音表示 PyTorch 在训练中比 TensorFlow 更慢。

高性能 PyTorch 的训练流程是什么样的?是产生最高准确率的模型?是最快的运行速度?是易于理解和扩展?还是容易并行化?

答案是,上述所有。

结合我自己给 PyTorch 提速的经历,本文将给出一些提升 PyTorch 性能的方向。当然,作为本文的读者,您需要对 Linux 操作系统和 PyTorch 足够熟悉。

了解瓶颈所在

首先,当感受到训练缓慢时,我们应当检查系统的状态来得知代码的性能被哪些因素限制了。计算,是一个由应用程序(代码)、存储设备(硬盘、内存)、运算设备(CPU、GPU)共同参与的过程,有着非常明显的木桶效应,即其中任意一个环节都有可能成为性能瓶颈的来源。

工具

此时,熟悉一些运维工具可以有效地帮助你了解当前整个计算机以及各个硬件设备的工作状态。只有将性能瓶颈定位到 CPU、GPU、I/O 或是代码中,才能开始解决问题。

htop

htop 是一个跨平台的交互式流程查看器。

htop 允许垂直和水平滚动进程列表,以查看它们的完整命令行以及内存和CPU消耗等相关信息。显示的信息可以通过图形设置进行配置,并且可以交互地进行排序和过滤。与进程相关的任务(例如终止和更新)可以在不输入其 PID 的情况下完成。

从 htop 顶部的信息集合中,可以监视 CPU 和内存的使用情况。

一个好的高性能程序,应当尽可能多地进行异步运算,来充分发挥多核 CPU 的能力。同时,尽量多地使用内存,能够大大提高数据的交流效率。当然,这并不意味着你可以把所有的 CPU 和内存资源耗尽,这将使系统不能够正常调度资源,反而拖累计算。

在所有的 Linux 发行版中,你都可以直接从软件仓库中安装 htop,例如:

iotop 和 iostat

iotop 是用来监视每个命令所占用的 I/O 情况的命令行应用程序。

iostat 则是属于 sysstat 工具包中的一个组件,可以监视外部存储设备(硬盘)当前的 I/O 情况。

安装命令分别为:

需要注意的是,因为涉及到 I/O 情况的监视,所以以上两款程序均需要 root 权限才能正常运行。

nvidia-smi

NVIDIA System Management Interface 是基于 NVIDIA Management Library (NVML) 的命令行应用程序,旨在帮助管理和监视 NVIDIA GPU 设备。

一般来说,GPU 的流处理器使用率越高,就说明 GPU 是在以更高的效率运转的。设备的当前功率也能从侧面反映这个问题。换言之,如果你发现你的流处理器利用率低于 50%,则说明模型没能很好地利用 GPU 的并行能力。

在通过 sh 脚本安装 NVIDIA GPU 驱动后,nvidia-smi 会被自动安装。如果是从发行版的仓库中安装的驱动,可以尝试在软件源中搜索安装 nvidia-smi

nvtop

nvtop 代表 NVIDIA top,由开发者 Maxime Schmitt 发布于 GitHub,是一款用于观察和记录 NVIDIA GPU 使用情况的 (h)top 任务监视器。你会发现 nvtop 有着与 htop 非常相似的 UI。

它可以用于 GPU,并以曲线图的形式输出在一段时间内 GPU 流处理器和显存使用情况的变化。

相比于 nvidia-sminvtop 会关注到更关键的设备信息,并给出其在时间序列上的变化情况。

你可以参考 Syllo/nvtop 中的描述自行编译安装 nvtop

py-spy

py-spy 是一个针对 Python 程序的采样分析器。

它使您可以直观地看到 Python 程序正在花费时间,而无需重新启动程序或以任何方式修改代码。 py-spy 的开销非常低:为了提高速度,它是用 Rust 编写的,并且不会在所分析的 Python 程序相同的进程中运行。这意味着对生产 Python 代码使用 py-spy 是安全的。

py-spy 可以生成如下的 SVG 图像,来帮助你统计每一个 package、model 甚至每一个 function 在运行时所耗费的时间。

py-spy 同样能够以 top 的方式实时显示 Python 程序中哪些函数花费的时间最多。

只需要在 pypi 中安装即可:

问题分析与解决思路

有了以上工具所收集的信息,我们就可以开始分析限制程序性能发挥的原因了。

PyTorch Workflow

而首先,我们要了解 PyTorch 的工作流程。

因为 PyTorch 使用 Python 接口,同时在底层调用了相当多的 C 库,所以在使用 PyTorch 时,很多细节对用户是不暴露的。实际上,在常见的训练过程中,用户和 PyTorch 一起,大致完成了以下的步骤:

  1. 构建模型。将编写好的模型类实例化为 nn.Module 对象。
  2. 准备数据。将训练数据和测试数据进行预处理,然后组织为 Dataloader 的形式,并设置好数据增强方案。
  3. 定义 Loss Function 和 Optimizer。
  4. 主训练循环。

其中,主训练循环决定了网络经过多少次完整的数据集,即我们常说的 epoch:

  1. 从 Dataloader 中提取当前 batch 的数据。一般 Dataloader 中只记录了数据的 index 信息,所以每次训练循环时,对应的数据都会从硬盘被读取到内存,然后再从内存放入显存中,交由 GPU 进行后续步骤。
  2. 数据经过模型。
  3. 将得到的输出送到 Loss Function 中计算损失,随后进行 backward 求导。
  4. Optimizer 执行梯度下降,更新参数。

一般来说,PyTorch 训练的过程的快慢决定于主训练循环。主循环中的每一步都将被执行上万次乃至几十万次,任何的效率提升都能够带来极大的收益。

CPU 瓶颈

CPU 和 GPU 的计算特点,决定了它们不同的功用:CPU 具有更高的主频和精度,适合于进行串行任务;GPU 拥有几千到上万个 Stream 核心,可以进行大规模的并行任务。

所以,对于数据增强等没有相互依赖的任务交给 CPU 来进行,很大程度上会拖慢训练的进程。在每次的数据导入时,都会产生一定时间的等待。这是一种非常普遍的 CPU 瓶颈,即将不适合 CPU 的任务交给它来处理。

GPU 瓶颈

如果你熟悉梯度下降 (Gradient Descent) 的原理,那么你一定能够理解 batch size 对训练速度的影响。梯度下降将一个 batch 中的平均梯度作为总体梯度方向的近似,进行一次参数更新。Batch size 越大,那么 GPU 内同时并行计算的数据也就越多,相应的训练速度会有很大的提升。

Batch size 的设定对最终的训练结果有一定的影响,但是在一定范围内的调整并不会产生非常大的扰动。

主流观点中,在不过分影响最终的模型性能的前提下,batch size 的选取以最大化利用显存和流处理器为佳。

I/O 瓶颈

I/O 瓶颈是最常见、最普遍的训练效率影响因素。

正如上文中所描述的,数据将会在硬盘、内存和显存中不断地转移和复制。不同存储设备的读写速度,可能有几个数量级上的差异。

出现 I/O 瓶颈的标志主要有:

  1. 系统 I/O 读写(尤其是硬盘读写)占用过高;
  2. 内存占用和 CPU 占用都普遍偏低;
  3. 显存占用较高的情况下,流处理器的利用率过低。

将数据预读入内存中、异步进行数据加载都是有效的解决方案。一个简单稳定的方式是直接使用 DALI 库。

NVIDIA Data Loading Library (DALI) 是一个可移植的开源库,用于解码和增强图像、视频和语音,以加速深度学习应用程序。DALI 通过重叠训练和预处理减少了延迟和训练时间,缓解了瓶颈。它为流行的深度学习框架中内置的数据加载器和数据迭代器提供了一个插件,便于集成或重定向到不同的框架。

用图像训练神经网络需要开发人员首先对这些图像进行归一化处理。此外,图像通常会被压缩以节省存储空间。因此,开发人员构建了多阶段数据处理流程,包括加载、解码、裁剪、调整大小和许多其他增强算子。这些目前在 CPU 上执行的数据处理流水线已经成为瓶颈,限制了整体吞吐量。

DALI 是内置数据加载器和数据迭代器的高性能替代品。开发人员现在可以在 GPU 上运行他们的数据处理工作。

参考链接


高性能 PyTorch 训练 (1):性能瓶颈的调查与分析

nvidia-smi GPU性能状态(Performance State)含义

我正在使用Nvidia GTX Titan X进行深度学习实验。
我正在使用nvidia-smi来监视GPU的运行状态,但是提供的工具的性能(性能)状态没有意义。

我已经查看了nvidia-smi手册,它表示以下内容:

Performance State
The current performance state for the GPU. States range from P0 (maximum performance) to P12 (minimum performance).

如果不在GPU上运行任何进程(空闲状态),则GPU性能状态为p0。
但是,当运行一些计算繁重的过程时,状态变为p2。

我的问题是,为什么我的GPU闲置时处于P0状态,但是在执行繁重的计算任务时切换到P2? 不应该相反吗?

另外,有没有办法使我的GPU始终在P0状态下运行(最高性能)?


令人困惑。

但是,nvidia-smi手册是正确的。

当一个或一组GPU处于空闲状态时,在计算机上运行nvidia-smi的过程通常会使其中一个GPU退出空闲状态。这是由于该工具正在收集的信息-需要唤醒其中一个GPU。

此唤醒过程最初会将GPU置于P0状态(最高性能状态),但如果GPU空闲或不是特别忙碌,GPU驱动程序将监控该GPU,并最终开始降低性能状态以节省功耗。

另一方面,当GPU在工作负载下处于活动状态时,GPU驱动程序将根据其自身的启发式方法不断调整性能状态以提供最佳性能,同时使性能状态与实际工作负载相匹配。如果没有达到热或功率限制,则对于最活跃和最重的连续工作负载,性能状态应达到最高水平(P0)。

周期性很重但不连续的工作负载可能会导致GPU功耗状态在P0-P2级别附近波动。由于热(温度)或电源问题而"受限制"的GPU也可能会看到P状态降低。这种限制是显而易见的,并在nvidia-smi中单独报告,但是可能并非所有GPU类型都启用这种报告。

如果要在GPU上查看P0状态,我可以提供的最佳建议是运行短暂,繁重且连续的工作负载(例如,执行大型sgemm操作的工作),然后在该工作负载期间监视GPU。在这种情况下应该可以看到P0状态。

如果您使用的是正在使用cuDNN库的机器学习应用程序(例如Caffe),并且正在训练大型网络,则应该可以不时看到P0,因为cuDNN会执行类似于sgemm的操作通常情况下。

但是对于零星的工作负载,最常见的状态很有可能是P2。

要始终"强制" P0电源状态,可以尝试通过nvidia-smi工具尝试持久性模式和应用程序时钟。使用nvidia-smi --help或nvidia-smi的手册页了解选项。

尽管我认为这通常不适用于Tesla GPU,但除非特别设置更高的应用时钟,否则某些NVIDIA GPU可能会在计算负载下将自身限制为P2功耗状态。使用nvidia-smi -a命令查看可用于GPU的当前应用程序时钟,默认应用程序时钟和最大时钟。 (某些GPU(包括较旧的GPU)可能会在其中某些字段中显示N / A。这通常表明应用程序时钟无法通过nvidia-smi进行修改。)如果在计算负载期间卡似乎以P2状态运行,则可能通过将应用程序时钟增加到最大可用时钟(即最大时钟),可以将其增加到P0状态。使用nvidia-smi --help了解如何格式化命令以更改GPU上的应用程序时钟。修改应用程序时钟或启用可修改的应用程序时钟可能需要root / admin特权。设置GPU持久模式也可能是理想的或必要的。这将防止驱动程序在GPU活动期间"卸载",这可能导致驱动程序重新加载时重置应用程序时钟。

对于这种情况下受影响的卡,此默认行为是在计算负载下限制为P2,这是由GPU驱动程序设计的。

参考链接


ubuntu 21.10(GeForce GTX 3060 12GB)编译StyleGAN3

安装驱动:

Anaconda 上建立独立的编译环境,然后执行编译:

参考 Anaconda conda切换为国内源  加速下载。

编译配置StyleGAN3

如果报错:

上述报错产生的原因是在 Anaconda 下载的包,在进行编译的时候,使用了高版本的 libstdc++.so。而运行时却使用了Anaconda 环境里低版本的 libstdc++.so 导致报错。

了解了原因,解决方法就比较简单了,可以手工升级 Anaconda 环境下的 libstdc++.so 动态库。

如下:

目前测试发现,当batch=4的时候会在第11天的时候报告OOM,如下:

参考链接


VNC还是RDP? 云上的远程桌面究竟该如何选

这里说的VNC是什么?

简单来说,所谓的 VNC(Virtual Network Computing)是一种图形化的桌面共享系统,它使用远程帧缓冲协议 (RFB) 来远程控制另一台计算机。它将键盘和鼠标事件从一台计算机传输到另一台计算机,通过网络向另一个方向转发图形屏幕更新。

类似这样的技术VNC不是绝无仅有,但VNC 的流行和普及却因为其具有的过人之处 –

  • VNC是平台无关的—— 有多种客户端和服务器的实现,几乎涵盖了所有的主流平台。甚至一些VNC的实现被称“无客户端”,这是因为不需要安装插件或客户端软件而,而是依靠HTML5技术,只需要一个浏览器就可以访问远程桌面了。
  • VNC是开源的—— VNC最初是在英国剑桥的Olivetti & Oracle研究实验室开发的。原始的VNC源代码和许多现代的衍生品在GNU通用公共许可证下是开放源码的。
  • VNC的协议是简单、普适的—— VNC使用的是 RFB(Remote Framebuffer) 协议。这是一个开放且简单的协议。因为它在framebuffer级别工作,协议是基于像素的所以适用于所有窗口系统和应用程序,包括Microsoft Windows、macOS和X Window系统。这个协议的性能表现是很出色的。

说起来满满的都是优点,那么

访问云上的实例,为什么不选择VNC呢?


VNC 的优点很多,很多场景下都能看到VNC。例如,访问树莓派的桌面,对 headless 服务器的管理等等。但是,对于云上实例的远程图形化的访问VNC却不是好的选择。考虑到我们的使用场景是通过互联网来访问云上的主机,这就引出了否定 VNC 最主要的原因– 安全性。

默认情况下,RFB并不是一个安全的协议。虽然这个协议下密码不以明文方式发送,但如果能从网络中嗅出加密密钥和已编码的密码,还是有可能破解成功的。因此,建议密码至少有8个字符。另一方面,VNC的一些版本也有8个字符的限制; 如果发送的密码超过8个字符,则删除多余的字符,并将截断的字符串与密码进行比较。

在VNC生态系统中,”Big Four” 指的是LibVNC、UltraVNC、Tight VNC和TurboVNC 这四家提供VNC 产品的厂商。2019年,Kaspersky Lab 的研究人员对这四家公司进行了审计,以了解它们的安全性。他们的发现是令人失望的。总的来说,研究人员发现这四个程序的客户端和服务器部分共有37个严重缺陷。其中22个在 UltraVNC,另外10个在 LibVNC, 4个在 TightVNC,还有一个在 TurboVNC,但这是一个严重的漏洞,它会让攻击者在服务器端远程执行代码。

有人会建议通过 SSH 或 VPN 连接进行 VNC 的隧道化,通过这种方法增加一个具有更强加密功能的额外安全层。但是这种方法并不完美,除了增加了复杂性也容易引起其它的一些安全问题,例如中间人攻击等。

否定一个技术是简单的,但我们是否有替代技术呢?答案就是 Remote Desktop Protocol (RDP)。

那么,RDP又是什么?

有过 Windows 使用经验的人对于远程桌面(Remote Desktop Protocol ,RDP)一定不会陌生。RDP 是由微软公司开发的一种专有协议,它为用户提供了通过网络连接到另一台计算机的图形界面。在使用上,用户需要使用 RDP 客户端软件,而在远程另一台计算机则需要运行 RDP 服务器软件。

微软的Windows、Linux、macOS、iOS、Android等操作系统都有客户端。Windows操作系统内置RDP服务器;Linux与 macOS 可以安装一个 RDP 服务器。缺省配置下,服务器监听 TCP 端口 3389 和 UDP 端口 3389。

微软目前把他们的官方RDP客户端软件称为Remote Desktop Connection,以前叫做 “Terminal Services client”

与VNC 相比,RDP的安全性有很大的提升。主要的安全特性包括了:

  • 128位加密,使用RC4加密算法(版本6加入)
  • 提供了对TLS的支持(版本2加入)

此外,正如前面提到的VNC协议是基于像素的。尽管这带来了极大的灵活性,可以显示任何类型的桌面,但它的效率往往不如那些更好地理解底层图形布局(例 如: X11)或桌面(例如:RDP )的解决方案。这些协议以更简单的形式(例如:打开窗口)发送图形原语或高级命令,而 VNC 的 RFB 协议尽管支持压缩但只能是发送原始像素数据。

如何使用RDP?

在 Windows 环境下使用RDP是再简单不过的事情。我想谈的是在 Linux 环境下RDP 的安装部署与使用。虽然微软公司没有为 Linux 提供 RDP 的软件,但是我们可以使用开源的xRDP,这是RDP协议在Linux平台 的实现。

xRDP是一个开源的远程桌面协议服务器,它用来实现Linux接受来自 Windows、Mac 或者 Linux 远程桌面客户端或的连接。这意味着你不需要在我们所使用的 Windows 或 macOS 机器上安装额外的第三方应用程序。

Linux 安装 xRDP的方法如下。这里我以 Ubuntu 20.04 为例 –

  • 安装Gnome

GNOME是一个Linux 操作系统下的桌面环境,完全由免费和开源软件组成。缺省情况下在EC2上安装的Linux 操作系统都不包含Gnome,需要额外安装。Ubuntu 缺省的桌面窗口管理器就是Gnome,用以下命令安装。

安装 xRDP除了Gnome 以外,我们还可以选择 LXDE、Xface 等等。相比之下,LXDE 是轻量级的窗口管理器,Xface 则具备类似 Windows 界面的风格。如果不在乎额外增加的大约 5GB 的磁盘存储的开销,我还是推荐使用Gnome。原因在于这与我们本地使用的 Ubuntu 具有一致性的体验。

输出结果如下:安装完成后,xRDP服务将自动启动。可以通过这个命令来检查其状态:

  • 接下来,要为Linux的用户(ubuntu)设置登录密码

此外,如果我们的 EC2 实例绑定了Elastics IP 并且拥有自己的域名,我推荐使用Let’s Encrypt 发出的免费SSL证书替换缺省的自签发的证书。需要注意的一点,Let’s Encrypt 证书的有效期是90天。可以考虑配置crontab 使用cerbot 自动更新证书。这里设置的密码将被用来登录到目标的 EC2 实例,出于安全的理由务必使其符合密码安全的策略。第二条命令是将 用户ubuntu 加入到 ssl-cert 用户组中。这是因为默认情况下,xRDP 使用的是自签发的证书,这个证书保存在 /etc/ssl/private/ssl-cert-snakeoil目录下。证书的密钥文件只能由 “ssl-cert” 用户组的成员读取。

  • 修改安全组 RDP 协议缺省使用3389端口。务必记得在EC2的安全组中打开TCP与UDP在这个端口上的访问许可。

  • 登录

在这里,username 输入ubuntu,password 输入刚刚修改过的用户密码。成功登录之后熟悉的Ubuntu 桌面就会立刻出现在眼前。

按照我的体验,网络延迟在35ms以内xRDP 的与本地Linux 桌面的操作体验几乎没有差别。实测之下,通过我所使用的100M联通宽带访问AWS 中国(北京)区域的EC2 实例,网络延迟大约在5ms左右。

单以鼠标、键盘的使用体验而论,本地桌面与远程桌面的差别已经微乎其微了,我终于可以放心的将许多工作移到云端。无论是通过我的笔记本电脑、iPad 甚至是一台树莓派都能够让我接入顺利的接入远程桌面。如此一来,开发在云端岂不是再简单不过的事情了。

参考链接


解决KiCAD(系统macOS Big Sur)关联封装(Footprint)报错“No PCB footprint libraries are listed in the current footprint library table.”

最近在把macOS Big Sur系统上的KiCAD5.x升级到6.x之后,新建的项目在进行关联封装操作的时候,报错

具体如下图:

造成这个问题的原因是KiCAD 6.x版本的全局封装库默认存储位置发生了变动,从低版本升级上来,还是会从以前的配置文件中读取原来配置的路径信息,导致找不到新版本的全局封装库路径。

解决方法是删除低版本配置的封装库相关的配置信息即可(删除之前,先关闭KiCAD软件,否则删除无效)。

macOS系统:

Windows系统:

Linux系统:

完成后,重启KiCAD软件即可。

参考链接


[华硕主板] 支持ECC内存的AMD Ryzen™处理器列表

下表列出了支持带ECC功能内存模组的Ryzen™处理器的列表;它们的官方名称;以及是否支持B550X570系列。

请注意,当涉及到APUsRyzen 3000/4000 G系列),只有PRO处理器(例如Ryzen 3 PRO 3200G)支持ECC内存。

官方名称

支持B550系列

支持X570系列

ECC功能

UDIMM ECC

REG ECC

AMD Ryzen™ 5000 Series Processors

V

V

支持

V

 

AMD Ryzen™ 4000 G-Series Processors

V

V

只有PRO支持

V

 

AMD Ryzen™ 3000 Series Processors

V

V

支持

V

 

AMD Ryzen™ 2000 Series Processors

 

V

支持

V

 

AMD Ryzen™ 3000 G-Series Processors

 

V

只有PRO支持

V

 

注意,Ryzen™处理器只支持二手UDIMM ECC,不支持更便宜的二手REG ECC

这个问题简单解释一下,新的REG ECC从性能到价格都是远远高于UDIMM ECC的,但是REG ECC不能用在家用主板上,导致大量淘汰的二手内存需求量不高,因而价格偏便宜。

继续阅读[华硕主板] 支持ECC内存的AMD Ryzen™处理器列表

运维中tcp_tw_recycle net.ipv4.tcp_timestamps引发的坑

NAT环境下,遇到因为tcp_tw_recycle=1和net.ipv4.tcp_timestamps=1引起 Nginx upstream timed out 后,一直没在遇见,今天在朋友的阿里云环境下又重新再一次出现;因此在这炒一次冷饭,让运维新手或者刚上云的朋友大概了解一下,避免再一次采坑。

故障情况:

阿里云账号A的A机房,内网里面部署两台Nginx,通过网络出口(NAT),代理用户访问到阿里云账号B的B机房服务。A机房的Nginx出现:upstream timed out 。

故障的诱因是:net.ipv4.tcp_timestamps=1

抓包图:

注意,这个选项生效的前提是,报文的发出方必须在TCP头部的可选项中增加时间戳字段,否则这个设置是不生效的。

直接上当年的笔记:

先看看TCP IP 对tw的一些解析:
RFC 1323里有这样的定义:

大概的中文意思就是:TCP协议中有一种机制,缓存了每个主机(即ip)过来的连接最新的timestamp值。这个缓存的值可以用于PAWS(Protect Against Wrapped Sequence numbers,是一个简单的防止重复报文的机制)中,来丢弃当前连接中可能的旧的重复报文。而Linux实现这个机制的方法就是同时启用net.ipv4.tcp_timestamps和net.ipv4.tcp_tw_recycle 这两个选项。
这种机制在 客户端-服务器 一对一的时候,没有任何问题,但是当服务器在负载均衡器后面时,由于负载均衡器不会修改包内部的timestamp值,而互联网上的机器又不可能保持时间的一致性,再加上负载均衡是会重复多次使用同一个tcp端口向内部服务器发起连接的,就会导致什么情况呢:

负载均衡通过某个端口向内部的某台服务器发起连接,源地址为负载均衡的内部地址——同一假如恰巧先后两次连接源端口相同,这台服务器先后收到两个包,第一个包的timestamp被服务器保存着,第二个包又来了,一对比,发现第二个包的timestamp比第一个还老——客户端时间不一致。服务器基于PAWS,判断第二个包是重复报文,丢弃之。

反映出来的情况就是在服务器上抓包,发现有SYN包,但服务器就是不回ACK包,因为SYN包已经被丢弃了。为了验证这一结果,可以执行netstat -s | grep timestamp 命令,看输出里面passive connections rejected by timestamp 一项的数字变化。

tcp_ipv4.c中,在接收SYN之前,如果符合如下两个条件,需要检查peer是不是proven,即per-host PAWS检查:

  • 收到的报文有TCP option timestamp时间戳
  • 本机开启了内核参数net.ipv4.tcp_tw_recycle

解决办法:

tcp_tw_recycle=0 或(和)net.ipv4.tcp_timestamps=0同时从4.10内核开始,官方修改了时间戳的生成机制,所以导致 tcp_tw_recycle 和新时间戳机制工作在一起不那么友好,同时 tcp_tw_recycle 帮助也不那么的大。

此处的时间戳并不是我们通常意义上面的绝对时间,而是一个相对时间。很多情况下,我们是没法保证时间戳单调递增的,比如业务服务器之前部署了NAT,LVS等情况。相信很多小伙伴上班的公司大概率实用实用各种公有云,而各种公有云的 LVS 网关都是 FullNAT 。所以可能导致在高并发的情况下,莫名其妙的 TCP 建联不是那么顺畅或者丢连接。

而这也是很多优化文章中并没有提及的一点,大部分文章都是简单的推荐将net.ipv4.tcp_tw_recycle设置为1,却忽略了该选项的局限性,最终造成严重的后果(比如我们之前就遇到过部署在nat后端的业务网站有的用户访问没有问题,但有的用户就是打不开网页)。

参考链接


树莓派固件更新 rpi-update

1 简介

rpi-update是一个用于更新树莓派固件的工具,可以通过apt get install rpi-update来安装

一般来说直接执行下面的命令就可以更新固件(扯淡,基本不可能好吗):

2 跳过自更新

rpi-update启动时会更新自己,如果报以下错误:

可以尝试一下他说的方法来解决:

如果都不行,直接跳过算了,反正也可以用apt来更新这个工具,没必要让他更新自己。

跳过自更新,直接更新固件的方法:

3 【究极方法】本地更新

即使跳过自更新,下载速度也太慢,还经常断流。可以考虑本地更新:

先在PC上下载固件(可以用一下魔法上网):

然后用scp传到树莓派上的/root目录下,之后ssh连上树莓派:

4 检查

对于树莓派4的一个检查方法:

看该目录下是否有libEGL.solibGLESv2.so这两个库,更新前这两个库都是没有的。

如果这两个库出现了,并且是真正的库而不是软链接,就说明更新大概可能也许成功了吧。

参考链接


树莓派固件更新(rpi-update)的那些坑

群晖DSM7通过Docker安装WordPress最新版

PHP 7.x连接MySQL 8.x报错“The server requested authentication method unknown to the client [caching_sha2_password]”

PHP 7.x使用如下代码连接MySQL 8.x

结果出现如下错误信息

原因为 MySQL 支持 caching_sha2_password  / 用户名密码 两种验证方式,但是从 MySQL 8 开始默认使用caching_sha2_password 的验证方式,导致以前版本的客户端不能成功连接。更详细的解释如下:

The server validates the user and returns the connection status. MySQL uses caching_sha2_password and auth_socket plugins for validation.

The caching_sha2_password plugin uses an SHA-2 algorithm with 256-bit password encryption. MySQL 8 prefers this auth method.

Whereas the auth_socket plugin checks if the socket username matches with the MySQL username. If the names don’t match, it checks for the socket username of the mysql.user. If a match is found, the plugin permits the connection.

But to serve the pre 8.0 clients and avoid compatibility errors, it is preferred to revert back the auth method. Older versions of MySQL use mysql_native_password plugin for validation.

解决方案为修改用户默认的认证方式,如下:

参考链接


[RESOLVED] MySQL the server requested authentication method unknown to the client