别心急

Leave a comment

工作时节奏快些,其它的时间,让自己慢些,调整自己的快慢节奏。

下午又困在了 apache 支持 python 上,整了一会儿有些心急了。这周暂时先不用 Django 开发,学习使用 PHP 的 Yii 框架和 Javascript 的 backbone.js 做开发。先完成功能,再完善。

OSI 七层模型的理解

Leave a comment

今晚看 《TCP/IP详解 卷1:协议》(英文版·第2版)时,才对 OSI 七层模型有了更深入直观的理解。

OSI 把网络通讯分为七种层级:

  • Physical 层规定了 bit 是如何在线路上传输的
  • Data Link 层规定了两个设备如何在同一种线路上进行数据传输
  • Network 层规定了不同的网络之间是如何进行数据传输的,本质上是不同的终端如何交换数据
  • Transport 层规定了不同终端上的程序是如何交换数据的
  • Session 层规定了不同终端上的程序的不同功能持续交互时的操作
  • Presentation 层规定了数据的编码之类的表示方法
  • Application 层规定了用户如何完成所需的网络功能

Physical、Data Link、Network 这三层很多都是在网络设备上实现的,剩下的几层由终端设备来实现。

一般情况下,交换机只实现 Physical 和 Data Link 两层的协议,即主要用来连接同一线路上的设备进行数据传输。路由器一般只实现 Physical、Data Link 和 Network 三层的协议,一般有两个网络接口,即主要用来连接不同的网络,在不同的网络之间传输数据。但当前的交换机和路由器一般也实现了其它几层的协议,功能更为强大。

可通过 《TCP/IP详解 卷1:协议》(英文版·第2版)的 Figure1-4 来理解以上概念。

对于 “Multiplexing” 和 “Demultiplexing” 可以这样理解,”Multiplexing” 就是可通过一条线路传输多种不同类型的数据,”Demultiplexing” 就是把一条线路上的不同类型的数据传输到不同的线路中,具体参考 http://zh.wikipedia.org/wiki/%E5%A4%9A%E8%B7%AF%E5%A4%8D%E7%94%A8

python-cn 邮件列表上关于同步/异步、框架的选择、协程等的讨论

Leave a comment

下面这些内容,是从  python-cn 邮件列表上对上述问题讨论的一个摘录,认真学习下其中的思想,研究下提到的技术,对个人成长挺有帮助的。

特别赞成 shell 提到的关于学习框架的观点,不要纠结于选择哪个框架,不如把纠结的时间用去学这些框架,多学几个又何妨。不要把自己限定死了某个框架范围内,多学多用就是了。

《深入理解计算机系统》《TCP/IP 详解》(三卷)《Unix 网络编程》《Unix 环境高级编程》这几本书,要多读几遍,很多底层方面的问题都可以通过这几本书去理解和解决。

 

* 邮件地址
https://groups.google.com/forum/?hl=en&fromgroups=#!topic/python-cn/poF9LmXJXl0%5B1-25%5D
* Sager
公司属于二次创业,做新产品研发,使用到的技术主要有以下方面:
Python/ZeroMQ/MessagePack/Flask/SQLAlchemy/MySQL/ExtJS/C/C++等,我相信有技术敏
感度的兄弟一定能胜任这些工作。

我们的领域相对特殊,属于运维领域,整个运维领域发展相对互联网要慢很多,发展慢意味
着可以用成熟、先进的理念和技术去打拼,可以慢慢去打磨我们的产品,我们可以做出领域
中最好的产品。

我们的公司在该领域有7年的积累,也有几百家客户,因此我们的新产品不是从零开始,而
是从这些优质客户的真实需求中提炼出来的需求,所以我们的产品更接近客户,因为我们更
了解客户。

公司的氛围还好,年轻人在一起,轻松、活泼、开放、自由,公司的办公环境还行,离滨
江第一站地铁走路5分钟,离星光大道走路5分钟,窗外的钱塘江一览无遗,五A级写字楼
外加绿城的物业服务.

* jjx
哈,技术堆栈我喜欢,不过把falsk->tornado或cyclone,mysql替换为postgrsql就完全和
我现在所用的一样了
* gelin yan
flask 和 cyclone 好像没什么冲突,也有人把它放在上面用。
* yunfan
赞成mysql->pgsql flask本身设计时就考虑规模变大以后组件一个个替换掉 所以我感
觉这点似乎更胜tornado一筹 没必要换成那种
* Zephyr
tornado 大小通吃。它本身提供的东西就少,没有换掉的问题。更重要是它可以直接集成非HTTP协议的东西。
不过,要说规模变大之后,怎么是替换掉呢,难道不是分离服务,去做一个又一个的新服务么。
* jjx
的确,tornado 就是个简版的twisted,不过twisted现在有cyclone了。我正在逐步转移
tornado项目了。其实现在规模大了无非就是异步,协程之类,所以我现在主张一开始就
上twisted/tornado/gevent之类的,而不是以后再替换之类的
* Shell Xu
我奇怪一下,为什么大部分的人上来都在看是tornado,flask还是twisted?这个压根不
是关键。

爱用什么用什么吧,只要代码够趟风,用tornado还是gevent跑,还是nginx+uwsgi,还是
apache+mod_wsgi,或者用werkzeug,换起来有很困难么?都是wsgi。

反倒是zeromq,在架构上打算干些什么事情,值得猜测一下。我可以先说一下,不是和C
通讯的。

另外,我们其实也不招python程序员。我们的招聘中从来都宣称,我们寻求优秀的程序员。
如果在python社区发,只能说明python是加分项。其实C/C++/go/lua/scheme都是加分
项。。。你们可以接着猜我们要干吗。。。

mysql -> pgsql的还算靠谱,不过用mysql是有原因的。
* Ins
我随便猜猜。

不为了和C通讯,为了给客户提供publish, subscribe的服务,或者内部使用需要到这样
的服务?

用Mysql的原因是历史遗留以及其它xxx各种原因?
* Zephyr
我只是单纯针对tornado说一些东西,完全没有什么猜测楼主系统结构的意思。

其次,我觉得 tornado , gevent 这些与 apache + mod_wsgi, nginx + uwsgi等 wsgi
方式 是可以有很大区别的,单进程的方式可以做很多有趣且有用的事(比如某些条件下
你可以把队列,缓存这些服务都省了)。

最后,就我个人而言,如果我在找一份工作,其实是很在乎系统中是重用 tornado 还是
django,jQuery 或 Dojo 或 ExtJs 什么的。个人喜好问题。因为这可能意味着你需要在
自己不喜欢的环境下工作(至少工作不短的一段时间,才有可能去改变它)。
* Shell Xu
那用rest更好。
* 依云
tornado 只是提供了一个 wsgi 兼容而已,用那个的话根本无法进行异步操作。而
且在 tornado/gevent 这些异步网络框架里可以自己开连接干其它的事,比如
IRC、XMPP、inotify 等。Django 这种专用框架就不适合了。

至于 ExtJs 么,我觉得它更像 Java 而不是 JavaScript。
* Shell Xu
单进程是有好处,但是省掉缓存服务想的太美了。系统迟早有到底的一天,到时候再改架
构,把缓存慢慢往出抠?

队列也是一样,就没考虑过,python只能用一个核?

单进程协程不是这么玩的。这是唯一一种只用python能够跑上c10k的途径。为什么必须只
用python?因为多一重访问就慢一重,而且大部分的远程协议都是cgi扩展协议,就是经
典request response模式,没机会做comit。

至于框架问题,确实对很多人非常重要。但是我觉得会将框架作为自己主要竞争力的人,
也没什么可能愿意去学工作中出现的一些其他知识。

其实我都是用gevent做tcp编程的,和web根本没关系。

但是我们的系统目前还没有comit的需求,单纯是个web,可预见的将来还是个纯web。为
了一个将来可能的需求放弃大量的现有工具,把开发难度从框架提升到tornado,是不是
太不pythonice了?

如果将来需要comit,我更愿意另开个服务,把其他url转发过来,专门处理comit。
* ubunoon
倒是很同意shell,大部分的web开发,并不需要commit,tornado很强悍,但是很低级,
需要造的轮子比较多,所以有一定的入门与学习成本,包括接手成本

django,flask之类的,相对web.py和tornado更加强大,对于一些访问量数量级不会特别
高的,又不需要commit之类的,是比较方便的。

而对于市场而言,django相对比较庞大,flask和tornado相对比较低一些。

相对学习而言,django比较多,也稍微复杂一些,tornado也比较多,flask相对比较少一
些。

选择什么不重要,重要的是东西做出来后,其他的人可以接手!
* Zephyr
| 单进程是有好处,但是省掉缓存服务想的太美了。系统迟早有到底的一天,到时候再改架构,把缓存慢慢往出抠?
| 队列也是一样,就没考虑过,python只能用一个核?

我说了是在某些条件下。

1. 系统尽早有到底的一天,这不假,不过根据实际情况我不会太去考虑这一天的。在到
这一天的过程当中,系统可能重构N回了。通常我做东西,更重要是去考虑如何好改,
而不是去考虑我做的东西足以应对一切情况。

2. 只能用一个核的问题,就是通常我们会说起多个实例。进程间通讯就成为问题。配合
前端的反向代理(按一些参数做proxy),也可以把这种机制的能力扩大很多。

当然,从另一方面,这带来了系统部署上的额外代价。至于换回一个“去掉队列/缓存”
的回报是否值得,就看实际情况了。

不管怎么说,这种单进程的机制,赋予了一种可能性,不是吗?

| 单进程携程不是这么玩的。这是唯一一种只用python能够跑上c10k的途径。为什么必须只
| 用python?因为多一重访问就慢一重,而且大部分的远程协议都是cgi扩展协议,就是经
| 典request response模式,没机会做comit。
你从效率上去理解单进程模型这当然没有问题。

但我是从应用层面去考虑单进程模型带来的可能性的。(一个例子就是登录要用验证码,
一个验证码会对应一个session,单进程模型不需要一个第三方持久化存储就可以实现了,
需要前端把一些请求代理到一个实例。还有很多这样的例子,单进程的模式可以为应用开
发带来很大的方便,我说的是方便,没说它快。)

| 至于框架问题,确实对很多人非常重要。但是我觉得会将框架作为自己主要竞争力的人,
| 也没什么可能愿意去学工作中出现的一些其他知识。

框架即工具。喜好与“当作竞争力”是不同的吧。

就像我喜欢用 LInux , VIM ,所以当然希望自己可以工作在一个完全的 Linux 环境下一
样(但我完全不会把会用 Linux 和 VIM 当成自己的竞争力。不过它们给我带来的一些境
遇确实成为了我的竞争力,折腾过程中接触的很多东西与思想)
* Shell Xu
我觉得这个就变得扯淡了。
你前面说tornado和uwsgi有很大区别,单进程可以做很多有趣的事情。后面又说解决多核
的时候可以开多进程。要开多进程不会直接上个uwsgi么?

tornado为什么要做异步协程?他们不知道异步协程必须非阻塞编程,会增加难度么?异
步协程模式最主要解决的就是直面服务的问题。在异步协程(主要是协程,异步早有
twisted)模式之前,python处理大量并发连接的时候必须借助前端服务器,例如apache
或者nginx,将并发请求转换为request/response模式。大规模访问常规有四种解法,

1. 进程模型,不超过c10k就崩溃了。
2. 线程模型,python不适用。
3. 线程加进程模型,

可以以很低的效率勉强支撑。只有异步模型可以很顺利的支持c10k以上。因此,在协程出
来之前,twisted非常重要就在于此。

这时候主要的模式是fastcgi,或者其他cgi的扩展。他们的设计都是,一个进程一个连接,
一个连接一次只能服务一个请求。第一个请求结束,第二个请求开始。这样就将并发访问
转换为了顺序访问。python在处理单连接顺序访问的时候没有问题。

但是request/response模式有缺陷,前端分配模式也有缺陷。

1. request/response模式阻止交互式访问。注意,这不是comit,而是比comit更加复杂。
从http的规范上说,body有三种模式,关闭模式,chunk模式,content-length模式。
如果显示指定了content-length,则是content-length模式。如果没有指定,但是
Transfer-Encoding有指定合法值且不是identity的,则是chunk模式。如果都没有指
定,则是close模式。在close模式中,请求默认没有body,回应有默认有body,除非
请求的method是head,或者回应的code是100,101,204,304。

因此,默认的情况下,cgi必须等待请求结束才有机会执行,这也是rfc3875的规定。
但是如果自己编写代码,就有机会在请求的body未结束前开始返回数据。而且如果请
求处于chunk模式,请求可以持续不断的po出数据,而回应也可以持续不断的响应。

常规的http服务器根本不支持这件事情。很多服务器甚至对请求里面使用chunk模式都
不加以支持。但是如果你觉得http根本不能实现这个事情?http有一个method叫做
connect,在支持的服务器上可以封装一个tcp连接——这个我们通常叫做http代理。
http代理是符合http通讯模型的。

2. 通常的前端转发程序是带有超时的,就算不超时。后端响应hold在那里不返回谁也受
不了啊。所以这个模式也没有机会做comit。

3. 无论规模大小,前端分发程序是个单点。

4. 大规模访问的时候,前端成为压力瓶颈。其实想像一下,如果老板有钱,堆无数机器,
理论上是否可以用shell来写网页呢?不是没有人这么干。问题是前端的压力不能像后
端那样释放,早晚有一天会崩溃的。因此apache换成了nginx,nginx早晚要换成
haproxy。haproxy早晚也要顶不住,到时候就要上F5或者LVS。

haproxy/F5/LVS的后面都是什么?压力不大的一台机器。那nginx在干吗?转发。而且,
静态内容都不应当继续放在nginx上了——你早应该上CDN了。来,再回答一遍,nginx最
重要的任务是干吗?

转发,顺便拖慢系统速度。
我擦,那我不会用个直接的http服务器了事啊。
这就是为什么tornado对于python在大规模系统部署上是那么重要一个东西。
而且,我问大家一个有趣的问题,系统离到底有多远?
我们可以想另外一个问题,现在离你的硬盘坏掉不能读取还有多久的时间?
永远没人知道。

我见过最吐槽的事情,是某人给我看他做的系统——一个很简陋的程序。真的很简陋,他做
的很开心。我觉得这种一没公开宣传二连完工都没有的玩意不该有压力问题——那就不该有
用户才对。

结果我的悲观和他的乐观在半个月后终结——鬼知道他的用户都是从哪里来的,反正服务器
崩了。
崩了,他又不想限制用户,那总得有个方案咯。
我review了一遍他的代码,发现他写的是很开心——session,存进程的内存里面,
template,用的django自带的,前端,用的apache+flup。

那我就建议他换nginx+uwsgi,好,速度上去点了,问题马上来了。用户登录状态不稳定。
因为用户session放在进程的内存里面!

我当时就崩溃了,你为什么不用session呢?django自带了session啊。。。
——那个性能太差了。
————性能太差了。
——————太差了。
————————了。
掉头回去改,改了一个周末,搞定了。然后我建议他把数据库分离开,单放一台机器。然
后加内存上mc来做session。这样暂时就没问题了。

过了没半年,瓶颈迁移到django的template上。这玩意在大量循环(尤其是>100)的时候
有严重的性能问题。这个是我当时已知的问题,我建议他换cheetah(当时我对template
的测试还有点问题,我现在会建议mako)。但是这样他就要重做所有模板,300多个页
面。。。

后来他不干了,因为不开心。。。
你真没必要太过考虑性能问题,但是如果碰到性能问题总要有解决的可能性。你像我们这
个系统,是没有comit需求。但是要哪天有?没问题,我用tornado加个前端转发程序,马
上解决问题。要一台机器拆多台?没问题,先拆数据库和mc,然后应用服务器拆一转多应
用,mysql再拆一写多读同步。压力再大,前面用haproxy,后面用tornado跑flask。压力
再大,前面换F5,设备上存储。再大?肖总,你该给我加工资了吼。。。

最后,框架是工具,因此你会在乎你去的地方用的是车床还是铣床么?除非给你把锤子。
我们金工实习的时候就学一种车床,理论上所有其他机床原理都一样,老师教一遍大家都
pass了。车床学不会的,铣床一样没戏。当年我们还用车床刻字,用线切割做名字吊牌
(那台可怜的线切割床跑了好久,最后我被老师骂了一顿)。估计给一台啥都学学能玩起
来——我金工时期只有两个项目得了中,一个是卧式铣床,靠紧没做好,粗心了。一个是钳
工,手里没劲,到最后都没做完。。。

同样,熟悉我的知道我是emacs用户,linux用户。但是我也会用vi,有mcse证书,
windows编程水准还凑合,写写dll hook,注入溢出也可以。我讨厌.net,但是我当作两
年的.net项目经理,拿.net写过算法——我倒现在还没搞明白.net到底是怎么回事。

我想问的是,框架真的有那么重要,值得你们一天到晚吵来吵去?难道学一门框架就这么
难?尤其有熟悉这门框架的人在旁边辅导的情况下?这可不是我一个人的观点,今天老潘
还在微薄上吐槽,python ui开发群里面安安静静,web开发群里面在搞框架选举。如果框
架真的那么重要,那程序员的plus版本就不该叫架构师,应该叫框架师。你都用python了,
我觉得你完全可以省下在语言里面纠结的时间,学一门框架。省下进行框架选举的时间,
再学一门框架。

反正我们有胆子招个好的程序员,不会用python也行。给你一周时间,看个大概,然后把
你放在我隔壁。我有信心,最多两个月,你完全可以胜任我们的工作。所以我上面再三说。
我们招个程序员,不招python员。
* Zephyr
| 我觉得这个就变得扯淡了。
| 你前面说tornado和uwsgi有很大区别,单进程可以做很多有趣的事情。后面又说解决多核
| 的时候可以开多进程。要开多进程不会直接上个uwsgi么?

这个问题我已经举例说过了吧。我整个应用起5个进程。其中1个单处理对 /auth 的请求
(nginx反向代理过来),在 /auth 的实现中,做验证码的逻辑。因为是单进程,请求之间
是同一个上下文,不需要引入另外的存储手段就可以实现。

这是一个实际的例子,多进程起的tornado项目,也因为它的单进程模式才能做到的。(传
统的web框架正式环境下加uwsgi是不行的,应该不行吧)

| tornado为什么要做异步协程?他们不知道异步协程必须非阻塞编程,会增加难度么?异
| 步协程模式最主要解决的就是直面服务的问题。……

我基本了解 HTTP 协议。我要说的东西和这个没关系,不关乎效率,不关乎协议。

只是因为它是单进程,不同的请求是同一个运行上下文。源于此,在实现一些应用逻辑的
时候,有额外的手段,可以很方便。是方便。

| 我见过最吐槽的事情,是某人给我看他做的系统——一个很简陋的程序。真的很简陋,他做
| 的很开心。我觉得这种一没公开宣传二连完工都没有的玩意不该有压力问题——那就不该有
| 用户才对。
| 结果我的悲观和他的乐观在半个月后终结——鬼知道他的用户都是从哪里来的,反正服务器
| 崩了。
| 崩了,他又不想限制用户,那总得有个方案咯。
| 我review了一遍他的代码,发现他写的是很开心——session,存进程的内存里面,
| template,用的django自带的,前端,用的apache+flup。
| 那我就建议他换nginx+uwsgi,好,速度上去点了,问题马上来了。用户登录状态不稳定。
| 因为用户session放在进程的内存里面!
| 我当时就崩溃了,你为什么不用session呢?django自带了session啊。。。
| ——那个性能太差了。
| ————性能太差了。
| ——————太差了。
| ————————了。
| 掉头回去改,改了一个周末,搞定了。然后我建议他把数据库分离开,单放一台机器。然
| 后加内存上mc来做session。这样暂时就没问题了。
| 过了没半年,瓶颈迁移到django的template上。这玩意在大量循环(尤其是>100)的时候
| 有严重的性能问题。这个是我当时已知的问题,我建议他换cheetah(当时我对template
| 的测试还有点问题,我现在会建议mako)。但是这样他就要重做所有模板,300多个页
| 面。。。
| 后来他不干了,因为不开心。。。
| 你真没必要太过考虑性能问题,但是如果碰到性能问题总要有解决的可能性。你像我们这
| 个系统,是没有comit需求。但是要哪天有?没问题,我用tornado加个前端转发程序,马
| 上解决问题。要一台机器拆多台?没问题,先拆数据库和mc,然后应用服务器拆一转多应
| 用,mysql再拆一写多读同步。压力再大,前面用haproxy,后面用tornado跑flask。压力
| 再大,前面换F5,设备上存储。再大?肖总,你该给我加工资了吼。。。

你说的这个好像和我们讨论的话题没有关系吧。

如果从一个实际应用的角度来看的话,我用 tornado 实现的角度是这样的:

1. session 机制用与不用的选择。如果仅仅是登录问题,在cookie上加签名就好了。不
需要引入一个session的机制。

2. 如果要用 session ,那就要考虑存哪里的问题。实际情况是放数据库不行,哪只有上
缓存,这又需要引入一种新的机制,回过头想想是不是一定要用session。我们已经付
出了两种机制的代价了,我们得到了什么。

3. 好吧,假设结果是 session 机制是一定需要的。那么我们需要有一个缓存。面对缓存,
假如暂时的选择有:

1) 应用自己 (因为tornado是单进程,所以它可以自己做缓存)
2) mc
3) redis

对于第1点选择,需要满足一个条件才行,就是对于单个用户,它的请求必须始终到达某
个固定的tornado实例。关于这点,在某些条件下是可以做到的。这是一种选择,虽然并
不一定是好的。

对于第2点选择,假设没有专门的tornado客户端。
而第3点选择它有专门的客户端。虽然 mc 会比 redis 快些,但在能满足需求的情况下,
我会选择 redis 。除了把它当成一个普通的,同步访问的 session 容器之外,它还可以
用来做其它事,因为 torndo 有redis 客户端,请求可以是异步的。既然引入了缓存机制,
就应该尽力去发挥一种机制的用处,我是这样认为的。

对于 session 的问题,在 tornado 的即定条件下,我会做如上考虑。而其它的传统的
web 框架是不会有这样考虑的,或者考虑的东西不一样。

再说模板。假设 tornado 的模板系统也碰到了效率问题。我首先考虑的不是换模板系统,
而是缓存。当然,对缓存的选择也是上面的那些。同时,tornado 的模板有一个 UI 机制,
“应用本身”这个缓存,就可以放在 UI 里。

回过头再来考虑。如果我们可以实现同一个用户到达固定的tornado实例,看来还可以带
来很多其它好处,当然,这得另说了。

缓存多了维护就是问题,不管是 mc 还是 redis 。但是如果缓存做到了应用本身当中,
问题稍有不同,因为这些缓存可以自我维护。比如你可以在 ioloop 中以 5 秒为间隔去
检查一些东西,以决定缓存中的数据如何处理。

另如有项目,有很多需要使用 cron 做的事,那完全可以全在tornado应用中自己解决,
控制力度绝不是 cron 可比的。(我可以起10个实例,6个用于处理用户请求,4个专干以
前 cron 干的事)

我想说的,只是,可能性,选择,方便。所以我说 tornado 这种单进程的模式与传统
web 框架的那种模式是不同的,可以做很多有趣有意思的事。

是不同,不是更好。

| 最后,框架是工具,因此你会在乎你去的地方用的是车床还是铣床么?除非给你把锤子。

但是我真的很在乎我去的地方,我不得不在windows下工作。我不得不在我不喜欢的框架
下写代码。(假设即有的选择都没有错,那又如何呢,一个人关注这些我觉得很正常啊,
也觉得和你说的学习其它知识没有关系)

当然,现在问题似乎又变成了,各种web框架是车床与铣床的关系,还是车床与锤子的关
系 🙂 关于这个,因为我前面说的那些“方便”,我越来越认同是后者的关系(tornado和
django。tornado和gevent可能是前者吧,gevent没怎么正式用过)

| 我们金工实习的时候就学一种车床,理论上所有其他机床原理都一样,老师教一遍大家都
| pass了。车床学不会的,铣床一样没戏。当年我们还用车床刻字,用线切割做名字吊牌
| (那台可怜的线切割床跑了好久,最后我被老师骂了一顿)。估计给一台啥都学学能玩起
| 来——我金工时期只有两个项目得了中,一个是卧式铣床,靠紧没做好,粗心了。一个是钳
| 工,手里没劲,到最后都没做完。。。
| 同样,熟悉我的知道我是emacs用户,linux用户。但是我也会用vi,有mcse证书,
| windows编程水准还凑合,写写dll hook,注入溢出也可以。我讨厌.net,但是我当作两
| 年的.net项目经理,拿.net写过算法——我倒现在还没搞明白.net到底是怎么回事。
| 我想问的是,框架真的有那么重要,值得你们一天到晚吵来吵去?难道学一门框架就这么
| 难?尤其有熟悉这门框架的人在旁边辅导的情况下?这可不是我一个人的观点,

首先,我没有说框架重要。我只是在说单进程与传统web框架之间的差异。这怎么说呢。
一个不太合适的例子,就像编辑器,从整体上看,它不重要。用自个儿熟悉的高效地完成
自己的想法即可。但对个人而言,你习惯了用 A,但又必须去用 B 的时候,我觉得问题
就变得重要起来了吧。

其次,也没有争执,至少我真没有因为框架问题争执过什么。(很多框架我根本不熟悉,
也没有发言权)

再次,关于学习框架的问题。学会用一个东西,和在学习的过程中得到一些启发,程度还
是不同的吧。我们怎么讨论到这问题上了……

| 今天老潘还在微薄上吐槽,python ui开发群里面安安静静,web开发群里面在搞框架选举。
| 如果框架真的那么重要,那程序员的plus版本就不该叫架构师,应该叫框架师。你都用
| python了,我觉得你完全可以省下在语言里面纠结的时间,学一门框架。省下进行框架选
| 举的时间,再学一门框架。
| 反正我们有胆子招个好的程序员,不会用python也行。给你一周时间,看个大概,然后把
| 你放在我隔壁。我有信心,最多两个月,你完全可以胜任我们的工作。所以我上面再三说。
| 我们招个程序员,不招python员。

你都说是“好的程序员”了。即使如此,两个月后,一个全新的项目,你敢让他独自一手
做起来吗?我觉得这和你教导得怎么,以及他自身能力如何都没有关系。我也认同框架不
重要,语言不重要。但是,积累很重要,经历很重要。这样说的话,tornado/gevent 这
些单进程,异步的东西,也算是一种经历吧。
* Shell Xu
|| 我觉得这个就变得扯淡了。
|| 你前面说tornado和uwsgi有很大区别,单进程可以做很多有趣的事情。后面又说解决多核
|| 的时候可以开多进程。要开多进程不会直接上个uwsgi么?

| 这个问题我已经举例说过了吧。我整个应用起5个进程。其中1个单处理对 /auth 的请求
| (nginx反向代理过来),在 /auth 的实现中,做验证码的逻辑。因为是单进程,请求之间
| 是同一个上下文,不需要引入另外的存储手段就可以实现。

可以,uwsgi可以开多实例分发,而且还可以根据不同的安全级别和访问地址分发。需要
的话我可以给你个例子,我的服务器上就跑着两个不同的uwsgi实例。而且你这个例子更
好的方式是使用python的代码在分发时验证。

| 这是一个实际的例子,多进程起的tornado项目,也因为它的单进程模式才能做到的。(传
| 统的web框架正式环境下加uwsgi是不行的,应该不行吧)

|| tornado为什么要做异步协程?他们不知道异步协程必须非阻塞编程,会增加难度么?异
|| 步协程模式最主要解决的就是直面服务的问题。……

| 我基本了解 HTTP 协议。我要说的东西和这个没关系,不关乎效率,不关乎协议。
| 只是因为它是单进程,不同的请求是同一个运行上下文。源于此,在实现一些应用逻辑的
| 时候,有额外的手段,可以很方便。是方便。

如果无关效率,就别提tornado。你不知道python自带http服务器么?难道这个服务器会
比tornado更麻烦?

|| 我见过最吐槽的事情,是某人给我看他做的系统——一个很简陋的程序。真的很简陋,他做
|| 的很开心。我觉得这种一没公开宣传二连完工都没有的玩意不该有压力问题——那就不该有
|| 用户才对。
|| 结果我的悲观和他的乐观在半个月后终结——鬼知道他的用户都是从哪里来的,反正服务器
|| 崩了。
|| 崩了,他又不想限制用户,那总得有个方案咯。
|| 我review了一遍他的代码,发现他写的是很开心——session,存进程的内存里面,
|| template,用的django自带的,前端,用的apache+flup。
|| 那我就建议他换nginx+uwsgi,好,速度上去点了,问题马上来了。用户登录状态不稳定。
|| 因为用户session放在进程的内存里面!
|| 我当时就崩溃了,你为什么不用session呢?django自带了session啊。。。
|| ——那个性能太差了。
|| ————性能太差了。
|| ——————太差了。
|| ————————了。
|| 掉头回去改,改了一个周末,搞定了。然后我建议他把数据库分离开,单放一台机器。然
|| 后加内存上mc来做session。这样暂时就没问题了。
|| 过了没半年,瓶颈迁移到django的template上。这玩意在大量循环(尤其是>100)的时候
|| 有严重的性能问题。这个是我当时已知的问题,我建议他换cheetah(当时我对template
|| 的测试还有点问题,我现在会建议mako)。但是这样他就要重做所有模板,300多个页
|| 面。。。
|| 后来他不干了,因为不开心。。。
|| 你真没必要太过考虑性能问题,但是如果碰到性能问题总要有解决的可能性。你像我们这
|| 个系统,是没有comit需求。但是要哪天有?没问题,我用tornado加个前端转发程序,马
|| 上解决问题。要一台机器拆多台?没问题,先拆数据库和mc,然后应用服务器拆一转多应
|| 用,mysql再拆一写多读同步。压力再大,前面用haproxy,后面用tornado跑flask。压力
|| 再大,前面换F5,设备上存储。再大?肖总,你该给我加工资了吼。。。

| 你说的这个好像和我们讨论的话题没有关系吧。
| 如果从一个实际应用的角度来看的话,我用 tornado 实现的角度是这样的:
| 1. session 机制用与不用的选择。如果仅仅是登录问题,在cookie上加签名就好了。不
| 需要引入一个session的机制。

不怕不知道,就怕一知半解。。。cookie加签名有严重的安全问题。
一般来说,登录时会使用https协议,而完成后会换用http协议。这是常规网站的常识。
登录时,用户会传递user/pass对到服务器,服务器会做一些事情,拿到身份相关数据,
然后放入session,传回sessionid。这样数据是不经过网络的。
如果加签名,虽然无法更改,但是在经过网络的时候,cookie会暴露出你在服务器端保存
的数据。你存什么暴露什么,粗心大意一点还会暴露出密码。
而且,为了对抗重放攻击,你必须在cookie内保存时间。在拿到cookie后,你还要验证这
个cookie的时间。否则客户端可以恶意的保持原有cookie不变更,从而达到类似“花了钱
但是现在还能消费”这种事情。
当然,你可以选择以下三种驳斥方式。
1. 可以在服务器端加密。但是这无法对抗重放攻击。
2. 加密加时间验证。但是这对不支持cookie的浏览器无效。
3. 我说的应用没有你说的问题。

| 2. 如果要用 session ,那就要考虑存哪里的问题。实际情况是放数据库不行,哪只有上
| 缓存,这又需要引入一种新的机制,回过头想想是不是一定要用session。我们已经付
| 出了两种机制的代价了,我们得到了什么。

为什么要多一种机制?你不知道有一种session store叫做in memory么?直接用python的
dict保存,和你的方案一样。但是在需要的时候,可以切换store,直接改用
redis/mc/mongo。

| 3. 好吧,假设结果是 session 机制是一定需要的。那么我们需要有一个缓存。面对缓存,假如暂时的选择有:
| 1) 应用自己 (因为tornado是单进程,所以它可以自己做缓存)
| 2) mc
| 3) redis
| 对于第1点选择,需要满足一个条件才行,就是对于单个用户,它的请求必须始终到达某
| 个固定的tornado实例。关于这点,在某些条件下是可以做到的。这是一种选择,虽然并
| 不一定是好的。

不是这样的。准确的说法是某个session一定要映射到某个固定的实例。类似的方案有源
ip地址映射,haproxy的关键字映射,但是都不完善。

| 对于第2点选择,假设没有专门的tornado客户端。而第3点选择它有专门的客户端。虽然
| mc 会比 redis 快些,但在能满足需求的情况下,我会选择 redis 。除了把它当成一个
| 普通的,同步访问的 session 容器之外,它还可以用来做其它事,因为 torndo 有
| redis 客户端,请求可以是异步的。既然引入了缓存机制,就应该尽力去发挥一种机制的
| 用处,我是这样认为的。

| 对于 session 的问题,在 tornado 的即定条件下,我会做如上考虑。而其它的传统的
| web 框架是不会有这样考虑的,或者考虑的东西不一样。

| 再说模板。假设 tornado 的模板系统也碰到了效率问题。我首先考虑的不是换模板系统,
| 而是缓存。当然,对缓存的选择也是上面的那些。同时,tornado 的模板有一个 UI 机制,
| “应用本身”这个缓存,就可以放在 UI 里。

| 回过头再来考虑。如果我们可以实现同一个用户到达固定的tornado实例,看来还可以带
| 来很多其它好处,当然,这得另说了。

| 缓存多了维护就是问题,不管是 mc 还是 redis 。但是如果缓存做到了应用本身当中,
| 问题稍有不同,因为这些缓存可以自我维护。比如你可以在 ioloop 中以 5 秒为间隔去
| 检查一些东西,以决定缓存中的数据如何处理。

相反,缓存如果做到了应用本身当中,才是问题的开始(如果你不明白的话)。
你以为tornado不需要锁么?我记得谁吐槽过异步协程无锁模式,所谓无锁,是指在上下
文切换之间不需要上锁。什么时候发生上下文切换?

抱歉,你去做sql的时候也会上下文切换的。
我们在缓存的维护上一直有个竞争问题。例如一个session,同时在两个上下文中访问,
一个对用户金钱+1,一个对用户登录时间+1,最终就只有一个生效的。这是可能的,做过
竞争访问的都应该明白我在说什么。

如果你在sql访问前读取本地缓存,sql访问完了对其进行保存,刚刚那个问题一样会跳出
来。

也就是说,即使是异步模式,即使是本地缓存,也避免不了竞态访问的问题。那你逞论控
制呢?

| 另如有项目,有很多需要使用 cron 做的事,那完全可以全在tornado应用中自己解决,
| 控制力度绝不是 cron 可比的。(我可以起10个实例,6个用于处理用户请求,4个专干以
| 前 cron 干的事)

崩溃。。。如果你都起了这么多实例,何必让那4个跑tornado。。。你不会让他们直接作
为daemon跑么?python的daemonized只需要15行。。。

| 我想说的,只是,可能性,选择,方便。所以我说 tornado 这种单进程的模式与传统
| web 框架的那种模式是不同的,可以做很多有趣有意思的事。
| 是不同,不是更好。

如果你硬要说单进程的话,你以为uwsgi不能在单个进程中开启一个http服务么?理论上
甚至可以在uwsgi里面跑的python程序里面exec一个nginx来给自己做反向代理。毕竟
uwsgi只是框架,但是做什么是代码的事情。你要跳出框架,我曾经在一个wsgi+django里
面整了一个爬虫进去。在你点一个按钮后去跑一个爬虫线程。为什么不可以?

我也可以让uwsgi的进程开一对pipe,fork一遍,然后spawn一个vncserver,把内容灌到
自己的pipe里面。然后用持续异步模式(这到真没法用uwsgi了,当然,tornado也不行,
你要用我写的框架才行)或者被动通讯模式和一个页面控件通讯,为什么不可以?

|| 最后,框架是工具,因此你会在乎你去的地方用的是车床还是铣床么?除非给你把锤子。

| 但是我真的很在乎我去的地方,我不得不在windows下工作。我不得不在我不喜欢的框架
| 下写代码。(假设即有的选择都没有错,那又如何呢,一个人关注这些我觉得很正常啊,
| 也觉得和你说的学习其它知识没有关系)
| 当然,现在问题似乎又变成了,各种web框架是车床与铣床的关系,还是车床与锤子的关
| 系 🙂 关于这个,因为我前面说的那些“方便”,我越来越认同是后者的关系(tornado和
| django。tornado和gevent可能是前者吧,gevent没怎么正式用过)

|| 我们金工实习的时候就学一种车床,理论上所有其他机床原理都一样,老师教一遍大家都
|| pass了。车床学不会的,铣床一样没戏。当年我们还用车床刻字,用线切割做名字吊牌
|| (那台可怜的线切割床跑了好久,最后我被老师骂了一顿)。估计给一台啥都学学能玩起
|| 来——我金工时期只有两个项目得了中,一个是卧式铣床,靠紧没做好,粗心了。一个是钳
|| 工,手里没劲,到最后都没做完。。。
|| 同样,熟悉我的知道我是emacs用户,linux用户。但是我也会用vi,有mcse证书,
|| windows编程水准还凑合,写写dll hook,注入溢出也可以。我讨厌.net,但是我当作两
|| 年的.net项目经理,拿.net写过算法——我倒现在还没搞明白.net到底是怎么回事。
|| 我想问的是,框架真的有那么重要,值得你们一天到晚吵来吵去?难道学一门框架就这么
|| 难?尤其有熟悉这门框架的人在旁边辅导的情况下?这可不是我一个人的观点,

| 首先,我没有说框架重要。我只是在说单进程与传统web框架之间的差异。这怎么说呢。
| 一个不太合适的例子,就像编辑器,从整体上看,它不重要。用自个儿熟悉的高效地完成
| 自己的想法即可。但对个人而言,你习惯了用 A,但又必须去用 B 的时候,我觉得问题
| 就变得重要起来了吧。

我真的不觉得这个问题很重要,我不喜欢vim,可这不妨碍我用vim改emacs的启动脚本。
因为emacs自己改完了启动脚本往往会搞得自己没法启动!!!
| 其次,也没有争执,至少我真没有因为框架问题争执过什么。(很多框架我根本不熟悉,
| 也没有发言权)
| 再次,关于学习框架的问题。学会用一个东西,和在学习的过程中得到一些启发,程度还
| 是不同的吧。我们怎么讨论到这问题上了……

|| 今天老潘还在微薄上吐槽,python ui开发群里面安安静静,web开发群里面在搞框架选举。
|| 如果框架真的那么重要,那程序员的plus版本就不该叫架构师,应该叫框架师。你都用
|| python了,我觉得你完全可以省下在语言里面纠结的时间,学一门框架。省下进行框架选
|| 举的时间,再学一门框架。
|| 反正我们有胆子招个好的程序员,不会用python也行。给你一周时间,看个大概,然后把
|| 你放在我隔壁。我有信心,最多两个月,你完全可以胜任我们的工作。所以我上面再三说。
|| 我们招个程序员,不招python员。

| 你都说是“好的程序员”了。即使如此,两个月后,一个全新的项目,你敢让他独自一手
| 做起来吗?我觉得这和你教导得怎么,以及他自身能力如何都没有关系。我也认同框架不
| 重要,语言不重要。但是,积累很重要,经历很重要。这样说的话,tornado/gevent 这
| 些单进程,异步的东西,也算是一种经历吧。

没问题啊。为什么不可以。我觉得好的程序员其实用不着我教他python。我只需要告诉他,
哪里可以拿到他需要的东西,仅此而已。
如果说有什么单进程可以做而多进程做不了的事情,那就是ipc。但是tornado可以支持的
事情,你开一个uwsgi难道做不到么?如果一个uwsgi性能不够了,tornado又能支撑多久
呢?uwsgi还有另一种让人吐血的模式,在同一个进程内跑两个python解释器。我没见过
这种模式跑的,估计是因为有很多内存引用块会在两个解释器中乱窜。但是这也并不是不
可以。
* Sager
我比较认同Shell的观点,虽然我们的项目使用Python为主,但我相信一个好的程序员,
就算现在不会Python,有我们团队的支持和氛围,我想一周最多两周就可以上手做具体功
能了。至少我们团队现在就有两个这样的例子,加入公司之前完全没接触过Python,目前
都已经在项目中承担了一些核心模块的开发。所以我们招优秀的人,现在会不会Python并
不是最关键的,但在Python的论坛,我不太可能说不需要Python知识,吼吼
* 风间星魂
说起shell写web,过去gentoo主页就是shell。。。。
我觉的异步这个说法太扯淡了,从api来说,从来就不存在真正的异步。。
1:多进程
2:多线程
3:多进程+多线程
4:线性io复用 select/poll
5:非阻塞io SIGIO信号
6:内核通知型io复用 epoll/kqueue

API意义上的所谓异步接口aio实际上是用户态线程或者SIGIO模拟的.
我不知道怎么才算异步?因为根本不存在。。
* Sager
补充一句,熟练使用开发语言和框架,是一个程序员的基本素质,也是立足的根本。对于
新项目使用新技术,好的程序员也能非常快的上手,并将以往的经验融合进来。

技术的好坏不能决定你的生活好坏,而项目的好坏才是关键,好的技术不能转化为生产力,
不能转化为好的产品,都是扯蛋!
* 风间星魂
新技术新项目有时候会影响士气。
比如要换同级别的技术栈,我就不干了。。。。太浪费时间了
* luzi
纠正一点,fastcgi是异步并发模型,并不是顺序访问。
* Shell Xu
异步和非异步的模式还是有区别的。同步模式,或者说阻塞模式,是通过上下文挂起/恢
复来响应数据到达事件的。以系统来说,就是线程切换。同步编程,为每个连接开一个线
程,然后阻塞在读取上,进程状态从R转换为D(或者S)。fd数据就绪,线程就从D转换为
R。然后做线程切换。

异步则是通过检测就绪手段,例如select/poll/epoll来完成这个工作,也就是io复用。
先检测有哪个fd就绪了,然后准备和这个fd相关的数据(如果是异步协程就是唤醒阻塞在
这个fd对应事件上的协程),继续处理。

为什叫做异步?因为在fd操作后不是等待,而是直接返回。异步不代表协程,异步可以不
协程。例如用reactor模式。。。

但是这年头,python异步提的最多的就是协程。为什么?因为reactor模式太蛋疼了。写
个代码,读取个数据,处理一下,返回。写代码要写一个函数,驱动数据读取,当返回时
调用另外一个函数,做数据处理,再调另外一个函数,做返回处理。本来是很顺的代码,
变成一堆鸡零狗碎的玩意。当然你也可以说没问题阿,老子scheme都用的很顺,谁怕这
个。。。

协程可以将上下文切换和线程调度分离开。即使是异步协程模式,协程切换开销也远远小
于线程切换开销,而检测fd就绪又比操作系统检测线程上下文,加入调度队列等待调度快。
这就是协程模式快的原因。

另一个协程模式快的原因是因为他基本“无锁”。协程在上下文切换间是不需要锁的。进
程/线程由于操作系统的抢占特性,都必须上锁。因为你根本无从预测,时钟中断会在哪
个时刻到来。没有经过合理设计,又不上锁的并发编程就是耍流氓。

唔,这倒是。fastcgi允许在一个连接上发出多个请求,response用requestId指名响应哪
个id。
* 风间星魂
我不把io复用叫异步的原因是:io复用要做到无阻塞,实际上是轮询fd可读可写有异常等
等,只不过等待时间是0。。

理想的异步应该是aio真正内核实现(目前没有系统真正在内核态实现),注册监听到内
核,然后事件发生直接回调,而不是反复轮询消耗性能。

当然,在用户态某种包装模拟实现的不算。

SIGIO通知勉强算异步io,但信号排队的问题导致这玩意根本不堪大用。
* Shell Xu
所以我们的做法是,把多个组件拆分开,每个部分都可以独立使用。ORM专门用ORM的,框
架专门用框架,form专门用form,容器专门用容器。

一般来说,大多数人会有一两项陌生的,但是大部分人也会有一两项熟悉的。而且还有两
个额外优势。

1. 每个组件都是为了这个事情专门设计的,专业。
2. 万一有问题,可以很快替换掉这个组件。
* jjx
我倒是比较认同YS.Zou 的观点,因为我上一个项目就是torando+pyzmq+extjs,最早用
django,后期想切换到gevent,但现在这个组合最终我非常满意,ys.zou的大部分做法都
是我这个项目中在用的。其实异步也就这个套路了。

同时我也不认为框架不重要,随时可以换来换去,大概是我现在年级大了,不爱好折腾了。
像twisted这样的异步框架,已经建立了一个生态系统。在上面的经验可以说在以后一段
时间内都能受用。反之,

我用django/flask的结果是,我并不觉得我从中积累到了什么东西。 同ys.zou 一样,如
果有个项目,让我用django 而不是tornado,我会拒接这个项目,这里看起来有很多个人
的喜好在里面,其实更多的是因为在这个框架中有积累,能更好的把控而已。
* Shell Xu
唔,其实在c100k这个量级以下区别并不特别明显,不过接近c100k后还是很明显的。超过
c10k就有感觉了。

实际上epoll干的就是这个事情。通过内核级别的注册和回调,不轮询。

我看不懂这个逻辑。
虽然框架不重要,但是换个框架我会拒接你的项目哦。。。

我觉得框架不重要。所以如果我碰到我不喜欢的框架会怎么办?
我会写个wsgi分离代码,将两个url映射到不同的框架里面去执行,或者干脆在分发层搞
定。
你用你喜欢的框架写,我用我喜欢的。mc的读写上要保持一致,其他没啥问题。
* 风间星魂
epoll只是大大提高了性能,
它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入队列的描
述符集合。
实际上依然是某种轮询机制。。你还得一个个去检查队列中被唤醒的fd。

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int
timeout);

使用上和select本质上无区别,你要想非阻塞还是要设等待时间为0。
然后定时轮询。

select/epoll不只是web服务器在用!
我在游戏里也用select来实现网络对战,就是设等待时间为0.定时轮询。

我觉的框架很重要。
虽然重新学习一个也不难。但凭什么?重学一个既不能提升技能能力,而且也相当浪费时
间和无聊。
我的策略是定义接口,想怎么实现随你喜欢。
* jjx
哈,不认为框架不重要,就是说框架重要,这个就像语言一样,比方说我现在不用.net
,虽然我也曾经是c#的mvp, 如果有人让我做项目,指定要c#,我会推掉这个单子一样。当
然,如果穷到揭不开锅的时候,我也会硬着头皮接的。

总之这个贴子有点离题了,还是不要影响lz招聘了,我们个人喜欢什么对别人是没有任何
影响的
* Zephyr
|| 这个问题我已经举例说过了吧。我整个应用起5个进程。其中1个单处理对 /auth 的请求
|| (nginx反向代理过来),在 /auth 的实现中,做验证码的逻辑。因为是单进程,请求之间
|| 是同一个上下文,不需要引入另外的存储手段就可以实现。

| 可以,uwsgi可以开多实例分发,而且还可以根据不同的安全级别和访问地址分发。需要
| 的话我可以给你个例子,我的服务器上就跑着两个不同的uwsgi实例。而且你这个例子更
| 好的方式是使用python的代码在分发时验证。

我想看一下这个例子,麻烦了。不用是验证码。只要是多次访问之间,修改了同一个变量
即可。(就假定场景为,一个GET请求,返回一个数字,这个数字表示此页面被访问了多
少次吧。tornado 中就是在 handler 的方法做,做 self.__class__.counter += 1 这样
的效果)

|| 这是一个实际的例子,多进程起的tornado项目,也因为它的单进程模式才能做到的。(传
|| 统的web框架正式环境下加uwsgi是不行的,应该不行吧)

||| tornado为什么要做异步协程?他们不知道异步协程必须非阻塞编程,会增加难度么?异
||| 步协程模式最主要解决的就是直面服务的问题。……

|| 我基本了解 HTTP 协议。我要说的东西和这个没关系,不关乎效率,不关乎协议。

|| 只是因为它是单进程,不同的请求是同一个运行上下文。源于此,在实现一些应用逻辑的
|| 时候,有额外的手段,可以很方便。是方便。

| 如果无关效率,就别提tornado。你不知道python自带http服务器么?难道这个服务器会
| 比tornado更麻烦?

这当然没问题,如果 python 自带的 http 服务器可以做 tornado 能做的事(不考虑效
率),同时用起来也方便,也足以跑起来不挂掉。这和我认同的东西不矛盾。

其它框架在测试时也是一个进程,虽然这时也能做我所谓的“有趣”的事,但是这是没意
义。我觉得这点上没必要再多说吧。

|| 你说的这个好像和我们讨论的话题没有关系吧。
|| 如果从一个实际应用的角度来看的话,我用 tornado 实现的角度是这样的:

||1. session 机制用与不用的选择。如果仅仅是登录问题,在cookie上加签名就好了。不
|| 需要引入一个session的机制。

| 不怕不知道,就怕一知半解。。。cookie加签名有严重的安全问题。
| 一般来说,登录时会使用https协议,而完成后会换用http协议。这是常规网站的常识。
| 登录时,用户会传递user/pass对到服务器,服务器会做一些事情,拿到身份相关数据,
| 然后放入session,传回sessionid。这样数据是不经过网络的。
| 如果加签名,虽然无法更改,但是在经过网络的时候,cookie会暴露出你在服务器端保存
| 的数据。你存什么暴露什么,粗心大意一点还会暴露出密码。

认同。所以在选择是否使用 session 时会考虑这些问题。考虑如果不用 session ,那么
在 cookie 中放哪些东西。

比如我放入用户ID,用户类型这两个数据,明确地知道,就算这两个数据暴露了也没有问
题。系统可以承受这个结果,那就可以这样做。(也可以在cookie中做一些简单的,类似
xxtea的对称加密,这不是说万无一失,只是数据不那么露骨)

我们在做一些选择时,总是付出一些代价,而获取一些对实际而言有用的东西。我不否认
你说的安全问题。

| 而且,为了对抗重放攻击,你必须在cookie内保存时间。在拿到cookie后,你还要验证这
| 个cookie的时间。否则客户端可以恶意的保持原有cookie不变更,从而达到类似“花了钱
| 但是现在还能消费”这种事情。
| 当然,你可以选择以下三种驳斥方式。
| 1. 可以在服务器端加密。但是这无法对抗重放攻击。
| 2. 加密加时间验证。但是这对不支持cookie的浏览器无效。
| 3. 我说的应用没有你说的问题。

对,关键是第3点。我从一开始讨论 tornado 时,就说是在某些条件下。它的机制只是多
了一种选择,多了一种可能性。

我没有说这种做法就一定是对的。就像我说这个例子的时候,并没有否认 session 机制
一样。都是摆在我们面前的选择。合适与否要看实际情况,是吧。

|| 2. 如果要用 session ,那就要考虑存哪里的问题。实际情况是放数据库不行,哪只有上
|| 缓存,这又需要引入一种新的机制,回过头想想是不是一定要用session。我们已经付
|| 出了两种机制的代价了,我们得到了什么。

| 为什么要多一种机制?你不知道有一种session store叫做in memory么?直接用python的
| dict保存,和你的方案一样。但是在需要的时候,可以切换store,直接改用
| redis/mc/mongo。

当然,假设不需要引入一种机制。那当然使用 session 的代价就会小很多。在考虑时会
有不同的考量。

另外,我只知道 in memory 这个在测试时很好用,但是在实际部署时会有怎样的效果我
是真没用过,见笑了。

|| 3. 好吧,假设结果是 session 机制是一定需要的。那么我们需要有一个缓存。面对缓存,
|| 假如暂时的选择有:
|| 1) 应用自己 (因为tornado是单进程,所以它可以自己做缓存)
|| 2) mc
|| 3) redis

|| 对于第1点选择,需要满足一个条件才行,就是对于单个用户,它的请求必须始终到达某
|| 个固定的tornado实例。关于这点,在某些条件下是可以做到的。这是一种选择,虽然并
|| 不一定是好的。

| 不是这样的。准确的说法是某个session一定要映射到某个固定的实例。类似的方案有源
| ip地址映射,haproxy的关键字映射,但是都不完善。

不错。所以这只是一种考虑。我没说它一定是最适合的方案。

|| 对于第2点选择,假设没有专门的tornado客户端。而第3点选择它有专门的客户端。虽然
|| mc 会比 redis 快些,但在能满足需求的情况下,我会选择 redis 。除了把它当成一个
|| 普通的,同步访问的 session 容器之外,它还可以用来做其它事,因为 torndo 有
|| redis 客户端,请求可以是异步的。既然引入了缓存机制,就应该尽力去发挥一种机制的
|| 用处,我是这样认为的。

|| 对于 session 的问题,在 tornado 的即定条件下,我会做如上考虑。而其它的传统的
|| web 框架是不会有这样考虑的,或者考虑的东西不一样。

|| 再说模板。假设 tornado 的模板系统也碰到了效率问题。我首先考虑的不是换模板系统,
|| 而是缓存。当然,对缓存的选择也是上面的那些。同时,tornado 的模板有一个 UI 机制,
|| “应用本身”这个缓存,就可以放在 UI 里。

|| 回过头再来考虑。如果我们可以实现同一个用户到达固定的tornado实例,看来还可以带
|| 来很多其它好处,当然,这得另说了。

|| 缓存多了维护就是问题,不管是 mc 还是 redis 。但是如果缓存做到了应用本身当中,
|| 问题稍有不同,因为这些缓存可以自我维护。比如你可以在 ioloop 中以 5 秒为间隔去
|| 检查一些东西,以决定缓存中的数据如何处理。

| 相反,缓存如果做到了应用本身当中,才是问题的开始(如果你不明白的话)。
| 你以为tornado不需要锁么?我记得谁吐槽过异步协程无锁模式,所谓无锁,是指在上下
| 文切换之间不需要上锁。什么时候发生上下文切换?
| 抱歉,你去做sql的时候也会上下文切换的。
| 我们在缓存的维护上一直有个竞争问题。例如一个session,同时在两个上下文中访问,
| 一个对用户金钱+1,一个对用户登录时间+1,最终就只有一个生效的。这是可能的,做过
| 竞争访问的都应该明白我在说什么。

| 如果你在sql访问前读取本地缓存,sql访问完了对其进行保存,刚刚那个问题一样会跳出
| 来。
| 也就是说,即使是异步模式,即使是本地缓存,也避免不了竞态访问的问题。那你逞论控
| 制呢?

从sql这里开始说。

>> 你在sql访问前读取本地缓存,sql访问完了对其进行保存

在这期间没有对数据的读取操作,就不会有问题。如果保证没有对数以的读取操作。

如果我对 tornado 的理解没有错的话,因为它的单进程模式,在一个请求的处理过程当
中,如果你没有做一些“弹出式”的操作(比如加上 tornado.gen.engine 做了一个
yield ,把 _auto_finish 设置成 false 时 handler 的方法返回了),那么整个过程就
是同步的,它就是安全的。比如代码中给的 chat room 的例子,在一个实例中可以对
self.__class__.message 安全地操作一样。

|| 另如有项目,有很多需要使用 cron 做的事,那完全可以全在tornado应用中自己解决,
|| 控制力度绝不是 cron 可比的。(我可以起10个实例,6个用于处理用户请求,4个专干以
|| 前 cron 干的事)

| 崩溃。。。如果你都起了这么多实例,何必让那4个跑tornado。。。你不会让他们直接作
| 为daemon跑么?python的daemonized只需要15行。。。

你可以认为我就把这 4 个 tornado 当成一个 daemon 在用吧。这有什么不好的问题,或
者不合适的地方吗?

还是说你说的 15 行比在项目中统一做一些 add_timeout 方便?我觉得没有,所以选择
了在 tornado 中实现。如果不是这样,我就选择你的那 15 行。就是这样。

|| 我想说的,只是,可能性,选择,方便。所以我说 tornado 这种单进程的模式与传统
|| web 框架的那种模式是不同的,可以做很多有趣有意思的事。

|| 是不同,不是更好。

| 如果你硬要说单进程的话,你以为uwsgi不能在单个进程中开启一个http服务么?理论上
| 甚至可以在uwsgi里面跑的python程序里面exec一个nginx来给自己做反向代理。毕竟
| uwsgi只是框架,但是做什么是代码的事情。你要跳出框架,我曾经在一个wsgi+django里
| 面整了一个爬虫进去。在你点一个按钮后去跑一个爬虫线程。为什么不可以?

| 我也可以让uwsgi的进程开一对pipe,fork一遍,然后spawn一个vncserver,把内容灌到
| 自己的pipe里面。然后用持续异步模式(这到真没法用uwsgi了,当然,tornado也不行,
| 你要用我写的框架才行)或者被动通讯模式和一个页面控件通讯,为什么不可以?

还是我们在这个邮件中说的,最开始的那个例子的问题吧。先看那个。

再者,我并没有跳出框架。(如果是选择tornado干一些事,在系统上就需要前面的nginx
配合的话,那就算是跳出框架了吧。没关系,我只是关心能实现的东西,想看到我之前没
见过的东西)

当然,如果你说 uwsgi + django 也做了一些有意思的事,我当然是很感兴趣,还请赐教。
因为在我的概念当中,它们和传统的web运行方式没有区别(请求与请求之间是完全隔离的)

||| 我们金工实习的时候就学一种车床,理论上所有其他机床原理都一样,老师教一遍大家都
||| pass了。车床学不会的,铣床一样没戏。当年我们还用车床刻字,用线切割做名字吊牌
||| (那台可怜的线切割床跑了好久,最后我被老师骂了一顿)。估计给一台啥都学学能玩起
||| 来——我金工时期只有两个项目得了中,一个是卧式铣床,靠紧没做好,粗心了。一个是钳
||| 工,手里没劲,到最后都没做完。。。
||| 同样,熟悉我的知道我是emacs用户,linux用户。但是我也会用vi,有mcse证书,
||| windows编程水准还凑合,写写dll hook,注入溢出也可以。我讨厌.net,但是我当作两
||| 年的.net项目经理,拿.net写过算法——我倒现在还没搞明白.net到底是怎么回事。
||| 我想问的是,框架真的有那么重要,值得你们一天到晚吵来吵去?难道学一门框架就这么
||| 难?尤其有熟悉这门框架的人在旁边辅导的情况下?这可不是我一个人的观点,

|| 首先,我没有说框架重要。我只是在说单进程与传统web框架之间的差异。这怎么说呢。
|| 一个不太合适的例子,就像编辑器,从整体上看,它不重要。用自个儿熟悉的高效地完成
|| 自己的想法即可。但对个人而言,你习惯了用 A,但又必须去用 B 的时候,我觉得问题
|| 就变得重要起来了吧。

| 我真的不觉得这个问题很重要,我不喜欢vim,可这不妨碍我用vim改emacs的启动脚本。
| 因为emacs自己改完了启动脚本往往会搞得自己没法启动!!!

一码是一码。我不喜欢windows也不妨碍我在上面玩游戏 🙂

|| 其次,也没有争执,至少我真没有因为框架问题争执过什么。(很多框架我根本不熟悉,也没有发言权)

|| 再次,关于学习框架的问题。学会用一个东西,和在学习的过程中得到一些启发,程度还
|| 是不同的吧。我们怎么讨论到这问题上了……

||| 今天老潘还在微薄上吐槽,python ui开发群里面安安静静,web开发群里面在搞框架选举。
||| 如果框架真的那么重要,那程序员的plus版本就不该叫架构师,应该叫框架师。你都用
||| python了,我觉得你完全可以省下在语言里面纠结的时间,学一门框架。省下进行框架选
||| 举的时间,再学一门框架。

||| 反正我们有胆子招个好的程序员,不会用python也行。给你一周时间,看个大概,然后把
||| 你放在我隔壁。我有信心,最多两个月,你完全可以胜任我们的工作。所以我上面再三说。
||| 我们招个程序员,不招python员。

|| 你都说是“好的程序员”了。即使如此,两个月后,一个全新的项目,你敢让他独自一手
|| 做起来吗?我觉得这和你教导得怎么,以及他自身能力如何都没有关系。我也认同框架不
|| 重要,语言不重要。但是,积累很重要,经历很重要。这样说的话,tornado/gevent 这
|| 些单进程,异步的东西,也算是一种经历吧。

| 没问题啊。为什么不可以。我觉得好的程序员其实用不着我教他python。我只需要告诉他,
| 哪里可以拿到他需要的东西,仅此而已。

你要这样说的话,问题就没边了。我们得从 “好” 的定义谈起了。没必要。

| 如果说有什么单进程可以做而多进程做不了的事情,那就是ipc。但是tornado可以支持的
| 事情,你开一个uwsgi难道做不到么?如果一个uwsgi性能不够了,tornado又能支撑多久
| 呢?uwsgi还有另一种让人吐血的模式,在同一个进程内跑两个python解释器。我没见过
| 这种模式跑的,估计是因为有很多内存引用块会在两个解释器中乱窜。但是这也并不是不
| 可以。

所以我对你说的,开一个 uwsgi 能做的事,真的有兴趣。等你的例子。谢谢。
* Shell Xu
问题是,由内核调就不需要一个个遍历fd了么?实际上我们不大可能真的在网卡中断发生
时,在中断响应过程中直接找到对应的fd,然后调度到这个线程。因为搞不好这个线程响
应到一半,我们还得去一下中断。因此你的响应程序就必须是可重入的。

如果由内核直接回调,其实是有这么个东西的,叫做内核中服务器。就是把程序全写到内
核态去。我记得这变态事情真有人干过,代码要求就是可重入。
* Zephyr
| 我会写个wsgi分离代码,将两个url映射到不同的框架里面去执行,或者干脆在分发层搞定。
| 你用你喜欢的框架写,我用我喜欢的。mc的读写上要保持一致,其他没啥问题。

这是可以解决问题。但是代价是产生两套不同的东西。从业务实现层面,是否需要两套东西是个问题。

比如业务需要两套系统,一套 Python 一套 Java 实现,没关系。但是如果你硬是因为个
人原因,而把 A 系统拆成 两套部分实现,这不是好的做法,也不便维护吧。

还是看实际需求的事。因为个人原因,而硬去做本来不应该做的事,我觉得比较不合适。
* Shell Xu
第一个我就不给出完整代码了,给你看uwsgi的配置你应该明白我的意思。

shell@debox:~$ cat /etc/uwsgi/apps-enabled/boxsrv.ini
[uwsgi]
plugins = python
workers = 1
reload-on-as = 64
touch-reload = /var/web/boxsrv/RELEASE
chdir = /var/web/boxsrv
pythonpath = /usr
module = main

shell@debox:~$ cat /etc/uwsgi/apps-enabled/hosts.ini
[uwsgi]
plugins = python
workers = 1
reload-on-as = 196
touch-reload = /var/web/hosts/RELEASE
chdir = /var/web/hosts
pythonpath = /usr
module = main

然后在nginx里面
location ~ ^/(…..)/ {
include uwsgi_params;
uwsgi_param UWSGI_SCHEME $scheme;
uwsgi_pass unix:/run/uwsgi/app/hosts/socket;
}

location ~ ^/(…..)/ {
include uwsgi_params;
uwsgi_param UWSGI_SCHEME $scheme;
uwsgi_pass unix:/run/uwsgi/app/boxsrv/socket;
}
* 风间星魂
很不幸,我们公司就出现了这种状况。
一个小系统2人开发,我是其中一个,另一个要用.net,我拒绝使用,然后我提议把系统
拆成2部分。
她用.net 我用python。。。。。。。
* Shell Xu
这种杯具是每个程序员都会碰到的,如果你觉得一个玩意恶心,又有人非要用,我们只有几个方案。
1. 你学
2. 他学
3. 你走人
4. 他走人
5. 拆开

另外,奇怪,不是你说单进程可以做很有有趣事情么?让我举例干吗?我只是说,不提性
能就别提tornado。说单进程,uwsgi也可以。说提供http服务,python自带。
* luzi
django基本上被认为是100W级的产品,所以它的价值更多的在于开速开发。
每个工具所擅长的东西是不一样的。

* Zephyr
|| 从sql这里开始说。

|| >> 你在sql访问前读取本地缓存,sql访问完了对其进行保存

|| 在这期间没有对数据的读取操作,就不会有问题。如果保证没有对数以的读取操作。

|| 如果我对 tornado 的理解没有错的话,因为它的单进程模式,在一个请求的处理过程当
|| 中,如果你没有做一些“弹出式”的操作(比如加上 tornado.gen.engine 做了一个
|| yield ,把 _auto_finish 设置成 false 时 handler 的方法返回了),那么整个过程就
|| 是同步的,它就是安全的。比如代码中给的 chat room 的例子,在一个实例中可以对
|| self.__class__.message 安全地操作一样。

| 访问数据库的时候,这个进程需要阻塞么?

如果要安全,就需要阻塞(这样是一种方法,达到“保证这期间没有数据的读取操作”这
个目的)。否则安全问题需要另外考虑。

| >> 另外,奇怪,不是你说单进程可以做很有有趣事情么?让我举例干吗?我只是说,不
| 提性能就别提tornado。说单进程,uwsgi也可以。说提供http服务,python自带。

我之前的概念是,tornado能做的事,uwsgi + web.py/django 不可以做。你可以通过一
个例子,来告诉我,我的概念是错的。说是你说的,单进程,uwsgi 也可以。

我去补课看你的配置。
* yunfan
其实既然已经用到了zmq 还有什么好争的呢 想用啥都可以 完全不影响他人 :] 只是后端
要确保完整控制在某个方案里

不是还有lua么 ;D
* Zephyr
| 另外,奇怪,不是你说单进程可以做很有有趣事情么?让我举例干吗?我只是说,不提性
| 能就别提tornado。说单进程,uwsgi也可以。说提供http服务,python自带。

下面是一段 tornado 中的登陆逻辑的一部分。

做的事是如果某个用户在一个时间段内登陆太频繁,就要处理。

我是真的不知道,在 uwsgi + django/web.py 环境下,不引入额外的机制如何实现。

class AuthHandler(BaseHandler):

SUPPORTED_METHODS = (“GET”, ‘LOGIN’, ‘LOGOUT’, ‘CAPTCHA’)
LOGIN_COUNT = {}

def _reduct_login_count(self, username):
self.__class__.LOGIN_COUNT[username] -= 1

def login(self):

username = self.get_argument(‘username’, ”)
password = self.get_argument(‘password’, ”)
session = self.get_argument(‘session’, ”)
captcha = self.get_argument(‘captcha’, ”)
next = self.get_argument(‘next’, ‘/’)

cls = self.__class__
if username in cls.LOGIN_COUNT:
cls.LOGIN_COUNT[username] += 1
else:
cls.LOGIN_COUNT[username] = 1
IL.add_timeout(int(time.time()) + 60,
functools.partial(self._reduct_login_count, username))

if cls.LOGIN_COUNT[username] > (self.settings[‘constant’].MAX_LOGIN_COUNT + 10):
#不发邮件了
return self.write({‘result’: -1, ‘msg’: u’登录频繁, 账户被临时禁用’})

if cls.LOGIN_COUNT[username] > self.settings[‘constant’].MAX_LOGIN_COUNT:
from PROJECT.lib.smtpclient import SMTPClient
from email.mime.text import MIMEText
from email.header import make_header
import email.utils

msg = MIMEText((u’账户: %s, 最近一分钟尝试登录次数: %s’ % (username,
cls.LOGIN_COUNT[username])).encode(‘utf8′)
, _subtype=’html’, _charset=’utf8′)
msg[‘Subject’] = make_header([(‘账户暴力登录报警’, ‘utf8’)])
msg[‘From’] = ‘<test@test.net>’
msg[‘To’] = ‘,’.join([(‘<%s>’ % x) for x \
in self.settings[‘constant’].MAX_LOGIN_NOTIFY_MAIL])
msg[‘Message-ID’] = email.utils.make_msgid()
msg[‘Date’] = email.utils.formatdate()

client = SMTPClient(SMTP_HOST, SMTP_PORT)
client.send_mail(‘test@test.net’, self.settings[‘constant’].MAX_LOGIN_NOTIFY_MAIL,
msg, lambda code, msg, res: None, lambda code, msg,res: None)

return self.write({‘result’: -1, ‘msg’: u’登录频繁, 账户被临时禁用’})
* Shell Xu
照做不就行了。
uwsgi不是每次都初始化一遍module的,你把你要存的变量放global里阿。
* @@
你这个很占内存啊。相当于所有登录用户名都在一个dict里了
* Zephyr
| 照做不就行了。
| uwsgi不是每次都初始化一遍module的,你把你要存的变量放global里阿。

ioloop 中的 减少计数 的操作呢?

| 你这个很占内存啊。相当于所有登录用户名都在一个dict里了

是这样的。能承受这个才能使用这种方案嘛。

| 另外,奇怪,不是你说单进程可以做很有有趣事情么?让我举例干吗?我只是说,不提性
| 能就别提tornado。说单进程,uwsgi也可以。说提供http服务,python自带。

另外一个例子。在渲染模板时需要把总体不多,变化不频繁的实体的id显示成name。(我
知道我可以从数据库查,某时也可以多做一下join)

class NameMap(UI):

MAP = dict([(x, y) for x, y in Name.objects.all().values_list(‘id’, ‘name’)])
last = 0

@classmethod
def refresh(cls):
‘每隔1小时刷新一次’
now = int(time.time())
if now – cls.last > 60 * 60 * 1:
cls.MAP = dict([(x, y) for x, y in Name.objects.all().values_list(‘id’, ‘name’)])
cls.last = now

def render(self, v):
if not v:
return ”
if v not in self.__class__.MAP:
obj_list = Name.objects.filter(id=v)
if not obj_list:
return v
else:
self.__class__.MAP[v] = obj_list[0].name
return self.__class__.MAP[v]

tornado.ioloop.PeriodicCallback(NameMap.refresh, 1000 * 60 * 10, io_loop=None).start()
* Shell Xu

好吧,我大概想了一下,你们是不是在纠结,万一公司里这堆框架你们没学过咋办?

我们的答案是,放心吧,这个问题我们考虑的比你多。去一个公司,要技术栈完全符合你
的技术栈的概率,我觉得很低吧——除非对方使用django。左右也是要学,你学的成本这事
情是我们来负责的。

我们招人进来,大部分都是要培养的。我现在的老板原来是做培训的,我是他培养出来的,
我后面的哥们也是。我们说培养你,不是说说而已的。你技术差一点,没事,只要感觉对,
我们会有一定的培养期的。你要学什么东西,有人教。公司还会定期组织互相培训,或者
请培训公司来做培训(233,多半都是老板前同事)。你的代码,有人review(多半就是
我了),有人告诉你这样写有什么问题,怎么改进。你要是牛,可以,我们会让你来做培
训。

python这个东西,学习成本非常低。学框架的成本也远远比你想像中低。我们招一个
python程序员,可以只签一年(公司标准是签三年的)。开始的三个月,熟悉公司,熟悉
技术(但是不是不干活了)。后面的九个月你要干活。我们还招系统管理员(高级点可以
叫系统分析师,装B用的),一般预计的培养时间就是一年,签三年刚刚好。招C程序员,
就很难考虑培养了。不是没人来培养你——我们还有个前大学教授——问题是培养一个C程序
员大概要两三年,你说这个合同周期该怎么签?所以我们的C程序员招聘要求是最高的,
一定要有相当经验,待遇当然也是最好的。

有些人会疑虑,万一培养好了,这个人要求走人/加薪怎么办?公司每年会谈一次薪水,
做适当调整,合同到期后,会重新谈合同。愿意走可以走,我们只是培养你,又不是生了
你。即使生了一个人,也不能妄图操纵他的人生。但是我们公司到期后离职的相当少(主
要是公司福利好阿)。过了试用期以后离职的人中,大部分人的离职理由是出国留学,例
外只有两个。

我们最恨的,就是签了合同刚过培养期就走的人。公司培养一个人,付出的不仅是费用而
已。还有赌这个人在培养周期结束后,可以适任的风险。以及把一个可能有技术缺陷的人
放在一个要求更高岗位的成本。有培养就要干活,我觉得这个得算IT行业的职业道德。出
于个人原因还算无奈,为了高薪跳槽我觉得就是职业不道德。

开个后台线程吧。用入口触发超时也可以。

你说的是不是定时callback?这个直接开后台线程就可以了。如果你觉得不安全,可以在
入口处判断,全局值超时就触发。
区别在于,入口触发机制是“超过”这个时间触发,而不是精准触发。而后台线程则是基
本精准触发。
tornado的机制是计算最早的一个触发时间,然后在select中设定这个时间,在事件触发
和超时触发中最调度。但是你可以实验一下高压力下的情况,触发依然是略略超时的。
而系统级别sleep则相反,sleep会被信号打断,所以可能反而低于要求时间。
* 风间星魂
是这样,但还缺一点,你愿意培训,我还不愿意学呢。
因为同层次的技术栈对个人毫无意义!
* est
Shell Xu有两个问题:

1. 你说的 comit 是不是这个 come ?
http://en.wikipedia.org/wiki/Comet_(programming)

2. 生产环境下uwsgi用 domain socket 的并发性能是不是没有tcp 好?

django唯一比较好用的部分其实只有默认admin我会到处乱说么。。。
* Zephyr
| 你说的是不是定时callback?这个直接开后台线程就可以了。如果你觉得不安全,可以在
| 入口处判断,全局值超时就触发。
| 区别在于,入口触发机制是“超过”这个时间触发,而不是精准触发。而后台线程则是基
| 本精准触发。
| tornado的机制是计算最早的一个触发时间,然后在select中设定这个时间,在事件触发
| 和超时触发中最调度。但是你可以实验一下高压力下的情况,触发依然是略略超时的。
| 而系统级别sleep则相反,sleep会被信号打断,所以可能反而低于要求时间。

我大概明白你的意思了。

通过在 uwsgi 的单个进程,module 层的 global 可以做到请求与请求之间得到某种程度
上的同一个运行上下文。在这里可以开后台线程自己做计时器。

那么,这种情况下,如果要能同时接收多个请求,并且在请求与请求之间还有交互,是否
意味着需要自己做一个多线程的环境来处理呢?

比如这个例子,一个请求,总是返回它的下一个请求所带的参数:
# -*- coding: utf-8 -*-

import tornado.httpserver
import tornado.ioloop
import tornado.web

IL = tornado.ioloop.IOLoop.instance()

class IndexHandler(tornado.web.RequestHandler):
CONN = None

@tornado.web.asynchronous
def get(self):
cls = self.__class__
if cls.CONN is not None:
cls.CONN.finish(self.get_argument(‘a’, ”))
cls.CONN = self

Handlers = (
(“/”, IndexHandler),
)
class Application(tornado.web.Application):
def __init__(self):
settings = dict(
debug=True,
)
tornado.web.Application.__init__(self, Handlers, **settings)

def main():
http_server = tornado.httpserver.HTTPServer(Application(), xheaders=True)
http_server.listen(8888)
IL.start()
if __name__ == “__main__”:
main()
* Shell Xu
不需要。
uwsgi的请求基本是顺序的。所以一般建议开cpu*2个。

1,对,拼错。
2,生产环境中,uwsgi不并发,依赖前方分发器并发。

那就没办法了,总不能强迫别人学吧。
* Zephyr
所以,我给的这个例子,它应该如何做呢?
* Shell Xu
我觉得有空了可以开个主题,如何在受限环境内编写和使用异步协程框架。

* Jason Wu
同步模式就是阻塞模式?还有select/poll/epoll这种io轮询的方式并不是异步,建议你
看一下unp,先把这些经典的书籍看完了,再发表高见,不要误导这个社区里的很多新人

建议大家在学习的过程中还是先看一些国外的经典书籍吧,不要只是在网上看了几篇文章
后加上自己的一些的理解就想当然了,你很有可能被国内的一些文章所误导,在开始的时
候,我也一样,把同步,阻塞,异步,非阻塞什么的混为一谈,并且还给别人讲这些东西,
现在想来真是惭愧,把一些错误的东西教给别人,现在对于自己不明白问题,都是多方求
证,把问题搞得透彻再讲出来,半懂不懂的东西不要拿出来讲
* Shell Xu
首先致歉,这几个确实有区别,没区别也不会是两个名字。但是我在使用过程中基本没加
区分,因为差异很难辨析。

这两天比较忙,没空看上面的代码了,不过可以结合早年写的一个单线程单进程异步仅网
络非阻塞模式协程框架给大家讲一下区别。。。。。再确认一下,名字应该没错吧。没错。
* laozh
作为菜鸟一枚的我就是认为同步就是阻塞,异步就是回调。很是希望哪位能贡献出几个好
的能点击的URL。或者更狠点,提议社区里面搞个专题,谁有贡献精神可以分享分享经验。
方便我们这些新人学习,让我们少走点弯路吧。。。。。。
* Shell Xu
首先,异步和同步,是指编程中,你是否需要确认你所申请的操作已经完成。而阻塞和非
阻塞,是指在调用函数时是否需要等待。两者听起来似乎一样,实际上差别也很小。但是
两者不是一回事。非阻塞调用返回时,不但无法确认操作完成,实际上甚至不能确认操作
进行。errno很可以为EAGAIN,解释是IO被打断或者资源未就绪。因此需要等待资源就绪
时再调用。按照组合,我们有四种可能的模式。

1. 同步阻塞模式。很好理解。当你调用函数时,需要等待,当函数返回时,你可以确认
操作完成。
2. 异步非阻塞。也很好理解。当你调用函数时,立刻返回,函数不能被确认执行,也不
能确认执行完毕。
3. 异步阻塞。其实阻塞模式也可以异步的。怎么做?在每次读写前,你都需要用select
确认fd已经就绪。读写调用发生的时候,就会立刻返回,而不会在IO上发生阻塞。
4. 同步非阻塞。非阻塞怎么同步?你忙等待或者睡眠等待就可以。

为什么通常都是同步阻塞模式?因为这个比较符合人的常规思维方式。我要读一个数据,
数据读到了,返回。如果我要读一个数据,返回,数据可能读到了,也可能没读到,甚至
可以没开始读,你要自己做相应处理,你不觉得很崩溃么?

但是效率真正高的,却是异步非阻塞模式。原因我上面有说。同步模式是依靠将上下文状
态(linux就是进程队列状态)从R转换为D,从而阻塞在fd上。在D转为R之后,加入调度
队列。两次上下文切换,效率打折扣。加上由于为了提高效率,使用多线程访问数据。线
程随时可以切走,所以要加锁。加锁就要加内存栅,打乱CPU执行,还有大量的争持,更
低效。大量fd对应大量上下文,占用大量进程表项,愚蠢。

这里要提一样东西,select/poll/epoll。这三个东西的正式名称其实叫做就绪检测,他
们和异步没有关系,只是检查你的fd里面,有哪些处于就绪态,哪些未就绪。但是这三个
加上非阻塞socket,就是通常异步非阻塞的基础。为什么是非阻塞?因为异步模式一般提
倡一个进程只有一个线程,这样没有资源争持问题。如果在某个fd上,不幸发生了阻塞。
那么其他人就要干瞪眼直到地老天荒。

在使用就绪检测的编程中,常规是一个超大循环。while (1) {}来覆盖整个调度。里面是
一个就绪检测,select/poll/epoll。然后你可以获得就绪fd数组,包括哪些fd可以读,
哪些fd可以写。然后是一个非常繁琐的编程过程。对于可以读的fd,你要做一个读调用,
将数据附加到原本读取的数据缓冲区,再交给处理函数做完备性检查,是否可以处理?不
行,继续等待。可以,处理数据,并转换到下一个状态。对于可以写的fd,你要检测是否
有缓冲区。有,写出。没有,唤醒处理函数做处理,填充缓冲区,或者转换到下一个状态。

我们以http协议解析的数据读取代码为一个很小的例子。在select告诉你,某fd可读,之
后。你首先要读取这个fd,然后检测数据是否已经填充满头部(方法是持续扫描直到发现
一个空行)。没有满,继续等,满了,开始对头部的处理。首先判断是否需要body,需要
交给哪个handler处理,需要转发还是返回文件。。。等等等等。只要需要数据的时候没
有数据,就必须返回,直到数据充满,再从这种状态的入口开始工作。这就是io复用的最
早模式。

这个模式当然很纠结。经过封装,这个模式可以变成reactor模式,但是一样还是很纠结。
reactor模式可以为你检测缓冲区某种状态的数据是否就绪,但是就绪后处理必须是一个
函数。从头到尾跑完,转到下一个状态。思路很不连贯。

因此现在流行的模式是协程,python里面就是用greenlet。我们先简化一下假定,假设
socket是不会出错的,greenlet是没有异常的。这样比较简单,否则单是greenlet出错时
的奇怪特性就足够弄死大家了。

协程使得异步编程简化的原因在于,他简化了io复用时的中间状态保存。本来我们每次都
要保存数据和状态,然后call一个过程。协程可以为你保留整个上下文栈,直到下次继续
时。我们可以来看一下recv函数的实现。

def recv(self, size):
”’ 对原生recv的封装 ”’
while True:
try:
data = self.sock.recv(size)
if len(data) == 0: raise EOFError
return data
except socket.error, err:

if err.args[0] != errno.EAGAIN: raise
ebus.bus.wait_for_read(self.sock.fileno())

EOF那个是我的习惯,其实和默认的行为不符。不过我自己每次对socket都要做recv之后
的EOF抛出,所以干脆加到框架里面,用着方便。
注意到socket.error,如果正好是EAGAIN,这就是数据未就绪下的非阻塞调用。 我们已
经假定了没有异常(当然,产品不能这么粗糙),暂时就这么理解。此时我们就将当前
greenlet加入当前fd的读等待队列中,并且将调度交给主gr。

主gr的schedule函数只有两行。

while not self._switch_queue():
self._load_poll(self._fire_timeout())

其中,_load_poll是这么写的。

for fd, ev in self.poll.poll(timeout):
# print ‘event come’, fd, ev
if ev & epoll.POLLHUP:
self.throw_gr_exp(self.fdwmap.get(fd, None), EOFError)
self.throw_gr_exp(self.fdrmap.get(fd, None), EOFError)
self.unreg(fd)

# TODO: close here
elif ev & epoll.POLLIN and fd in self.fdrmap:
if self.add_queue(self.fdrmap[fd]): break
elif ev & epoll.POLLOUT and fd in self.fdwmap:
if self.add_queue(self.fdwmap[fd]): break
else: self._setpoll(fd)

主要目标只有一个,检测哪些fd处于就绪状态,找打阻塞在对应读写上的gr,加入调度队
列。注意,这里的实现上有个问题,我没有考虑两个gr同时阻塞在同一个fd上的情况。不
过我认为粗糙点,可以暂时不考虑这个,对理解流程没有问题。_fire_timeout则是这样
的。

t = time.time()
while self.tol and t > self.tol[0].t:
next = heapq.heappop(self.tol)
self.throw_gr_exp(next.gr, next.exp)
if not self.tol: return -1
return (self.tol[0].t – t) * timout_factor

基本就是检查当前队列中是否有未timeout事件。如果有,则加以触发。直到没有,则返
回下一个timeout的时间。由于python是应用序语言,因此_fire_timeout先于_load_poll
执行,timeout先于队列调度发生。

最后的_switch_queue就是切换到等待队列中,整个系统基本就是这个样子。

如果不考虑异常,写一个异步协程框架其实是非常简单和轻松的事情。一般能在500-1000
行内解决问题。

另外,异步协程模式是否只能用于网络编程?不一定。凡是你能够检测就绪,转换调度的
地方,都可以使用此种模式。例如,你可以写一个后台线程,接受前台的event事件,然
后去向某个X发一些消息,例如记录X里面所有的窗口。如果你有办法能够检测X请求和返
回的就绪状态(本质说还是个fd就绪检测,不过一般是C级别的),就可以在未就绪时转
换到主循环内继续等待下一个event,从而实现单线程多上下文调度。

因此,当然,我们也可以在uwsgi里面启动一个后台进程,让他等待在任务队列上。然后
我们写一个很简单的app函数,将请求的所有的数据全部放入任务队列。。。你们知道我
要干什么。

如果更复杂一点,要在多进程间公用一个监听fd进行并发,那么还要注意一点。fd是可继
承的,poll对象是不能继承的。继承使用poll对象会导致执行出问题。所以你需要先
socket建立,然后bind,然后fork,再进行poll的建立和注册。

关于更多c10k的总结,这篇文章更加全面一些。

http://www.kegel.com/c10k.html

同时 ,是不是没有就绪检测,就不能进行大并发网络编程?不是的,上文的5列了一种最
致命的模式,直接将代码编入内核。这可真是杀手级方案,可惜有胆子这么玩的人还真不
多。
* 依云
谢谢 Shell 的讲解,不过我还有个疑问。poll 等与同步 fd 的组合与异步非阻塞
区别大吗?至少在 socket 上,阻塞式的 socket 在已知可读/写时调用一次
recv/send 是不会阻塞的吧?

另外,我前些天才知道,异步非阻塞还适用于 inotify 呢。
* Shell Xu
没什么太大区别,主要是如果这个fd被继承过了/同进程内线程,可能有其他上下文在竞
态读写。非阻塞的有机会让你修正这个问题,阻塞的就干瞪眼。
不过这种可能性微乎其微,至少我没见过。
* 赵帅
我觉得同步异步 阻塞非阻塞 不是同一个抽象层次的概念。
同步异步是一种比较泛的逻辑上的概念,凡是一个执行序列在进行某个操作要继续进行前
都需要等待这个操作的结果都可称之为同步,反之只是申请做一个操作,等待通知完成就
是异步。
阻塞非阻塞 是操作系统上的一种概念,表示请求的计算资源未就绪时,被操作系统挂起
或马上返回。
* simon zhang
我来说几个问题:

1、这是人家招聘的帖子。大家怎么都讨论到这来了。给人家点空间。

2、Shell Xu兄的第一个长篇我拜读了,后面大概看了。有个问题,tornado什么时候有协
程模块了??Gevent这玩意是有协程。我想我对tornado还是比较了解的,因为一些特别
用途我用c/c++重写了tornado的底层模块,除了映射和模板还是tornado原有的外。这两
块我没怎么看。

3、Shell Xu对同步异步和阻塞的解释在误导新人。网络IO模型有5种,分别是:阻塞I/O、
非阻塞I/O、多路I/O复用、信号驱动、异步I/O。大家可以百度”网络IO模型”。tornado严
格来说是多路I/O复用模型的实现。事实上所有epoll的使用都是多路IO复用,只有win的
IOCP才真正实现了异步IO模型。Shell Xu的解释基本是网上以讹传讹的产物。。下面链接
是我在百度随便搜的
http://blog.163.com/kevin860@126/blog/static/129555995201021154640116/

4、这不是针对Shell Xu兄,因为Shell Xu写得最多。所以我看得最多。随便评论下。事
实上我个人以前对各种异步和阻塞纠结了很久。

5、我有时也想在我的项目中加上协程的支持,实在对那些回调函数头痛万分。但是来回
于c/c++和python之间,对协程的行为实现及其对GIL的影响还是有些迷糊,我想也许要花
上不少时间去研究,况且协程还有栈的切换效率损失不是。所以安慰自己先不要用了。

如果哪位同学对c++写的python模块中实现协程有研究,我倒希望可以交流下。
* 风间星魂
win那个iocp没有内核源码,无法证明是真正的异步。
也可能是类似aio和apple dispatch其它手段模拟包装接口。
linux和bsd肯定没实现真正的异步。
* Shell Xu
我从没说tornado有协程,那玩意算是回调模型的代表。用协程的是gevent,我更喜欢用
这个。
如果你要在linux里面使用异步io模型,可以看我给出链接里面的一章,在linux kernel
中增加异步io接口。
greenlet是个暴力分子,基本就是分配内存块,复制栈和状态。基本就是cpu上下文调度
的手法,除了每次都复制内存外。你试过在切换上下文时直接调用gr的接口么?

那个应该问题不大。毕竟这个比较重要,有那么多人能看源码。虽然他们都签了协议,但
是没听说谁有什么意见,这个能说明点问题。

另外,我想起个事。我从头到尾都没提异步io,怎么跳到异步io来了?
* est
| > 上文的5列了一种最致命的模式,直接将代码编入内核。这可真是杀手级方案,可惜有
| 胆子这么玩的人还真不多。

windows下就有个 http.sys,IIS和 .NET 的http栈都是基于这个。网上说IIS性能烂是
license限制了的。XP专业版默认只允许5个连接。。。

其实要说IO性能最好的还是IBM那个apache patch,硬盘的buffer直接丢网卡buffer,都
不需要经过内存得
* 风间星魂
一般人怎么玩的起内核panic的风险。
特别是linux这种宏内核。。。
* shhgs
http://en.wikipedia.org/wiki/Asynchronous_I/O

Asynchronous I/O, or non-blocking I/O, in computer science, is a form of
input/output processing that permits other processing to continue before the
transmission has finished.

所谓的多路复用和你说的异步,其实都属于Asynchronous I/O
* Yin Wenyan
关于”同步是不是阻塞”,”异步是不是非阻塞”,及”IO多路复用是不是异步IO”得看上下文。

一种上下文是UNP中所讲的Unix下的5种IO模型(blocking, nonblocking, multiplexing,
signal driven, asynchronous),这时候:
1. 同步或异步是POSIX中所定义的术语,是对IO操作/函数而言的, 如:recvfrom函数是
同步的,aio_read函数是异步的
2. 阻塞或非阻塞是对socket(通过fd来标识)而言的,如:默认情况下socket都是阻塞
的,但是可以显示地设置成非阻塞模式
3. 5种IO模型中的前4种(包括IO多路复用)都使用同步IO操作(如recvfrom),归类为”同
步IO”;而第5种则使用异步IO操作(如aio_read),归类为”异步IO”

另一种上下文是wiki中的解释(http://en.wikipedia.org/wiki/Asynchronous_I/O),这时候:
1. 异步 = 非阻塞
2. 同步 = 阻塞
3. IO多路复用是实现异步IO的一种方式
* simon zhang
。。。这个问题还是google清楚一些好。我要说明的是,区别还是很大的。
请确保能够明白epoll/kqueue和iocp的区别。。至少在现在的主流系统中混,这很重要,
特别是对混互联网的人来说。
总得来说,区别在socket的recv时间点不同。我相信很多地方有说明。

epoll(多路复用IO):当数据开始接收时recv.
iocp(异步IO):当数据接收完成时recv.

其实使用过会发现,效率也差别不大。但是想兼容这两种方式是头痛的问题。一直以来,
这都是很麻烦的工作,大部分库都是在windows部分使用select进行模拟。libevent2有实
现一种程度上的兼容。据说AIO有实现兼容,但是我没用过,因为AIO很重量级。
libevent2中如果想要使用也必须使用其提供的缓存库,只使用事件库的话还是使用
select模拟。至于另一个号称更快更轻的libev已经基本放弃了对windows的支持。
node.js另外做了个libuv库,是在libev的基础上,在 windows 下用 IOCP 另实现了一套。

gevent是基于libevent的。可惜这东西不但有内存泄漏,还是GPL的。据说准备改为libev,
但好像没看到这个版本。

这都取了不一样的名字。咋也有些区别!

有些错误。是boost::asio。。没用过这个库,名字都记错了。AIO就是linux中的异步IO,
但是据说速度比epoll还慢,所以基本没有人用。

gevent 的内存泄漏好像是因为他使用的一个GPL的库pyevent中有这个问题,有些记不清
了,这是libevent的python封装库。
* Goldfish Huang
gevent没有使用libevent。你说的应该是eventlet。gevent使用cython调用libevent。
* simon zhang
我并没有很仔细的研究过gevent的源代码,但其中有附带一份pyevent的协议文件
“LICENSE.pyevent”。

下面这些文字是从gevent中的core.pyx源码中复制出来的。奇怪怎么是BSD的,我记得
pyevent是GPL协议。

The code is based on pyevent_.

.. _pyevent: http://code.google.com/p/pyevent/
“””

__author__ = ( ‘Dug Song <dugsong@monkey.org>’,
‘Martin Murray <mmurray@monkey.org>’ )
__copyright__ = ( ‘Copyright (c) 2004 Dug Song’,
‘Copyright (c) 2003 Martin Murray’ )
__license__ = ‘BSD’
__url__ = ‘http://monkey.org/~dugsong/pyevent/&#8217;
__version__ = ‘0.4+’
* Goldfish Huang
那应该是我弄错了。

* Jason Wu
gevent之前是用libevent,现在换成libev了,libevent的bug比较多
* icaicai
基于libev版本的gevent

https://github.com/SiteSupport/gevent
https://github.com/SiteSupport/gevent/downloads

* Shell Xu
我解释一下异步这个东西。

首先说明一点,异步,和异步IO,是完全不一样的东西,至少对我来说不一样。请检查一
下我的上文,我记得我应该一直在说异步,而非异步IO。如果有用错(就像tornado,本
来想说异步,多了个协程),先行致歉。写的东西长了,就很难做到对全部内容一字一句
校对一遍以上。我大概看过一遍,但不能保证错误。

下面这篇长的和论文一样的东西,精确说明了什么是同步,什么是异步。

要说明异步,首先必须说明什么是同步。同步synchronous(syn共同,相同+chron时间
+ous……的→共同时间的),按照其来源来说,表意是时间相同的。异步和同步,是关于
并发的时间本质的最基本理念。这里,我对同步下一个个人的定义。

同步,即在某个时间点后,双方对某个事物的感知相互一致,并且双方知道对方的感知情
况。

当然,我们一般用的是弱版本,基本一致,而且经常只有一方知道另一方的感知情况。

异步和io没有关系,io和异步才有关系。io中经常碰到两个分离实体相互通讯的问题,同
步和异步是一个重要概念。但是异步概念的作用远远超出io,甚至超过程序领域。我们举
两个例子:

1. 异步应用设计。在提交一个好友申请后,不必等待对方通过,可以继续其他操作,直
到对方通过,才完成互相加好友的功能。在提交一个网页抓取请求后,不必等待网页
抓取完就返回。抓取请求在MQ中排队。

2. 电话是同步的,短信是异步的。

我们可以看到同步和异步的明显差异。如果某个过程对某两者是同步的,那么当过程完成
后,双方对过程的结果的认识相一致。例如电话。而如果某个过程是异步的,双方对结果
的认识不能保证一致,例如短信。

我们以一个例子来分析区别。你要通知同事去参加重要会议,然后为他安排机票。

1. 你是不能用短信的,就算用也不能光发就了事。如果你发送了短信,你无法确认对方
是否收到(双方对过程的结果认知不保证一致)。

2. 你可以用电话,只要打电话的技巧不算太差,一般都没事。

3. 如果非用短信,该如何做?让你的同事回一条短信是不行的。因为如果他不能确认你
收到了他的短信,他无法确认自己会有飞机票。

4. 严格的说,没有一种方法能保证同步,因为有两军问题的存在。

5. 除了程序员外,一般人不会想这么多。一条通知,一条回复,一条确认收到回复就可
以了。这就是tcp三次握手的原因。

而异步程序,则是指程序对某个请求不需要确认其结束。异步程序之所以特殊,是因为异
步一般,大多数情况下有一个不同于正常程序的要求——通知。

我们以DMA为例。DMA的使用是个典型的异步过程。当CPU使用DMA进行吞吐的时候,是不需
要等待直到吞吐结束的(那也失去了DMA的意义)。因此,DMA一般会触发一个中断,来通
告系统传输完成。同样的事情在http程序设计中会变得异常艰难——因为http是单纯的请求-回
应模式,并没有设计由服务器通知客户端的机制。因此人们想出了comet——将一个请求在
一半hold住。来保证通讯的进行。

传统来说,阻塞/等待模式的程序是同步的,CPU陷入IO过程中,因此返回时可以确认IO操
作完毕。信号/回调/中断/轮询是异步的,因为CPU并不等待在IO中。在这里,同步就是阻
塞的,异步就是非阻塞的。

另外注意,针对不同的双方,同步和异步的结论是不同的。例如某个文件加了缓冲,当写
操作完毕后,你无法确认数据是落了磁盘的。因此,针对磁盘和程序双方,这个过程是异
步的。而如果我们考虑另一个问题——有一个其他程序需要读取内容。那么你一旦完成了写
入,对方即时可以读出你的内容,哪怕没有落磁盘。因此,针对程序和内核文件对象而言,
写操作是同步的。

那么现在可以回到网络编程了。你会注意到下面有很多的杯具和奇葩。注意,下文我说的
网络编程,fd操控,基本都针对tcp而言。针对udp而言,我说的很多内容是不正确的。

首先需要明确,网络操作的同步/异步,说的都是发送/接受方和内核中tcp缓冲区之间。
至于数据什么时候发出,对方什么时候收到,这个事情不予考虑。熟悉tcp的应该可以想
到,数据到缓冲区,和数据发出是完全不同的,尤其是当你的tcp没有禁用nagle算法的时
候。

那么send这个操作是同步的么?

如果你设定了阻塞模式,那么send针对内核文件对象是同步的。一旦send返回,已经写入
的数据一定写入了内核文件对象——也就是tcp slide window打开。而在非阻塞模式下,
send——

——恩,不要想当然。下面是第一个悲剧。send在非阻塞模式下对内核文件对象也是同步的。
目前所有的socket操作,无论是否阻塞,都是同步的。唯一的区别在于,send不阻塞等待
缓冲区可用,立刻返回错误。

因此?因此什么?

从同步/异步这个术语原本的意义来说,如同UNP中描述的一样,前四个是同步的,只有异
步IO才是异步的。

那照着这个划分描述不就好了?

下面是第二个杯具,大部分情况下,我们并不是想区分异步IO和同步IO,而是想区分线程
在通讯时挂起和线程在通讯时不挂起。线程挂起的,我们需要用多线程/多进程技术来支
持并发,这样在超过1000并发的时候,服务器就会有问题。而不挂起的模式没这个问题

那使用非阻塞(non-blocking)来划分描述?

下面是第三个杯具。首先,如果我们认为阻塞,是“阻塞直到IO结束”,那么产生的划分
方式还是和上面一样。其次,“阻塞”(blocking)在socket编程中是个专用术语,他描述
的不是“阻塞直到IO完成”,而是“当IO不能进行时等到到IO可以进行”。最后,如果你
认为,既然目前阻塞这个术语描述的就是“当IO不能进行时等到IO可以进行”,那么显然
第一种模式是阻塞的,其他是非阻塞的。

很抱歉,如我上一篇所说,这两个又是没有关系的。通过小心的编程,用阻塞socket一样
可以完成“通讯时线程不挂起”。所以,想用阻塞这个词来描述阻塞是不行的。

那么,这东西又为什么会去叫异步呢?

下面是第四个杯具,如我们上文所说,传统上,阻塞/等待模式的程序是同步的,信号/回
调/中断/轮询是异步的。在网络上,尽管已经完全不是这么回事了。仍然有人管线程挂起
模式叫做同步编程,而后几个模式叫做异步编程。而且由于阻塞这个术语的歧义非常明显,
使用同步/异步来描述的人非常之多,几乎已经形成约定。

例如shhgs给的这篇文档(http://en.wikipedia.org/wiki/Asynchronous_I/O),实际上就
是如此划分和描述的。而gevent,尽管没有直接说,但是提到“provide a high-level
synchronous API”。结合上下文来看,他用的也是后一种称呼方式。

好了,现在谁能告诉我,如果我要描述一个方法,在一个上下文内可以对多个socket进行
操作,我该怎么叫?

@#¥%……&*()——+——『:》《》-【‘。。

不管了,我就管“阻塞/等待模式”叫同步,“信号/回调/中断/轮询”叫异步。而“调用
结束后能不能确认IO完毕”叫做同步IO/异步IO。上诉无效,就这么愉快的说定了。

之所以我一般不提异步IO模式,是因为linux下没有基于内核的异步IO API。在我上次给
的文档里面,列出了向linux内核增加异步IO的一些尝试(kaio)。但是我后来点了一下——
链接很多都打不开。至于libaio这种东西,纯粹是模拟出来让你用起来觉得像的,实际上
是用户态轮询模拟(当然,我没看过源码,但是kernel没提供api的情况下,他不会凭空
变出方案来吧)。

如果是内核级异步IO,内核到用户态的复制动作是内核来搞定的。这就是面simon说的
“接受完成时”和“开始接收时”的意思。但是其实,select时数据也接收完了,只是躺
在内核的tcp接收缓冲区里面,没往用户空间复制。从tcp协议栈原理来说,也不大可能出
现一个mtu没有接收完就开始通知fd就绪的可能性。因为tcp带16位的crc校验,必须等接
收完了,检验校验,OK了,才开始核对paws和seq。而通知就绪应当是ack发出(如果没有
delayack的话)之后的事情了。

不过要说明的是,UNP是模式。模式不是全部。我不想陷入关于模式的争论,但是特定情
况下反模式是听起来有意义的。例如在写文件服务器的时候,我希望将某个文件内容全部
发送到tcp上,可以用sendfile。这样可以节约两次复制和一份空间。当然,sendfile本
身是阻塞的,而且sendfile是不关闭文件fd和 tcp fd的。如果我修改内核,产生一个新
的sendfile2,可以非阻塞调用,而且调用结束时就关闭fd对内核句柄的引用。实际上在
调用这个函数后,就可以啥都不用管了。由于连通知/回调都消失了,因此尽管这是个绝
对无疑的异步api,却不能以异步IO来描述。这是前面所说的5,“内核中进行IO”的典型。

最后,额外的说一下,为什么需要“线程在通讯时不挂起”,以及我为什么看好异步协程。

常规同步通讯,是利用recv/send进行阻塞来工作的。当socket没有数据/没有缓冲时,就
进行挂起。当有数据时,再将上下文就绪。这样本身就有一来一去两次上下文切换。
linux通常上下文切换时间是5us,两次就是10us。10000个socket同时工作,就是100ms。

——如果仅仅是这点损耗就算了。问题是,当上下文增多的时候,每个上下文需要分配一个
栈(windows下1M,linux下2M,默认)来工作。32位用户态虚拟空间windows下2G,linux
下1G(这个不确定),啥都不干512-2048个线程就干掉了。每个线程又需要一个
task_struct,一个1.7k,10k个17M——内核空间,10k个内核表项。每个配备两个独立段寄
存器,又是20k个段寄存器表项。。。

所以很多程序压根没等到c100k,就耗尽了某些资源,回家重修去了。

当然,随着64位CPU的普及,内核模式的调整,还有内存的降价。每线程模式也许能够重
新火起来也未尝可知。不过多线程程序还有另外一个非常让我讨厌的特性,在下面我会说。

至于为什么我不是很喜欢到达回调,是因为到达回调的模式,要么不可阻塞,要么可重入。
你要么允许多个cpu同时处理到达回调,或者一个cpu在处理过程中被抢占重入,这没有差
别——可重入程序的编写总是非常复杂的,实际上和多线程没区别。而如果到达处理函数不
可重入,就势必队列进行。这样处理函数必然不可阻塞——会将后面的到达事件全部阻塞上
的——是的,意外也不行。

但是根本上,回调模式是一种非常不人道的模式,哪怕有闭包的存在依然如此。他强迫程
序员将连续的思维转为状态机。由于思考的复杂度增加,因此可编写的程序的复杂度必然
大大降低。而协程模式则可以线性的处理这个问题,如同阻塞模式一样。因此借助协程,
程序员可以直接书写业务逻辑。而回调结构的程序要书写复杂业务逻辑就比较要命了(为
了防止js程序员喷我,提前声明——这不是不可能)。

同样,我也不是很喜欢目前操作系统的抢占式多任务系统。目前锁的滥用,抢占式多任务
要负上很大责任(但是要理解,没有抢占式是不行的)。由于你永远不确定上下文在什么
时候调出,因此对一个变量的访问也必须上锁。锁是很伤的。每次上锁都必须锁住内存栅,
打断所有CPU的流水。而且万一运气不好,阻塞在锁上,又会发生上下文调度。我只是要
访问一个变量而已,至于么?

如果是非抢占式调度,例如单线程下的协程,或者单线程回调。这个问题就自然消失。对
于短暂的变量访问,由于没有上下文切换的可能性,自然就无需用锁。当然,这是有代价
的——永远只能用一核。这种思路比较适合需要多个上下文,上下文间联系比较紧密,cpu
密集度比较低的程序——基本只有高性能io处理程序了。

实际上,底层程序写多了往往有一种奇妙的即视感。你会觉得这些东西貌似在哪里见过。
提示一下,是不是在你的操作系统教材中?同步问题,两军问题,回调,中断重入问题,
多cpu同步。实际上,异步协程框架的设计思路和操作系统非常一致,很多东西都可以类
比。
* 依云
学习了。好长,可惜排版不好。HTML 版本我在火狐中查看文本有重叠。

关于协程,nginx lua[1] 的叫法是同步非阻塞。个人很认同协程是同步编程,而
Tornado / Node.js 之类是异步的。协程的出现就是为了把异步编程弄成同步编
程,因为很多人都说异步编程更难(虽然我一直没感觉到,也许是缺少对比吧)。

最后,吐槽下生活中的同步办事真低效,各种等待各种错过。异步手段(电邮、IM
留言)越来越发达某些机构却不懂得使用 😦

[1]: http://wiki.nginx.org/HttpLuaModule#tcpsock:receive
* Shell Xu
异步手段有缺陷阿。他们发了你可以说没收到阿。很多事情异步解决是需要额外手段的,
这就有风险了。
* simon zhang
那就水平触发。。
* Shell Xu
不行,水平触发只能保证有通知,不能保证通知送达。
送达后回应会比较合理点,可是这不是给xx添麻烦么?
* 依云
| > 异步手段有缺陷阿。他们发了你可以说没收到阿。很多事情异步解决是需要额外手段的,
| 这就有风险了。

三次握手嘛。至少各种问询完全不必同步啊。

工作小计

Leave a comment

今天收到了百度和人人的 offer,都是应用运维方面的工作。如果去百度,会在 spider 组里负责百度爬虫的稳定可依赖。若去人人,则是 DevOps 类型的工作,主要是 python 开发,做大数据处理和虚拟化方面的工作。

在和双方 HR 沟通后,选择了百度。现在年轻,更看重的是成长。

在当当,从实习到现在,只有过一段时间的开心。可能是性格方面的原因,有些自负和谨慎,一直没有达到一种满意的状态,在这里也没有找到可以全心付出的感觉。被 “敲了几棒子”,也有些比较好的观念的转变,然而,这里缺少我需要的,我也没有能力去创造那些我需要的,所以,离开是个比较好的选择。

十一假期过完后,听哈狗帮的《差不多先生》这首歌时,反应很大。或许前几个月真的就像 “差不多先生” 一样,就那样将就地过。我觉得我的青春不应该这样,我追求的是那种 “第二天仍然想做的” 事业。

这段也浮躁了很多,那么,沉下心,用博客记录技术和感悟。

2012/07/28 随记

Leave a comment

现在在学习 Python 下的一个爬虫框架,叫 Scrapy。下午看了简介,看了两边 Tutorial,练习了一个例子。现在在看第三遍 Tutorial,对使用 Scrapy 开发爬虫有了一个整体的概念。

在这个过程中,意识到有些技术是需要长期反复学习和思考的。目前想到的有:

+ Python 中的类

+ XML/HTML 解析

+ 字符串的处理

on the road

Leave a comment

今天在 G+ 上看到一篇文章,其中的一句话让我很感动:要么旅行,要么读书,身体和灵魂,必须有一个在路上。

灵魂和身体必须有一个在行进当中,当两个都停下来时,人们通常都会变得懒惰,懒于去思考,懒于去改变,想要过假想的生活。生活是公平的,它会让勤奋的人收获,让懒惰的人付出大的代价。所以,不管怎样,要一直在路上。

多读书,多行走。

 

1、On The Road「要么旅行,要么读书,身体和灵魂,必须有一个在路上!」不知这是谁的话,被这个温暖的句子瞬间击中了。静不下心来的时候,工作无力,读书无味,背包去那么一个地方,什么顾虑都丢下,随便行走,去哪儿都好,最好是远离城市。
2、在路上不珍惜风景的人,最后收获的一定是孤独。
3、你知道我从小最大的梦想是什么吗?我小时候我就想当一个厨师我想我在三十岁之前我一定会找到一个能够让我珍爱一辈子的女人我会好好对她,做最好的东西给她吃。一个人哪,不要轻易尝试否定自己的能力,任何情况下都应该尝试。
4、有段时间迷恋极了单身,害怕有女人,害怕有家。人是会改变的,无论是在身体上还是精神上。所以人是矛盾的,但也只有矛盾可以令人改变。
5、困窘。时间久了,习惯了贫穷,便无法突破自我,也抹杀了自己的梦想,而庸庸碌碌一辈子,所以越来越喜欢钱。年轻的时候事业最为重要。
6、微笑面对每一天,每一天即使明天。只要有明天,就有希望的所在!
7、王小波说:「不管我本人多么平庸,我总觉得对你的爱很美。」这一段话,可能被每一个没有勇气的去爱的人珍藏。
8、生活,退一步海阔天空;爱情,晚一步人去楼空!有些机会,因瞬间的犹豫,擦肩而过;有些缘分,因一时的任性,滑落指间;有些感情,因一时的冲动,遗憾一生。感情中最折磨人的,并非冲突,而是对方的冷漠与厌倦。

Linux 通过命令查看系统相关信息

Leave a comment

Linux 下查询系统相关信息很方便,一个简单的命令就满足要求,而且通过查询到的结果再联系到操作系统、计算机组成原理的课程,就比较容易理解所学的,觉得挺有帮助的。下面是一些命令的总结,大家可通过在http://linux.xidian.edu.cn/wiki 搜寻“查看系统相关”这个 wiki 页面来分享、修改相关内容。

查看 CPU 信息
$ cat /proc/cpuinfo
查看内存信息
$ cat /proc/meminfo
查看 I/O 端口
$ cat /proc/ioports
查看交换分区信息
$ cat /proc/swaps
查看中断信息
$ cat /proc/interrupts
查看磁盘分区
$ cat /proc/partitions
查看 USB 设备
$ cat /proc/bus/usb/devices
查看输入设备:键盘鼠标
$ cat /proc/bus/input/devices
查看系统负载
$ cat /proc/bus/pci/devices
查看 PCI 设备
# lspci
查看 USB 设备
# lsusb
报告虚拟内存统计信息
# vmstat
查看分区信息
# fdisk -l
查看磁盘参数
# hdparm -i /dev/sda
查看分区信息
# df -h
读取系统 DMI 表来显示硬件和 BIOS 信息
# dmidecode
当前加载的驱动
# lsmod
查看开机检查的硬件,可使用 grep 过滤: eth, cpu, mem, pci, usb, vga, sda…
# dmesg
查看系统负载
# uptime
查看当前 OS 版本信息
$ cat /etc/issue
查看 CPU 运行模式,结果若为 32 表示当前 CPU 运行在 32 bit 模式下,但不代表 CPU 不支持 64 bit.
$ getconf LONG_BIT
查看 CPU 是否支持 64 bit,若结果大于 0,说明支持 64 bit 计算,其中 ‘lm’ 指 long mode, 支持 lm 则是 64 bit.
$ cat /proc/cpuinfo | grep flags | grep ‘lm’ | wc -l
查看 CPU 信息概要
$ lscpu

Older Entries