我们不能失去信仰

我们在这个世界上不停地奔跑...

0%

使用下面的命令查看处于哪个状态的连接最多。

1
netstat -tna  | awk '{print $5,$6}'| sort | uniq -c | sort -n | tail -n 1

发现排名第一的是TCP的TIME_WAIT 状态连接,竟然高达三万个。

解决方法:

1
2
3
4
5
6
7
8
# 打开 sysctl.conf 文件,修改一下参数
net.ipv4.tcp_tw_recycle = 1
<!--more -->
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_timestamps = 1

# 保存关闭,执行 下面的命令进行 reload
sysctl -p

以上方法对解决 TIME_WAIT 很好。其中 tw_recylce 和 tw_reuse 一定需要 timestamps 的支持,而且这些配置一般不建议开启。

这样确实暂时性解决了 TIME_WAIT 问题,发现 “bug” 没有了,只不过 “bug” 隐藏在了更深的地方。

  • 原理

TIME_WAIT 发生在主动关闭连接的一方, 等待 2MSL 时间,结束 TIME_WAIT, 进入 CLOSED 状态。在一个连接没有进入 CLOSED 状态之前,这个连接是不能重用的。

被动关闭的一方,中间有个时段会进入 CLOSE_WAIT 状态,如果 CLOSE_WAIT 很多,可能程序写的有问题,没有合理关闭 socket,或者是服务器 CPU 处理不过来造成程序没法真正执行 close 操作。

  • TIME_WAIT 用处

为了解决网络的丢包和网络不稳定所带来的其他问题,如果没有 TIME_WAIT, 就可能接收到上一个连接迟迟未到的连接,造成错误的接收。

确保被动关闭的一方能够在时间范围内,关闭自己的连接。之所以等待 2MSL 是因为这样可以确保自己上一个包已经被对方正确接收,并且对方没有重新请求。

如果关闭太快,然后立即被复用,重新连接到刚才断开的被动方。 这时,如果主动关闭方在 TIME_WAIT 之前发送的 ACK 包丢失的话,被动方应该会继续等待 ACK,直至超时,但是此时却可能受到 SYN 建立连接的握手包,被动方就会返回 RST,无法连接成功。

如果忽略 TIME_WAIT,就有可能造成数据错乱,或者短暂性连接失败。

查询了一下,大概几百几千的 TIME_WAIT 应该还是比较正常的。但是我这里发现的是 3万多个 TIME_WAIT,并且都是 MySQL 连接,这样肯定有问题。 第一是 TIME_WAIT 实在太多了,第二 MySQL 也不是能承受 3 万并发吧,说明这些连接是并不是并发创建的(基于服务还正常,偶尔服务非常慢)。

  • net.ipv4.tcp_timestamps

RFC 1323 在 TCP 可靠性中,引入 timestamp 的 TCP option,两个4字节的时间戳字段,其中第一个4字节字段保存发送该数据包的时间,第二个4字节字段用来保存最近一次接收到对方发送数据的时间。有了这两个字段, 就可以利用 tcp_tw_reuse 和 tcp_tw_recycle 进行优化。

  • net.ipv4.tcp_tw_reuse

复用 TIME_WAIT 状态的链接。

主动关闭连接后,如果此时正好有连接需要连接, 就可以复用这个还处于 TIME_WAIT 状态的连接。

tcp_tw_reuse 应用的场景是:某一方,需要不断的通过“短连接”连接其他服务器,并且总是自己先关闭连接(TIME_WAIT 在自己这方),关闭后又不断的重新连接对方。

连接复用后,延迟或者重发的数据包到达,新的链接通过前面提到的两个时间字段记性判断。复用链接后,这条连接的时间被更新为当前的时间,当旧的连接的数据包到达后,只需要比较数据包的时间是否大于刚才新连接的时间,内核就可以快速的判断出,数据包是丢弃还是接收了。

这个配置,依赖于连接双方,同时需要对 timestamps 的支持。同事,这个配置,仅仅影响作为客户端的连接。 连接服务端时服用 TIME_WAIT 的 socket。(主动连接)

  • net.ipv4.tcp_tw_recycle

销毁掉 TIME_WAIT。

就是内核会快速回收掉处于 TIME_WAIT 的 socket 连接。这个回收时间,通过RTT动态计算出来的,不是 2MSL,而是一个 RTO(retransmission timeout,数据包重传时间) ,这个时间远远小于 2MSL 。

这个配置主要影响到了 inbound 的连接,即作为服务端校色,客户端连接进来,服务端主动关闭了连接, TIME_WAIT 状态的 socket 处于服务端,服务端快速的回收该状态的连接。(被动连接)

由此产生的问题,依旧利用 timestamps 解决。

总结: 作为客户端,主动进行一些短连接, 开启 net.ipv4.tcp_tw_reuse, 而作为服务器,主要提供被动连接,开启 net.ipv4.tcp_tw_recycle 。

  • 具体业务

在比较老的业务下面,如早起的 Django 框架,是不支持连接池的,也就是连接数据库的都是短连接,是产生 TIME_WAIT 的主要原因。

还有就是 CRONTAB 定时任务,由于定时任务执行完了后,进程应该也就销毁了,所有变量都不存在了,每次执行都需要重新连接,每次执行完成都需要进行断开。

解决方法: 对于业务上的,由于依赖关系比较复杂,可以试着从底层加入连接池功能,或者升级 Django 版本(由于稳定性问题,一般是不会升级的),或者自己将 DBUtils 集成进去。

定时任务问题,可以使用专门的定时任务框架,如 APScheduler 框架。

个人认为,与其最开始直接两句话搞定,修改个参数,看似好像也解决了,但是也只是看似,看不到 TIME_WAIT 连接了,但是其实是因为这些连接消失的很快,才看不到而已,并没有从本质上解决问题,也没优化什么性能,优化的只是 Linux 服务器处理的性能。因为每次连接 MySQL 还是需要连接、断开。

所以还是需要在根源找问题,将短连接换成长连接,并且加入连接池来减少连接的创建销毁,来提高响应时间和性能。

再说一个问题:SQLAlchemy长时间未请求,数据库连接会断开,就会导致第一次查询时会报错的,然后第二次就不会报错了。

参考:

系统调优你所不知道的TIME_WAIT和CLOSE_WAIT

SQLAlchemy长时间未请求

在 RESTful 分隔下的 API 中,有的时候,一个聚合查询,参数很长,GET 很有可能超
出浏览器的限制,我记得是 GET 的参数实际上是在协议里是没有限制的。 是浏览器和 web 服务器限制了 URI 的长度。
不同的浏览器和 Web服务器,限制大小是不一样的。

遇到这种 API 的时候,直接无脑改为使用 POST 吗。

其实我也搜了一些资料,最终回到改为 POST 是可以的的结论。

其实只想说明一件事情就是,只对知识保有记忆,而不思考,还不如不知道。

因为知道了 RESTful 里的规定后,只会生硬的硬搬,遇到需要选择的问题的时候,直接回到原始状态。

很多时候,或者解决一个问题,我知道最终的方法也还算是可以的,但是就是不知道为什么,也没有人告诉我。

阅读全文 »

在国内,如果不搭梯子,使用 git clone 会比较慢。一旦遇到很大的仓库需要
clone 的时候,只能静静等待。

其实 git clone 有一些可选参数,可以让我们只 clone 我们需要的内容, 即减少了网络传输, 也省了时间。

  • 不加任何参数

在不加任何参数的情况下,默认就是全部克隆。

并且这样也会把所有远程分支也克隆下来,其实很多分支我们是用不到的。

选择切换到其中的一个远程分支命令, 假设通过 git branch -aremotes/origin/dev 分支:

阅读全文 »

最近,升级了一下 MongoDB之后,突然发现在 终端中输入 mongo,报错如下:

1
Failed global initialization: InvalidSSLConfiguration: Error enumerating certificates: The specified item could not be found in the keychain.

但是使用 Navicat 竟然可以连接,说明有可能是 mongo 这个命令的问题,或者系统的一些设置问题。

看了下报错信息,应该是和 SSL 有关,本来本地的这个 MongoDB 也只是作为开发使用,监听在 127.0.0.1, 感觉也没有必要去大动干戈,去配置一下 SSL。

显示搜了下报错信息,然后搜了下 MongoDB 的 SSL,配 SSL 觉得有点麻烦,暂时还用不到。于是在 stackoverflow 上找到了答案。

Error when trying to obtain a certificate: The specified item could not be found in the keychain

报错信息不太一样,但是问题是同一个问题。

这里直接拿出来正确操作方法:

  1. 打开钥匙串
  2. 选择系统,然后看一下有没有一个叫做: Apple Worldwide Developer Relations Certification Authority 的证书。
  3. 然后确保证书图标右下角有个小 + 号,即信任的意思,我就是因为这个证书虽然在那里,但是我没有信任他,导致一系列麻烦。
  4. 如果还没有信任,双击点开然后选择 信任—- 使用此证书时: 选择始终信任,也会让你输入密码验证。
  5. 证书信任之后,重新打开终端,然后输入 mongo 就可以了,确保MongoDB 服务已经启动。

最近在写自己的端口上测试一些功能的时候,发现偶尔需要登录,偶尔不需要登录,有些情况下,只想看下 api 是否正确,也就没有搭建前端的代码。

经测试: 发现 Cookie 只跟域名有关,不区分协议和端口。

比如你在 127.0.0.1:8080 端口登录了一个网页,如果你的服务端的 session 是放在如 memcache 里面即共享的,那么你再开一个其他端口相同的服务,你会发现状态和8080端口的状态是一样的, 就是因为,浏览器把 8080 的 cookie 也发送到了另一个端口上。因为 Cookie 跟 协议及端口无关。

Cookie 的作用域: Domain 和 Path 。

Domain 指定了哪些主机可以接受 Cookie,如果不指定,默认不包含子域名。如果指定了 Domain, 则一般包含子域名,因此指定 Domain 比省略它的限制要少。但是,当子域名需要共享相关用户信息时,可能会有包住。

例如:

1
2
3
<!--more -->
Domain=mozilla.org
则 Cookie 也包含在子域名中如 dev.mozilla.org

Path 属性:Path 标识了主机下的哪些路径可以接受 Cookie (该 URL 路径必须存在于请求 URL 中),义字符 %x2F 作为分隔符,子路径也会被匹配。例如:设置 Path=/docs, 则以下地址都会匹配:

  • /docs
  • /docs/web/
  • docs/web/http
  • ……

MDN 参考_HTTP_COOKIE

Cookie 一般来说相对比较敏感,如果服务端不想让前端的 js 访问 Cookie 怎么办呢, 其实可以加一个 httponly 字段。

眼过千遍不如手过一遍,最近在复习 MySQL 知识的时候,准备边看边练。
所以,还是理论与实践相结合。

Docker MySQL 指南

搭建 MySQL 主从模式

如果那虚拟机,或者服务器搭建的话,有点麻烦,且不易移植,使用 Docker 就可以了。

由于当时pull 镜像的时候,没有选择版本,所以就默认 MySQL 最新版本,确实问题有点多。

MySQL 版本: 8.0.21

阅读全文 »

物理层

通过比特流进行传输,这里略过。

数据链路层

使用以太网帧进行传输数据。

STP 协议(生成树协议),解决了交换机连接交换机之间的环路问题。由于交换机为了冗余、带宽提升、或者错误连接,难免会产生一个封闭的物理环路,而以太网的转发机制又决定了不能有物理环路,一有环路,那些发给所有主机的包如广播包,就会永远在环路上转圈,到达不了目的地,交换机的CPU飙升。

STP 解决方法wiki百科

VLAN:全程虚拟局域网,由于二层交换机会转发所有的群发内容,可以划分多个虚拟局域网来避免广播内容的安全问题。 vlan 只需要在二层的头上加一个 TAG, 12 位,可划分 4096 个 VLAN。

阅读全文 »

最近,老是在电脑上看一些技术博客以及电子书,看着看着发现了一些优秀的资源,于是想把他们搬运到电子书上面来看。有些知识,并不是看一遍就能烂熟于心,上大学的时候,看了很多书,到找工作的时候,也忘记的差不多了。而我自己是最讨厌去用一些蛮力来记忆知识,我觉得这样不仅自己难受,而且如果不经常用,还是会忘。不管外部环境如何,反正自己不能亏待自己,重要的东西,需要的知识,多看几遍,多理解几遍也就记住了,自己也轻松。所以,就想着把感兴趣的内容放到电子书上,在上下班及中午晚上吃饭的时候,可以拿出来看一看。

已经毕业一年,工作了一年了,而这一年,主要是写一些业务,熟悉一些系统,有很多知识都有一些欠缺了,不看看就会被遗忘了,到时候用的时候自然也想不起来,所以还是需要保持学习。平时,不看书的时候,在上下班的时候,拿着手机,没事的时候也拿着手机,已经感觉到疲乏了,手机上并没有什么事务需要处理,但是就是爱看看,所以还不如找点有意思的事情来做。

从踏进大学的第一天起,我就知道一切靠自己,而最终也取得了比较好的结果。

在计算机领域,学什么? 我反正是完全不靠老是来教,因为教材与实际差的实在太远。与其有这个时间,还不如自己先去找一找优秀的教程及资料看看,然后在去研究一些底层的理论,这样应该会更加容易一些。

今天先是查了一些资料,不同的文档格式转为 kindle 专用格式,这样看的也舒服。

本来 kindle 自带的邮件推送功能,是可以帮你把 PDF 转为 kindle 专用格式的,但是有些内容是 html 格式的,我也照做了一下,发现转来的都是乱码,关于为何是乱码,就没有深究了,毕竟这个东西用的人少,换个思路就行了。

查了一番资料,发现了 calibre 这个软件,确实很不错,各种功能都有,但是唯一一个不太友好的地方在于配置邮件自动发送到kindle ,这样我就不需要再一个一个去打开邮箱编辑内容,上传发送了。但是,难受的是,看了一下教程,发现google 搜到的,十篇又九篇都是跟第一篇一模一样的内容,就是平台不同而已,这个在简书,那个在CSDN,内容是有价值的,但是没有什么讨论,出了问题也不太好解决。

阅读全文 »

内存映射

只有内核才可以直接访问物理内存。Linux 内核给每个进程都提供了一个独立的虚拟地址空间,并且这个地址空间是连续的。这样,进程就可以很方便的访问内存,更确切的说是访问虚拟内存。

虚拟内存地址空间内部又被分为内核空间和用户空间两部分,不同CPU指令可以处理的最大长度的处理器,地址空间范围也不同,32位,最大4G,64位系统都是定义的 128 T,并不是说最大。分别占据内存空间的最高位和最低位,剩下的中间部分是未定义的。

虽然每个进程的地址空间都包含了内核空间,但是这些内核空间,其实关联的都是相同的物理内存。这样,进程从用户态切换到内核态后,就可以很方便的访问内核空间内存。

内存映射,其实就是将虚拟内存地址映射到物理内存地址。为了完成内存映射,内核为每个进程都维护了一张页表,记录虚拟地址与物理地址的映射关系。

页表实际存储在CPU内存管理单元的 MMU 中,这样,正常情况下,处理器就可以直接通过硬件,找出要访问的内存。

当进程访问的虚拟地址在页表中查不到就会产生缺页异常,进入内核空间分配物理内存、更新进程页表,最后再返回用户空间,恢复进程的运行。(仅仅表示进程将要使用某块虚拟内存,但是此时并没有在真正的物理内存上进行创建)

阅读全文 »

CPU

1
2
3
4
5
6
➜  ~ uptime
10:57:12 // 当前时间
up 25 days, 14:38, //系统运行时间
2 users, // 正在登录用户数
load average: 1.03, 0.58, 0.43 // 平均负载
➜ ~

上面是哪个数字分别表示 1分钟、 5分钟、 15分钟的平均负载。

阅读全文 »