这个 TCP 问题你得懂:Cannot assign requested address
原文链接: 这个 TCP 问题你得懂:Cannot assign requested address
微信群里一阵骚动,响声震天。
我心想,虽然是周五,并且到了下班点,但也不至于这么兴奋吧。
打开微信一看,心凉半截,全是报系统 403
错误的消息。别说下班了,怕是老板会让我永远下班吧。
别慌,在长期的团队协作训练中,我明白了一个道理:稳住我们能赢。
冷静下来之后,我仔细分析了一下问题的原因。
403
说明权限不足,也就是说我们的子系统到鉴权中心拉取权限失败了。直接登录到子系统服务器,手动执行拉取权限程序,的确是拉不到。
我的推测没有问题,难道网络不通了?telnet
目标端口试试,然后 Linux 给我返回了这个错误信息:
Cannot assign requested address
原因
产生这个错误的原因是由于 Linux 分配的客户端连接端口用尽,无法建立 socket 连接导致的。
我们都知道,建立一个连接需要四个部分:目标 IP,目标端口,客户端 IP 和客户端端口。其中前三项是不变的,只有客户端端口不断变化。
那么在大量频繁建立连接时,而端口又不是立即释放,默认是 60s,就会出现客户端端口不够用的情况。
这就是这个问题的本质。
接下来使用两个命令来验证一下:
查看连接数:
查看可用端口范围:
结果就是连接数是远大于可用端口数的。
解决
怎么解决呢?有两个方案:
调低 TIME_WAIT 时间
调高可用端口范围
调低 TIME_WAIT 时间
编辑内核文件 /etc/sysctl.conf
,增加以下内容:
调高可用端口范围
编辑内核文件 /etc/sysctl.conf
,增加以下内容:
最后,执行 sysctl -p
使参数生效。
复盘
我通过增加可用端口范围,顺利将问题解决,看来可以正常下班了。
但是还没完,为什么会突然有这么多连接呢?通过分析日志发现,过去一段时间,我的一个同事疯狂请求系统接口,应该就是这个操作引起的,问了一下原来是在爬数据。好家伙直接来硬的,找我提供一个 API 不香吗?
不过这也反应了我们的系统也太过脆弱,一个小爬虫就给搞挂了。分析了线上代码,觉得有三个地方应该优化:
每次请求鉴权中心不应该都建立新的连接,而是应该复用之前的连接,比如单例模式;
权限相对来说是变化不频繁的,子系统应该建立本地缓存,而不是每次实时请求;
不止要对
POST
接口设置频率限制,GET
接口也应该限制。
剩下的事就是优化代码了,不过,先开心的过个周末再说。
文章中的脑图和源码都上传到了 GitHub,有需要的同学可自行下载。
地址: https://github.com/yongxinz/tech-blog
关注公众号 AlwaysBeta,回复「goebook」领取 Go 编程经典书籍。
Go 专栏文章列表:
版权声明: 本文为 InfoQ 作者【AlwaysBeta】的原创文章。
原文链接:【http://xie.infoq.cn/article/7bbc2fcee3f980a8cd4f412cb】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论