蒋云鹏: 在?
CMGS: 在
蒋云鹏: 感觉gtalk真实难用啊
输入框只有一行。
CMGS: = =
用久了irc表示朴素实在= =
蒋云鹏: 所以你们都通过文字来沟通,没有截图的需求?
CMGS: 有,丢pastebin。。
内部的系统支持
已发送(15:23,星期四 )
蒋云鹏: 嗯,之前我也问过hongqn ,他告诉我: 1. gevent是一个坑,用要谨慎。2. 直接用gunicorn的同步模式就能抗住大流量的web网站,豆瓣就是直接用gu的同步模式。但我对这个不是很理解,现实是我们的python 应用gunicorn+gevent连每秒50个请求都扛不住。
CMGS: 这个很简单。。。
蒋云鹏: 如果不利用gevent的自动切换,用同步模式性能更差。
CMGS: gevent确实是个坑。。不过可以避免
同步模式你们得做个profile,看性能到底是跨在哪里
单进程单线程负责一个请求,性能更差表明业务上耗时比较大,是耗在IO还是CPU上了这得先有个方向才能分析
Gevent同样,Gevent的话普通默认切换仅仅针对于socket IO,也就是请求
但是。。
某些C library会限制Gevent的能力
蒋云鹏: 如果同步模式,你们gunicorn会开多少个worker?hongqn说:“豆瓣现在的规模,单台应用服务器需要承受的并发量也就不到一百这个量级,也就是说不到一百个进程。用多进程单线程模型,由于不涉及到多线程编程,代码易编写且不容易出错,调试管理都比较容易。”单机开一百个进程根本不显示吧
CMGS: 一百个不算多啊……因为业务确实简单
我们这边『额外』IO无外乎就是MySQL, memcache,beansdb
MySQL,基本是短链接,没slow sql
而且读的话大多都过memcache了
memcached走的是libmemcached,所以性能能保证
beansdb一样,走的也是libmemcached
蒋云鹏: 我们之前是通过ab压测,一般也没有什么性能瓶颈,只是sql会比较耗时。主要我们也不知道python怎么能看到慢在哪个线程栈上。
CMGS: 这个简单,你在线程开始和结束的时候计算下时间,然后写个日志
比如线程A,输出A.name cost time,就一目了然了
蒋云鹏: 那其实我也可以在每个请求打印一下耗时。
CMGS: 也可以~我个人推测啊
蒋云鹏: java可以有工具能实时看到线程的方法调用情况,从a-->b-->c,python貌似没有这种工具
CMGS: 你们是挂在MYSQL IO上
python似乎也有这么一个。。之前在python news上看到过
蒋云鹏: 我们挂的时候日志显示:WORKER TIMEOUT (pid:20655)
2013-04-05 16:30:49 [12606] [CRITICAL] WORKER TIMEOUT (pid:20655)
WORKER TIMEOUT (pid:20655)
2013-04-05 16:30:50 [12606] [CRITICAL] WORKER TIMEOUT (pid:20655)
WORKER TIMEOUT (pid:20706)
2013-04-05 16:30:50 [12606] [CRITICAL] WORKER TIMEOUT (pid:20706)
WORKER TIMEOUT (pid:20706)
2013-04-05 16:30:50 [12606] [CRITICAL] WORKER TIMEOUT (pid:20706)
Booting worker with pid: 20761
2013-04-05 16:30:50 [20761] [INFO] Booting worker with pid: 20761
WORKER TIMEOUT (pid:20706)
2013-04-05 16:30:51 [12606] [CRITICAL] WORKER TIMEOUT (pid:20706)
WORKER TIMEOUT (pid:20718)
2013-04-05 16:30:51 [12606] [CRITICAL] WORKER TIMEOUT (pid:20718)
WORKER TIMEOUT (pid:20718)
2013-04-05 16:30:51 [12606] [CRITICAL] WORKER TIMEOUT (pid:20718)
Booting worker with pid: 20767
2013-04-05 16:30:51 [20767] [INFO] Booting worker with pid: 20767
WORKER TIMEOUT (pid:20718)
2013-04-05 16:30:52 [12606] [CRITICAL] WORKER TIMEOUT (pid:20718)
Booting worker with pid: 20773
另外会有connection泄漏,比如redis ,memcached,的connection单机达到几百上千。
CMGS: 这是很明显的同步30秒超时
你在同步模式下也会这样?
蒋云鹏: 我们现在的配置如下:
25084 ? Ss 0:14 /duitang/dist/sys/python/bin/python /duitang/dist/sys/python/bin/supervisord -c /duitang/dist/conf/supervisord.conf
25215 ? S 0:05 \_ /duitang/dist/sys/python/bin/python /duitang/dist/sys/python/bin/gunicorn_django -b 0.0.0.0:7199 -k gevent -w 16 --grace
7882 ? Rl 13:23 \_ /duitang/dist/sys/python/bin/python /duitang/dist/sys/python/bin/gunicorn_django -b 0.0.0.0:7199 -k gevent -w 16 --g
7948 ? Sl 20:02 \_ /duitang/dist/sys/python/bin/python /duitang/dist/sys/python/bin/gunicorn_django -b 0.0.0.0:7199 -k gevent -w 16 --g
7985 ? Sl 22:14 \_ /duitang/dist/sys/python/bin/python /duitang/dist/sys/python/bin/gunicorn_django -b 0.0.0.0:7199 -k gevent -w 16 --g
8050 ? Rl 30:50 \_ /duitang/dist/sys/python/bin/python /duitang/dist/sys/python/bin/gunicorn_django -b 0.0.0.0:7199 -k gevent -w 16 --g
8067 ? Sl 33:36 \_ /duitang/dist/sys/python/bin/python /duitang/dist/sys/python/bin/gunicorn_django -b 0.0.0.0:7199 -k gevent -w 16 --g
8137 ? Rl 34:06 \_ /duitang/dist/sys/python/bin/python /duitang/dist/sys/python/bin/gunicorn_django -b 0.0.0.0:7199 -k gevent -w 16 --g
8180 ? Sl 51:51 \_ /duitang/dist/sys/python/bin/python /duitang/dist/sys/python/bin/gunicorn_django -b 0.0.0.0:7199 -k gevent -w 16 --g
8188 ? Sl 73:50 \_ /duitang/dist/sys/python/bin/python /duitang/dist/sys/python/bin/gunicorn_django -b 0.0.0.0:7199 -k gevent -w 16 --g
8195 ? Rl 84:42 \_ /duitang/dist/sys/python/bin/python /duitang/dist/sys/python/bin/gunicorn_django -b 0.0.0.0:7199 -k gevent -w 16 --g
8203 ? Rl 84:14 \_ /duitang/dist/sys/python/bin/python /duitang/dist/sys/python/bin/gunicorn_django -b 0.0.0.0:7199 -k gevent -w 16 --g
8220 ? Sl 113:50 \_ /duitang/dist/sys/python/bin/python /duitang/dist/sys/python/bin/gunicorn_django -b 0.0.0.0:7199 -k gevent -w 16 --g
8227 ? Sl 131:36 \_ /duitang/dist/sys/python/bin/python /duitang/dist/sys/python/bin/gunicorn_django -b 0.0.0.0:7199 -k gevent -w 16 --g
8234 ? Rl 147:07 \_ /duitang/dist/sys/python/bin/python /duitang/dist/sys/python/bin/gunicorn_django -b 0.0.0.0:7199 -k gevent -w 16 --g
12541 ? Sl 18:39 \_ /duitang/dist/sys/python/bin/python /duitang/dist/sys/python/bin/gunicorn_django -b 0.0.0.0:7199 -k gevent -w 16 --g
12548 ? Sl 18:53 \_ /duitang/dist/sys/python/bin/python /duitang/dist/sys/python/bin/gunicorn_django -b 0.0.0.0:7199 -k gevent -w 16 --g
13231 ? Sl 13:10 \_ /duitang/dist/sys/python/bin/python /duitang/dist/sys/python/bin/gunicorn_django -b 0.0.0.0:7199 -k gevent -w 16 --g
CMGS: 哈明白了
蒋云鹏: 如果开同步模式,线上马上就出现这种错误
CMGS: 你看你用了Gevent
gunicorn的实现里面
WORKER TIMEOUT 意味着
timeout这么点时间里面没有任何输出
问题就来了。。
gevent的那个worker实现你看源码就知道
所以,一旦出现timout
timeout
意味着。。
某个地方block住了
而十之八九。。。是你们的MySQL
django的ORM。。。一如既往的屎= =
生成各种SLOW SQL来着
蒋云鹏: gevent不是会自动切换吗?
是的我们用了django ORM。
之前还开了事务
CMGS: nonono。。。
这就错了
MySQL这东西啊,在gevent下就是个巨坑
蒋云鹏: 哦,我之前大概知道mysql 的驱动用C写的,不会切换
CMGS: 没错!
蒋云鹏: 不过豆瓣开源的cmemcached 也是用c写的,也不会切换。
CMGS: no。。。
那只是我们开源的。。
有2种方案。。
1是我修正过后的PyMYSQL。。。
因为官方的PyMYSQL已经不支持1.4以上的了
因为django啊。。1.4用了内部接口= =
https://github.com/petehunt/PyMySQL/pull/106
我只是很纳闷。。。官方还不merge我的货= =
另外一种方案就是= =
https://github.com/hongqn/greenify
这个方案需要自行修改mysql-connect-c
然后重新编译
豆瓣用的就是后者
蒋云鹏: 你说的这两套方案都是通过修改mysql的驱动做到能 利用gevent的自动切换,但如果是gunicorn同步模式就没有这个必要了对吧?
CMGS: 没错
同步模式都超时。。。只能说明。。。不要直接OOXX数据库呐
蒋云鹏: 好,所以我先问问gu的同步模式,所以同步模式不能有远程IO操作?
CMGS: 能有
但是明显你的IO是瓶颈
可以通过比如缓存啊或者其他东西解决掉
从我使用经验来看。。gevent虽然坑多,但是只要避免了这些坑还是蛮好用的
性能当然不比静态的JVM,但胜在开发效率和并发支撑和运维上
node.js吧,并发大概是gevent2倍强力,运维就惨烈了,而且是callback hell
coroutine还是很好的
蒋云鹏: 唉,我怎么截图给你?
CMGS: 这。。。有droplr没= =
蒋云鹏: 想给你看一下我们监控图,s9的gu的load就是下不去,唉。
CMGS: =。=
搞得我都想去你们公司做优化了= =
蒋云鹏: 来吧,来吧,先来兼职也行。
CMGS: 你们这个啊,性能上的原因大概还是和业务有关
蒋云鹏: 我们业务绝对比豆瓣还简单
CMGS: WORKER TIMEOUT 这种事表明一定有个大的block code
业务是简单,但在架构上可能有热点
读写数据库那一块还是走的django的东西么
蒋云鹏: 堆糖网,就是发图片,不过我分析原因有几点。1.middle默认开启了事务,我发现错误日志有死锁。
2. 我们有外站抓取图片,通过后台多线程实现
CMGS: 事物!
这就能理解了
蒋云鹏: 但python这个线程啊,有多种实现,在gevent和uwsgi下的表现都不一样, 比java复杂多了。
CMGS: 抓取是写,和你网站读是并发执行的是吧
蒋云鹏: 我不知道一个异步的daemon线程不会不会lock住work
CMGS: 唉,简单了
会
GIL表示。。别想多线程
蒋云鹏: 擦。。。
CMGS: GIL的颗粒度蛮低,但不表明这货不会lock住daemon。。。
所以python界简单粗暴的方案一如既往的都是开进程
蒋云鹏: 开进程,多开启一些进程是吧?
这个是否需要在linux做一些调整配置
我们之前尝试过效果不好
CMGS: 进程就是消耗高点咯。。
如果你们的爬虫对性能比较敏感。。。
其实直接撸C or node.js or java 会好些
python的话就是无脑单进程单线程了。。一个进程跑1个线程去爬,内存占用上难看点
其实也没多大的事
另外就是锁表的问题。。这得DBA介入看怎么弄比较好
一种方案就是,写有优先,读优先读缓存,写锁了写完之后清理缓存或者更新缓存
蒋云鹏: 嗯,我是不建议开事务的,这个是早起做架构的同学弄的。
CMGS: 坏处就是可能有脏数据
蒋云鹏: 其实我给你说,我们现在切了80%的流量到jython,根本没有做太多的优化
CMGS: 开事物其实没多大的事。。但还是要跟着业务来。。
jython的好处太多了。。
蒋云鹏: 我们的写根本不是瓶颈
CMGS: 不不,这个问题在于,jython的server的实现和IO的并发要比Cpython好太多
蒋云鹏: python 如果要有好的并发 全靠gevent是吧?
CMGS: 是的。。
jvm里面线程是真并发。。。
这和动态语言是不同的。。
没GIL
换句话就是说,丫的不存在写瓶颈,在应用这一层
回头能不能抗住那是后端MySQL的事。。。
CPython就是卡在这种坑上
蒋云鹏: 所以gunicorn的同步模式的价值在于什么地方? 这个并发能力是最弱的,一条slow sql, 一个redis 卡住就把一个python进程给搞挂了。
价值在于简单粗暴,用多进程来抗吗?
CMGS: 同步简单……开发效率高,不用考虑任何线程安全/callback就能把代码写好,最符合人类逻辑
豆瓣一个同步进程的时间消耗如果是10ms的话,单进程RPS也可以达到100的
所以意思就是100个并发同时来,最慢的那个响应时间也就1秒罢了
关键就在于同步模式对业务架构上的要求会比较高点
蒋云鹏: 要求必须所有的点都快。
CMGS: 没错
gevent的好处就是单进程并发可以高,前提是所有的IO都能switch
蒋云鹏: 总结一下,web应用作为request/response,本来就是同步模式,代码开发就应该是同步的。
1. 同步代码 + gunicorn 同步模式
代码可维护,并发能力差
2. 异步代码,比如tornado,node.sj
自己写callback,代码不易维护
3. 同步代码+ gevent
代码可维护,并发能力好,但gevent有坑要踩
4. 同步代码+ jvm
代码维护,并发能力好
CMGS: 差不多
jvm的话其实你也要考虑编译成本。。
还有线程安全~
蒋云鹏: 嗯,不过可以规避,jython可以无需编译,线程安全可以通过不要共享变量。一般都不会,我觉得最关键的是jvm有稳定高效的tomcat和jetty服务器。
刚才你是10ms,qps=100,但很难做到一个请求能在10ms搞定,我们之前python一个about页面也要10ms。
CMGS: http://d.pr/i/aTmm
这是目前首页的值
350ms,单进程1秒大概能搞3个。。
蒋云鹏: 350ms 性能算很差吧
CMGS: 这是首页= =各种动态内容
蒋云鹏: 那你们需要好多机器来抗
之前我们一个detail页面--》http://www.duitang.com/people/mblog/81716155/detail/ 是300ms,后来迁移到jython耗时变成30ms
一天有千万PV的detail页面访问
CMGS: 这很好理解。。jvm的性能战翻Cpython还是很容易的
我们大概就是百来台吧
蒋云鹏: 这个吞吐量上能节约很多机器,现在我们2000千万PV才2台web server。
CMGS: 恩= =python这货。。。
就得压榨。。。
蒋云鹏: 现在python国内成功案例也就只有豆瓣和知乎吧?
CMGS: 恩,豆瓣其实服务器用得也是蛮少的
蒋云鹏: 不知道知乎他们用tornado怎么样。
CMGS: tornado不是不行。。只是我讨厌callback hell
蒋云鹏: 是的,这个相当于全部自己控制了。我现在体验豆瓣和知乎,感觉页面耗时都是100ms以上的。
吞吐量= 1000/单个耗时 *并发能力,如果单个页面耗时都很慢的话那会拖累整体性能,需要很多机器吧。
CMGS: 那是业务复杂导致
其实不然。。
多开几个进程呗
理论上进程=CPU*2
蒋云鹏: 我们cpu才8个core。
CMGS: = =
蒋云鹏: 我们都是1万的廉价机,最近还去淘宝上买2收服务器。
CMGS: 16个果然不够看的= =
。。。。
这。。
蒋云鹏: 你们cpu一般有几个core?
所以开100个进程不适合我们,是吧?
哈哈
CMGS: 上次看了一台是12+HT=24来着
似乎是这样,记得不是很清楚了
不过话说回来,你的用况下面
用jvm成本控制上会更好
在简单业务的前提下,用人成本也不会涨太多
已发送(16:24,星期四 )
蒋云鹏: 这是我们线上情况load:s9: http://cdn.duitang.com/uploads/item/201306/20/20130620162422_uWACV.png
s1:http://cdn.duitang.com/uploads/item/201306/20/20130620162338_idHYe.png
s1是jython ,s9是python。
CMGS: 。。。从我的角度来看。。
S9的数据表现其实还好些
蒋云鹏: 嗯,你是怎么解读的?
CMGS: 20G内存消耗,均负载6左右
对比S1,15G内存消耗,均负载4左右
CPU使用率上也比S1要凶残一些
简单的结论就是,S1的瓶颈卡在了内存,CPU没喂饱
蒋云鹏: 嗯,s1总内存只有16G, 已经有部分进swap了。
CMGS: 这2机器如果是同等流量的话
S9的表现其实更健壮
蒋云鹏: [admin@server9 ~]$ cat /duitang/logs/usr/www.duitang.nginx.access.log | grep ".10:7199" | grep "2013:16:30:55" | wc -l
8
[admin@server9 ~]$ cat /duitang/logs/usr/www.duitang.nginx.access.log | grep ".2:8890" | grep "2013:16:30:55" | wc -l
63
上面这个数据*2.5
10:7199 是s9; 2:8890 是s1
CMGS: 恩,虚拟机的差距啊= =
我觉得应用层上面还有很大的优化空间
蒋云鹏: 我这边后续python优化方案:
1. 事务关掉。
2. 不在web server中抓取外站图片。
3. 不在web server中使用异步线程。
CMGS: 从后续的scale上来看,jvm也是撑得了一时撑不了一世唉……
web server你可以起单独的后台运算进程去干抓取的事情
比如要在一个操作中跨表操作,没事物很容易不一致
蒋云鹏: 不回啊,jvm可以横向扩展,以后加机器就行了。
以后加上百台JVM机器就行了。
CMGS: 不是,我的意思是,单机的性能有多方面提高的可能
一般来说就是分应用层和服务层
你这相当于是换了服务层的东西
自然短期收益是巨大的,但是服务层换来换去就那么几个选择,再遇到瓶颈时候一样的要OOXX应用层
蒋云鹏: 你说的服务层是指类似soa服务框架这种东西?
其实刚才还有一个问题,豆瓣有上百台web server, mysql 的connection够用吗?
CMGS: 够用
对,后期还要拆soa啥的= =
我们有连接池
github.com/CMGS/pool
蒋云鹏: python多进程很浪费connection,之前我们5台web server就用了300个mysql connection。
CMGS: lol 是的
如果我写的话
告诉你个方案
反正是一个请求一个进程对吧
学PHP的
请求来的时候生成连接
请求走的时候释放连接
蒋云鹏: 嗯,这个之前java社区也搞过,不过是进程变成了线程。
如果每个进程都在服务,进程数=打开的mysql connection。还是有点浪费
我们现在有一套服务化框架,jython/python <---> server center <----> mysql
已发送(16:44,星期四 )
蒋云鹏: server center是java写的,利用java的dbcp pool,并且server center不会做template渲染这些事情,所以server center需要的机器远远小于web server, 这样就不浪费mysql的connection了。
CMGS: 这样也行~
就是看你能否接收每次proxy的性能损失了~
server center其实就是第三方的连接池啊= =
我们这边反正gevent加持。。再搞个pool,一个进程就是个连接池了= =
蒋云鹏: 是的,是第三方的connection;不过这个connection很廉价,是无状态的,且容易扩展。
所以最后总结一下,python的最佳实践是:
1. gunicorn 同步模式+ 多开线程
2. gunicorn + gevent,前提把mysql的坑补上
CMGS: 恩是的,不仅仅是mysql的坑
比如你看cmemcached吧
这货也是C socket
也需要填坑,不过由于memcached的速度极快,只有在极高并发的情况下才会凸现问题
蒋云鹏: 嗯,还有不要用django,呵呵。
CMGS: django 太evil了。。真心= =
蒋云鹏: 我们还用了sentry记录日志,不知道这个是不是也是瓶颈
我一直不喜欢这种远程记录日志的方式
CMGS: sentry很赞。。。
前提是。。。不要被它block
我也用这个
豆瓣的做法是单独起线程去sentry汇报
做个middleware
有了unhandle的exception再开线程去异步汇报
蒋云鹏: 哦,但这个异步线程也有可能会lock住整个进程对吧?
后续等你有空,看你是否有兴趣帮我们看看我们python应用情况。
已发送(16:58,星期四 )
蒋云鹏: 刚才掉线了,不知道我发的消息你收到没有。。。
CMGS: 最后一句是哦,但这个异步线程也有可能会lock住整个进程对吧?
4:58
后续等你有空,看你是否有兴趣帮我们看看我们python应用情况。
我的意见是GIL的话lock不lock其实界定起来比较麻烦
涉及到Cpython的设计神马的
蒋云鹏: 嗯 ,了解,所以个人觉得要把python用好了,真的挺难的 。
已发送(17:42,星期四 )
CMGS: 恩,入门容易进阶难