关于Python Web开发我的感悟-兼答wsgi fastcgi uwsgi

有位朋友最近在python.cn接连提问关于python web开发的问题,
当然由于他问题的标题是Python http server,我也很难确定我下面的说法可以回答他。

如果单论http server,通俗点讲,其实就是一个可以相应客户端针对80端口的http请求的一个网络程序。
apache也好,nginx也罢莫不如此。python写一个http server也很简单,只要绑定80端口,解析请求,返回一些格式化好的响应数据就行了。

但是根据列举的这些关键字,其实这位朋友要问的应该是python web开发。
从最简单开始讲,python支持cgi编程,也有标准的cgi模块。
什么是CGI,它不是Python自己独有的东西,而是一个通用的web标准,可以粗陋的认为CGI就是一种用任意语言(那时候都是指perl)写Web程序的标准。最简单的就是用C写一个hello world的程序,只要做一些简单的apache配置,你就可以在网上运行这个程序。

cgi有缺点,就是你自己玩玩还好,想想一个py程序如何运行的?它需要运行py解释器,然后载入这段py脚本,
运行,然后py解释器退出。也就是说每当一个用户访问你的py程序都要经历这个py解释器运行、载入、退出的过程。

设计很不优化。

很显然,这时候需要重用,缓存,对不对?

我印象中fastcgi机制(这是一种机制,早期nginx都是用lighttpd的spawn-fcgi)就是做这样的工作,它会起几个python进程,不退出一直运行着,当你有请求,某一个空闲的python进程就负责载入你的脚本,然后跑,然后继续空闲。

看出区别了吧,python不需要反复启动、退出,这是极为耗费时间的,所以节省了这个过程,就省去了很多时间。
实际上这个机制被普遍运用着,比如office预启动或者很多程序都在系统启动时加载。

当然fastcgi还有其它的机制保证它的运行,我了解不多,就不乱说了。

那么wsgi是什么?
跟wsgi一样概念的有Ruby语言的Rack,可以认为wsgi是一种通用的接口标准或者接口协议,实现了python web程序与服务器之间交互的通用性。就好比JCP一样。有了这个东西,web.py或者bottle或者django等等的python web开发框架,就可以轻松地部署在不同的web server上了,不需要做任何特殊配置(也需要一些小小的配置调整)
http://wsgi.org/wsgi/Frameworks 这里可以看到几乎所有流行的web框架都支持wsgi协议。

uwsgi又是什么?
就我看到的一些东西,uwsgi类似tornadoweb或者flup,也是一种python web server,负责响应python 的web请求,因为apache也好,nginx也罢,它们自己都没有解析动态语言如php的功能,而是分派给其他模块来做,比如apache就可以说内置了php模块,支持的非常爽,让人感觉好像apache就支持php一样。

我说的很简单,其实最重要的是这一句。学Pythonweb开发,应该从动手做开始,而且应该从appengine开始,先省略python部署问题,着重于用python开发web程序,真正跑起来用起来。至于后续的一些概念,应该多学多看,其实我现在对很多python基础也是了解的极为粗浅,但是喜欢没事就查查资料,所以能对这些词语忽悠一些出来。

《狂人C》阅读笔记

将在github的wiki上做持续更新,这里做个copy

https://github.com/saga/sagasw/wiki/%E7%8B%82%E4%BA%BAC

整体问题

对于C标准库的说明都说是编译器实现,这一点是不准确的,参考维基百科http://en.wikipedia.org/wiki /C_standard_library

提到一个真正是莫名其妙不知所云的C语言中国国家标准,这是个什么玩意?谭浩强主编的?

primary expression翻译成初等表达式,这个是败笔,难不成还有中等表达式和高等表达式?我建议如果再版,改做“基本表达式”,元表达式也不妥,一般来讲编程中的“元”多从“meta”这个单词产生,如metaprogramming。

page37, printf(“%d\n”, 1234567890123); // 0x11f71fb04cb 在little-endian机器上,大部分我们机器,如Intel、AMD,结果都应该是1912276171(0x71FB04CB),而在 powerpc的ibook g4 big-endian机器上,结果为287(0x11F)。这个结果是有章可循的。

page38, 应该说明sizeof不会对表达式求值。 sizeof(m++); // m will NOT increase

page62,”后者也是一种错误代码”,感觉提法不容易理解,-> “标准未定义的代码也是应该避免的”。

预处理命令 #definede : typo

注释在编译前被替换成空白字符,这部分待查(gcc,vc)。

page63,风格习惯。命名规范问题,应该用准确的英语做标记。reminder = dividend % divisor; better than ys = bcs % CS;

常见错误,/* comment */,一般常见的IDE会有代码着色功能,如VC++会把注释标记为绿色,而大部分编辑器如vim (textmate)也可以做标记着色。

《狂人C》阅读笔记(1)

Page8,main的原型应该是不需要void作参数的。我试验了codeblock(使用gcc为编译器),没有警告。

另外就是代码应该标记出行号,使用行号指明更清晰一些。

还有一个格式方面的问题是”;“在书中单独出现时都是全角中文”;“这个非常糟糕,很容易误解。

还有一个问题是C99还是C89,我的建议是以C89为主介绍,间杂一些C99,但是C89不应该以”过时“来形容,我倒是觉得C89更主流一些。

page9,关于#include””和#include<>的区别,应该指出后面会讨论的章节更好。

page14,关于中文做标识符、变量名、函数名的解释,反而让人比较迷糊。其实我觉得作为一本针对初学者的书籍,最好不要在开始碰这个方面,可以在后面加入一些说明补充。

page16,如表1-1所示,应该说”有些keyword”是C99新增关键字。另外我觉得这个C99其实意义不大,说了反而让人迷惑。在后面附录补充说明一下就可以了。

page19,风格习惯一节,如果说的更详细一点就好了,太简单。另外《狂人C》的代码风格是({}三种风格中)最不常见的一种,应该稍微提一下三种不同风格。

page20,开始一个段落介绍如何让printf分成print\换行加f这种,完全没必要,让人更混乱,而且代码示例如果从缩进来看还是错的。

page20,我觉得应该是”任何一个C程序必须有而且只能有一个入口函数,这个函数绝大多数情况下定义为main。“VC++和GCC都可以通过编译选项修改入口函数(entry function),如果用VC++,通常名字为_tmain()。还有就是main函数有两个可不可以?如果抬杠的讲,通过预定义编译选项,可以定义两个main,嘿嘿。

page20,printf()函数是由C标准库提供(C standard library),而C标准库大多数由操作系统编写者提供,通常情况下GCC只负责把printf()与标准库代码实现编译链接到一起。关于标准库,也可以选择比如DietC这种第三方。windows sdk也提供了标准库的实现,我不是很清楚,但是理论上说GCC应该是可以使用windows SDK提供的include和library的。

page25,应该介绍一下D(ecimal),H(ex)这些单词的完整形式,更好一些。

page29,对于学习、考试、面试而言,我觉得”同名”是一个很值得考的知识点,当然实际工作中就不要这么写了。

page31,由于字体的原因,/* */和//看上去非常奇怪,尤其是//就好像中间有个空格一样。

page31,关于注释,我不太同意书中说法。个人觉得注释只应该在该使用的时候使用,如何注释何时注释,代码大全和程序设计实践这两本书介绍的非常全。

page32,关于变量的定义,K&R英文版(以下提到K&R都指的是英文版页码)的page195说的非常清楚,《狂人C》的提法只说了其中一方面,也就是storage object这一面。不完整。

page32,变量使用之前必须声明(declaration),这一点《狂人C》的说法是错误的,对于C语言来讲,声明 (declaration)与定义(definition)是很有区别的两回事,不可以互换使用。K&R page40说的非常清楚。而定义(definition)在K&R的page250有说明。

推推C语言新书《狂人C-程序员入门必备》

http://product.dangdang.com/product.aspx?product_id=20974009

http://www.china-pub.com/54010

《狂人C》在CU上的意见贴:http://bbs.chinaunix.net /thread-1821644-1-1.html

今天上午收到快递,收到键盘农夫的大作《狂人C》。首先要感谢国家,也要感谢键盘农夫给我这个机会拜读他的大作。

这本书拿在手里很厚实,翻了翻目录以及章节内容,不是那种教科书死板的风格,用词摘句很平实,感觉键盘农夫应该是有实际编程的经验才能写出这样的书。我在前面推荐的C语言教学,里面只包含了《Linux C编程》这本是国人著作。想不到现在还有人愿意静下心,写一本有价值的C语言专著,非常佩服!

草草看了一下内容,感觉值得改进的地方有这样几点(希望农夫不要见怪哦):

一是英文字体选的不好,看上去很不舒服。另外代码字体与正文中的英文字体差别太大。字体的选择对于程序员来说其实不是小事。另外中文字体的间距感觉也不是很合适,有些别扭。还有一点是边界留的太窄,很难做笔记(当然,留的宽了也会增加页数成本,唉)。

第二点是变量名的选择,键盘农夫应该是针对这个问题有所考虑,选择了拼音首字母作为变量名或者函数名,但是我个人感觉这其实不是一个很好的选择,尤其是qiufqsm这种,很难直观的看出来。我个人觉得程序员必须要学英语用英语,文件名、变量名不要用中文或者拼音,其实读写简单的英文对于大多数程序员来说不是难题。

由于中文变量名的存在,后面tic-tac-toe的工程实例看的很费劲,因为非常不习惯这种风格。

第三点算是小问题,比如我倾向于使用“字符字面值”,“多维数组”这种翻译,而键盘农夫兄是另有选择,但是由于书中基本附带了英文原文,理解起来并不困难。还有就是指针这一章中,指针地址很多写的比较详细,但是感觉应该稍作说明(也许有,但是看的潦草没注意)这些地址仅仅是假设的。

第四点也不是大问题,就是练习题没答案,对于初学者来说,有答案对照,可能会帮助很大。当然也可以有一个网站提供答案,也是一个办法。

尽管吹毛求疵提了这些问题,但是要说的是,这本书写的还是很认真的,尤其是一些关键点,比如指针及数组的概念,用了大量篇幅来介绍,也体现了作者功力所在。建议各位C语言爱好者买一本读一读,一定会有所收获。我也会在将来把自己的读后感放上来分享。

关于C/C++内存管理一些乱讲

http://www.cnblogs.com/skynet/archive/2010/12/03/1895045.html 这篇博客最后写了5个规则,虽然简单,但是还是有些问题,在这里稍作说明。

【规则1用malloc或new申请内存之后,应该立即检查指针值是否为NULL。防止使用指针值为NULL的内存。

偶评:参考wiki百科针对malloc以及new的说明,对于C语言malloc方式,检查NULL是可以的,但是对于C++的new操作符(operator),检查NULL基本是无用的,因为C++有异常机制,new不成功就会抛异常std::bad_alloc,如何处理可参考 http://msdn.microsoft.com/en-us/library/kftdy56f%28v=VS.71%29.aspx

【规则2不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。

偶评:初值的赋值操作也是要花时间的,C语言初始化一块内存memset的时候要注意用buffersize * sizeof(Object),而C++就更复杂了,比如构造函数,new的重载之类,要小心。

【规则3避免数组或指针的下标越界,特别要当心发生“多1”或者“少1”操作。

偶评:一般来讲多一少一运行时不会一定出错,比如微软编译器申请内存一般会比你指定的大一些,比如你申请10bytes,如char* p= malloc(10);,通常情况下操作p[10],p[11]啥的是没有问题的,所以会发生一些奇怪的现象,就是某些bug有人机器会出,有人就无法重现。

【规则4动态内存的申请与释放必须配对,防止内存泄漏。

偶评:要说明的是,new一定和delete配对,malloc一定和free配对,否则也会出错。另外new [],一定要delete [],否则也会泄露。

【规则5用free或delete释放了内存之后,立即将指针设置为NULL,防止产生“野指针”。

偶评:这种情况一般是代码中函数写的比较长,指针用了又用,或者指针是全局的情况。所以一般软件公司的编码规范都会硬性要求这样做,如果函数很短小精悍,其实不会有这种担忧的。当然,偶也同意这样做有益无害。

再加入一些额外的说明:

对于指针类型的检查,不应该用assert,而是应该正常操作,if(!p) return E_POINTER;这样。使用assert的语义与空指针判断是两码事。如果不想判断或者图省事,C++中可以用const reference类型,但是不建议传入reference对象,然后在函数体内修改。

针对内存操作,比如memset,memcpy,同样要注意不能访问越界的数据,类似规则3。

针对C++,new delete其实可以做出好多花样,个人感觉用处不大,因为服务器端编程大多用C配合内存池,重载new之类的学了也很少用。如果对服务器端内存管理感兴趣,可以读读nginx的代码,简洁高效实用。

一般来讲,应该是谁拉的屎谁擦屁股,也就是说某个对象或者函数new了一些东西,它应该负责delete,否则距离不仅仅产生美,还会产生bug。(多废话一下,比如在aaa.cpp里面new了一些东西,然后你在bbb.cpp里面释放它,甚至在不同地方释放它,如果对程序结构不了解,很容易产生bug)

可以通过python或者lua编写一些小软件,检查项目中new与delete,malloc与free的个数,如果个数不匹配,就有可能有bug,另外还可以检查new[]与delete[]的配对情况。我编写了一个这样的小工具,完善以后放出来。