2016-12-16 Li Shuai
1. 连接数过高 Redis连接数过高,且没有修改进程能打开的最大文件数,当达到最大文件数限制时,Redis在accept新连接的时候会立即报错"Max open files",无法成功获取该连接,此时,listen socket是持续可读的状态,事件循环直接把CPU跑满。这种现象还可能与没有配置Redis配置文件中的timeout参数有关,不设timeout参数,Redis不会主动关闭僵尸连接,导致连接数越来越高,容易达到限制。 解决方法:1)config set timeout xx修改keeplive时间;2)client kill ip:port杀死一部分僵尸连接;3)修改系统参数,增加单进程能打开的最大文件数。 这一点M 继续阅读 »
2016-08-04 Li Shuai
这里的blocking signal里的blocking并不是传统意义上的针对IO的blocking, 尽管这可能是引起ioloop阻塞的一个原因之一。在这里,blocking指的是ioloop在epoll返回之后开始依次处理各监听文件句柄上的IO事件时,直到下一次进入epoll调用的这段时间的ioloop的状态。我们知道Tornado是单线程的,在处理完某次epoll调用返回的读写就绪事件之前,Tornado无法启动下次epoll监听,所以这段时间理论上是越短越好,这样,ioloop可以充分及时的获取就绪文件句柄,不会影响整体IO性能。然而在实际的使用过程中,难免会出现某次处理时间过长,从而导致ioloop的blocking时间过 继续阅读 »
2016-07-28 Li Shuai
greenlet是Python众多协程实现技术中的一种,eventlet是基于greenlet实现的。而eventlet和libev又是gevent的核心。greenlet的上下文切换清晰易懂,可以结合IO事件循环构建出一些高效的事件处理逻辑。不同于yield类型的上下文切换,greenlet的上下文切换从表现形式上看更纯粹,可以直接switch到另一个greenlet,不用管目标greenlet是否已经在运行,不同greenlet之间处于完全对等的状态,可以相互switch。基于yield实现的协程往往只能切换回自己的直接或间接调用者,要想在嵌套的调用中切换出去是比较麻烦的。本质上是因为yield只能保留栈顶的帧,Python3对 继续阅读 »
2016-06-06 Li Shuai
最不喜欢在Tornado中使用任何同步阻塞型的东西,不想让ioloop阻塞在某个IO调用上,因为单线程的东西任何阻塞都是代价很高的,除非你的数据库被优化的性能很好,速度很快。除了之前的线程池之外,直接使用异步库也是不错的选择,Motor就是Tornado里可以用的很好的异步库,它兼容Tornado的gen.coroutine式的异步调用形式,主要使用了greenlet来巧妙的封装PyMongo的同步API, 把底层的socketIO进行了异步化的处理,化同步为异步。 从使用的例子来分析Motor是如何把PyMongo的API异步化的: client = motor.MotorClient(...) db = client['te 继续阅读 »
2016-05-25 Li Shuai
也许你会偶然发现Python的多线程程序使用Ctrl-C杀不掉,必须拿到pid用kill -9才能干掉,研究这个问题的原因可以使得对Python多线程的信号处理及线程的退出机制有更好的理解。 假如有一个Python写成的用多线程模拟生产者-消费者的程序,代码如下: class Producer(threading.Thread): def run(self): global count while True: if cond.acquire(): if count > 1000: cond.wa 继续阅读 »
2016-04-28 Li Shuai
写Python多线程都知道当前线程调用a.join()后,会阻塞直到线程a运行结束,看了一下threading模块的源码, 了解了一下实现的原理。 每一个新开启的线程内部都维护着一个Condition类型的条件变量,对线程a进行join(),其实是wait()在线程a 内部的条件变量上,当线程a执行结束时,会通过notify_all()通知所有join()的线程,则阻塞的线程被唤醒,恢复执行。 以下是源码: self.__block = Condition(Lock()) #线程内部维护的Contition变量 def __stop(self): if not hasattr(self, '_Thread__block 继续阅读 »
2016-04-27 Li Shuai
GIL 熟悉Python的人对GIL这货可定都不陌生, 全局解释器锁(Global Interpreter Lock)简称GIL, 这货是 Python多线程的核心机制。由于Python的线程实际是操作系统的原生线程,多个线程同时执行同一段字节码可能 会导致很多问题(比如: 内存管理的引用计数需要线程安全机制的保护),于是使用GIL这把大锁锁住其他线程,保 证同一时刻只有一个线程可以解释执行字节码。关于GIL的更多分析, 可以看David Beazley大神的研究。本文主要分析下CPython的GIL在Linux上 基于pthread的实现细节,看完这些源码后能够对GIL有更深入的理解。 GIL的定义 有人可能会想,从GIL的 继续阅读 »
2016-04-07 Li Shuai
Tornado本身的设计目标是单线程异步非阻塞,要想很好的发挥它的性能最好使用异步IO,并且Tornado本身也提供了异步的AsyncHttpClient的实现,配合gen.coroutine和yield,可以让请求异步执行从而不阻塞当前线程,对于单线程服务器来说,阻塞(blocking)和同步的sleep这种会挂起线程的动作都是服务器的噩梦,因为只有一个线程,所以任何等待都会影响服务器对于其他请求的处理。 异步非阻塞对于第三方IO是http请求的情况还好,毕竟可以使用Tornado提供的异步实现,但是对于有些数据库的IO,则需要异步库的支持,比如针对MongoDB的Motor等。但是第三方异步库的质量也是参差不齐,在实际的工程中 继续阅读 »
2016-03-29 Li Shuai
每次进入应用客户端时,都需要进行后端鉴权服务,接口会调用某牌照方的鉴权接口,根据用户的MAC地址决定用户是否有权限登陆使用服务。由于调用的接口不是很稳定,有时会出现连续一段时间误判,导致终端大量用户无法使用APP,所以决定在接口这边做一个策略: 统计一段时间内的第三方鉴权接口鉴权失败数量,当超过某一阈值时,接口暂时对用户请求返回成功。 由于只是周期性的计数,比如十分钟,所以当第三方服务异常,连续大量用户请求失败的时候,只要同时做好监控报警工作,及时上报给第三方,同时,并不影响用户使用服务。第三方发现后可以及时处理,处理正常后,接口又可以继续以牌照方的响应为准,所以基本也不违背广电总局可管可控的原则。 这种周期性计数功能,使用R 继续阅读 »
2016-03-11 Li Shuai
延迟绑定 Python闭包函数所引用的外部自由变量是延迟绑定的。 In [2]: def multipliers(): ...: return [lambda x: i * x for i in range(4)] In [3]: print [m(2) for m in multipliers()] [6, 6, 6, 6] 如以上代码: i是闭包函数引用的外部作用域的自由变量, 只有在内部函数被调用的时候才会搜索变量i的值, 由于循环已结束, i指向最终值3, 所以各函数调用都得到了相同的结果。 解决方法: 1) 生成闭包函数的时候立即绑定(使用函数形参的默认值): In [5]: def multip 继续阅读 »