压测时本地端口耗尽问题分析

最近在压测的时候遇到一个问题,python requests库在发起HTTP请求的时候,报错:Failed to establish a new connection errno 99。查了一下,这和错误是因为本地TCP端口耗尽导致的。
因为之前通过打开tcp_timestampstcp_tw_reuse选项解决过这个问题了,这次又出现这个问题,我觉得是时候好好梳理一下这个问题的来龙去脉了。

第一个问题,本地端口有哪些可用?

首先,需要了解到TCP协议中确定一条TCP连接有4要素:local IP, local PORT, remote IP, remote PORT。这个四元组应该是唯一的。
在我们发送HTTP请求的时候,local IP remote IP remote PORT是固定的,只有local PORT是可变的,可用的local PORT的数量就限制了client和server之间TCP连接数的数量 …

more ...

字符串拼接的效率

资深一点的python开发者肯定知道如果要把很多小的字符串拼接起来,用''.join的方式效率最高。

最近遇到一个字符串拼接的问题,比较有意思,记录一下。

场景是,在开发thrift序列化库的时候,产生了一个字符串类型的链表,最终要把链表中的字符串拼接成一个整串,并在前面加上整个字符串的长度。

最原始的实现是:

def list_join_and_concat(_list):
    msg = ''.join(_list)
    return pack('!i', len(msg)) + msg

写的过程中发现这里有个字符串相加的操作,而且msg这个字符串对象比较大,担心对效率有影响,因此思考一下是否有别的实现方案。

为了消除字符串相加,想了两个方案:

def list_insert_join(_list):
    length = sum(map(len, _list))
    _list.insert(0, pack('!i', length))
    return ''.join(_list …
more ...

dns相关知识和工具

Authoritative VS non-authoritative

使用nslookup查询域名的时候,经常发现"Non-authoritative answer"这样输出。

那么authoritative/non-authoritative answer分别是什么意思呢?

Authoritative answer:

官方dns server返回的域名解析结果,比如ns1.google.com返回的maps.google.com域名解析的结果(你需要把ns1.google.com配置成你的dns resolver)。

Non-authoritative answer:

非官方dns server返回的域名解析结果,比如你的路由器或者局域网中的dns server返回的maps.google.com域名解析的结果。

参考文档:

  1. meaning of non-authoritative answer
  2. authoritative answer vs non-authoritatice answer

Cache相关

nscd

nscd(name service cache …

more ...

SQLAlchemy数据库连接被关闭

最近在使用SQLAlchemy做一个后台server时,发现server启动一段时间之后偶尔会出现"MySQL has gone away"的错误。

研究了一下发现是因为MySQL默认会关掉空闲的连接,默认是28800秒,即8小时。

而后台server起来之后,如果8小时没有请求没有数据库操作,那么8小时之后原来的数据库连接就被MySQL关闭了,后面SQLAlchemy再使用该连接操作数据库的时候就会报"MySQL has gone away"的错误。

需要解决此问题,只需要在create_engine时加上pool-recycle即可,详细说明可参考官方文档

more ...

mac删除目录需要目录的执行权限

最后,遇到一件有意思的事情,在删除一个非空的可读写目录时,发现竟然删除不了:

mkdir tmp
echo "1" > tmp/1
chmod 666 tmp
rm -fr tmp
rm: tmp/1: Permission denied

看了一下错误提示,是在删除tmp/1文件的提示没有权限了,但是这个文件明明是有可读写权限的。

google了一下发现一个解释:

目录其实也是一个文件,这个文件是其他文件的列表,其读、写、执行权限分别控制:

  1. 读:你可以阅读这个文件列表,可以用ls列出所有文件或者可以使用命令补全来补全文件名
  2. 写:可以修改、增加、删除这个列表上的内容
  3. 执行:可以cd进这个目录,可以访问(读、写、执行)这个目录中的子文件

回想了一下刚才删除失败的原因,应该是rm在删除tmp …

more ...

从forked的项目同步代码

从forked项目的master分支同步代码至fork项目的master分支:

1. git remote add upstream <forked project url>
2. git fetch upstream
3. git checkout master #保证本地处于master分支
4. git merge upstream/master #把forked项目的修改merge到本地
5. git push origin master #把本地修改提交到远端仓库(fork项目)的master分支

从forked项目同步新分支到fork项目:

1. git remote add upstream <forked project url>
2. git fetch upstream
3. git checkout -b newbranch …
more ...

在python列表推导中使用lambda可能遇到的问题

相信python代码写多了人应该都会遇到或见过下面这种情况:

functions = [lambda x: i*x for i in xrange(5)]
[f(2) for f in functions]

输出结果:[8,8,8,8,8],而并不是[0,2,4,6,8]


原因是: 使用lambda生成的匿名函数的时候,并没有立即查找i并求值,在函数被调用时才会查找i并求值,而在函数被调用的时候i的值是4。

比如你定义一个函数,引用一个不存在的变量,在函数定义的时候并不会报错,只有函数被调用的时候才会提示找不到变量:

f = lambda x: nonexist_i*x
f …
more ...

关闭UseDNS来加速ssh登录

最近公司内部几台虚拟器在远程登录的时候特别慢,要等10秒左右才能连接上,开始也没怎么在意,因为公司内部无线网有时候就是很慢。

然后,偶然的机会下,我修改了这几台机器的dns服务器的配置(之前IT调整了dns服务器的ip,这几台远程机器没有我没及时做修改,所以dns服务器配置是错的),发现登录速度又恢复到秒登的级别了。

好奇心使然,我又把dns配置改成之前的,发现登录又要10秒左右了。

我发现这个问题越发有意思了,错误的dns还会影响到ssh的登录过程,之前是万万没想到的。在我的意识里,dns是用来解析域名对应的ip的,我用ssh通过ip来远程登录一台机器,过程中怎么会涉及dns解析呢?

于是做了个实验:先打开一个session,在该session里面一直使用netstat查看网络连接,同时另外一个session发起ssh登录,果然发现在登录过程中有访问dns server的udp连接。

但是为什么ssh登录的过程中为什么会使用到dns呢?google了一圈ssh登录过程,没找到能够解惑的材料。

直接google问题:ssh dns slow

终于找到不少解释,看来这个问题还是蛮普遍的:

http://unix.stackexchange.com/questions/56941/what-is-the-point-of-sshd-usedns-option

http://askubuntu.com/questions/246323/why-does-sshs-password-prompt-take-so-long-to-appear

解决这个问题也很简单 …

more ...

python修改对象从属关系和类继承关系

修改对象从属关系

其实这里想介绍的一个神奇的函数__instancecheck__,它的描述文档在这里

The primary mechanism proposed here is to allow overloading the built-in functions isinstance() and issubclass() . The overloading works as follows: The call isinstance(x, C) first checks whether C._instancecheck_ exists, and if so, calls C._instancecheck_(x) instead of its normal implementation …

more ...

慎用python的__del__方法

python的__del__方法是什么?

和C++的destructor有点类似,在对象销毁的时候,会调用对象的__del__方法,但是,但是有些区别。

有哪些区别呢?

调用时机

在C++里面,对象是用户手工管理的,用户通过显示地调用delete obj来销毁对象obj,对象销毁时对象的destructor会被调用。

而在python里面,用户不需要手工管理对象,python自带的垃圾回收机制会通过引用计数来判断对象是否可以销毁。当一个对象的引用计数为0时,python解析器会自动销毁该对象,同时调用该对象的__del__方法。

也就是说del obj在python里面并不一定会销毁一个对象,只是让该对象的引用计数减一。

"del x" doesn't directly call x.__del__() — the former decrements the reference count for x by …

more ...