我一直认为作为程序员要经常阅读别人的代码,阅读成功开源项目的代码。这就像作家经常阅读,写程序有时候很像写文章。几年前购买过一本书叫《Code Reading》的原版图书,里面很清晰的讲解了如何看代码,怎么抓住重点等技巧。还提到一些编写技巧,方便阅读时理解。建议大家看看,这里有个链接可以到google的在线图书馆找到这本书。
任何代码都是值得学习的,哪怕它很乱、很差,你也能从中学习到怎么乱、怎么差,你怎么在你自己的代码里规避它。作为入门道的程序员请不要动不动就哭着喊着要让人家开源(真开源了你看得懂嘛?),不要认为所有不是自己写的代码都一无是处,什么都要自己重新再写一套。
发了些牢骚,接下来列举一些我认为wordpress做得让我感动的地方,算是对代码用我的小红笔圈圈点点吧。如果你觉得你都知道了,或者太简单,就请当做这是我的自我娱乐吧。
- define( ‘ABSPATH’, dirname(__FILE__) . ‘/’ ); 定义了很多全局常量作为可配置的代码,而具体实现不会有过多的硬代码(就是写死的地方)。我的项目中的配置参数一般会用一部分全局常量,另外一部分用变量,因为有些数组的情况。wordpress里尽可能用的是常量,因为这样值不会被后面的代码所改动,使用一个全局变量全靠自己自觉,谁没事儿改一下就乱了,也没有办法控制。所以想办法全部定义成常量试试。我原来是比较担心这个东西不在程序开头实现,再加上一些逻辑是否可行?实际是可以的,所以“定义全局常量来避免程序中的硬代码”。而且这样还有一个好处,就是在一些类方法或者函数中调用这个值时,不用每次都要global这个变量了,因为常量是全局的,无需特殊声明。按照编码惯例或者规范,常量一般都用全大写的字符标示,分割单词使用下划线,这也方便在程序内辨别那些是常量。
- dirname(__FILE__),很久前就在使用这个参数用于include,require了,这样写相当于是当前编辑的文件所对应的路径以及需要包含文件的关系。所以你知道你当前编辑的这个文件的位置就能推导出包含文件的路径。wordpress没有在每个程序文件包含时使用相对路径,而是在根目录下的wp-load.php一次性获取一个根路径并定义在常量中。其它程序文件用这个作为定位的基础。
- wordpress没有完全转向面向对象的编程,2.0.0开始才引用了一些新的类来构建系统,而为了兼容性,旧的一些面向函数的代码还存留在系统中。所以现在从头运行的过程是顺序的:index.php -> wp-blog-header.php -> wp-load.php -> wp-config.php -> wp-settings.php -> new WP() -> WP::init() -> WP::main() -> template-loader.php相信未来会全面向面向对象转换。
- 目录结构非常之简单,根目录下的程序都是直接可以请求调用的,相当于访问入口。子目录wp-admin里面都是管理系统的代码。wp-includes里面都是核心程序的代码,类库,函数库,业务核心逻辑代码。wp-content目录保存的是所有跟站点、用户相关的代码,包括模板,风格,汉化包,用户上传的文件等。可以说这样分离业务数据可以轻松实现升级。以后升级保留wp-content目录的所有内容,覆盖新的程序代码,运行一下/wp-admin/upgrade.php解决一些版本之间需要调整的数据结构,数据等内容,就可以完成升级了。
- 本地化方面有两个细节,如果写死在代码里的翻译,前后加了/*WP-I18N-XXXX*/的注释,方便用程序查找替换。没有写死的都被替换成__(‘xxxxx’)这样的函数样子的内容,其实这个双下划线加括号是映射到gettext()函数。具体用法可以参考php手册。至于怎么生成 .po语言包,我还没有研究,稍后有空可以看看。
- wordpress一大特色就是支持很多插件,你也可以遵循api文档,为它编写插件。插件支持的实现方法主要是有通用的函数负责注册不同位置需要调用的函数数据,然后在很多地方写下调用处理插件程序的钩子入口hooks,触发钩子代码后就会检查对应这个地方需要调用那些预注册过的函数,调用后才会继续运行程序。这样你的程序只要按照文档注册,就会被在适当的地方调用。它不像discuz的实现,discuz是在不同代码位置使用eval来执行注册好的程序,插件代码写入数据库中进行管理了。wordpress是基于php程序文件的,并且有更高的注册机制管理这些需要触发的插件代码。值得学习。
- 模板或者样式方面没有使用第三方类似smarty替换标签的方法实现,而是将显示部分的代码混合尽可能简单的php调用代码来组合成模板文件。使用load_template()函数来调用。无需定义标签并和实际程序映射,需要自己实现页面级缓存机制代码了。我是比较喜欢smarty的,所以推荐使用smarty,哈哈。
粗略看了一下,没有全部看完,再继续看到心得会继续编写同类文章。happy coding!
Tags: code reading, wordpress, 源代码
LFS是Linux From Scratch的缩写,是一个按照文档指导,全部通过软件的源代码编译构建一个自定义Linux系统的项目。先不说它怎么可以定制自己的Linux系统或开发自己的一个Linux发行版,我觉得它可以让大家更深入的了解Linux系统。也许你已经可以在现成的发行版中编译个apache或者mysql什么的,但是你知道linux中众多软件包之间的关系吗?你知道你常用的工具来自那个源代码包吗?你熟悉bash下面一些常用的自动化脚本吗?这些都可以通过成功的完成一次LFS编译来学习到,甚至可以领悟到更多。
很早以前就了解到LFS这个项目了,但是一直没有时间编译一个系统出来,记得两年前曾经搞过一次,当时纯属乱来,卡到中间报错也不知道为什么,无法继续下去又不肯重新来,于是就放弃了。这几天又重新将这个东西拾起来,过程中学到了不少东西。下面就拿出来晒一晒,看你知道这些linux的tips吗?
- bash下可以用大括号来代表几个需要遍历的值,比如 cp /tmp/{abc,def}表示 cp /tmp/abc /tmp/def。这样有时候前面路径很长,就写一次就好了。
- sed是一个很常用的文本编辑程序,需要程序化调用的时候,批量处理,或者你没有vi的时候,非常方便。你需要熟悉一些简单的正则表达式,如果加入-i参数,就会直接按照给定的规则修改输入的文件。
- 2>$1这个这么诡异的符号代表将错误输出也重定向到标准输出上来,linux里分stdin,stdout,errout三种东西,前面的指令就是将后两个输出合并。2>$1 > /dev/null就是将所有输出都不予显示。2>$1 | tee tmp.log使用tee这个程序既将显示内容显示在屏幕上又将内容储存在文件中。如果一些工作你需要记录日志,又要查询当前情况,看到屏幕的输出,就可以这么用。
不管是学习还是完整编译一个LFS系统,为了顺利完成,请参考我下面的经验:
- 所有指令建议自己一个个字敲进去,不要copy & paste,因为你敲进去才会注意细节。想想每一句都是什么意思,尝试将指令搞懂。文档中其实都有很详细的解释,配合着应该所有的指令都能看懂,也能总结出来我上面描述的以前不知道的小窍门。
- 严格按照文档中的软件包版本号来编译,除非找不到该包,可以尝试稍微新一点版本。因为版本不同,可能提到的一些细节就不被支持,到时候你也不知道正确还是错误,也许会出现很多莫名奇妙的错误。
- 严格的按照文档中的要求打patch,我有一次就自认为不需要那个patch,结果到后面几步出现问题,排查才发现是这个patch的原因。
- 遇到问题首先检查当前输入的命令是否正确,环境变量以及前置指令是否拼写正确。如果无法排除错误就要看输出,找到错误提示再尝试看看是不是前置的包安装有问题导致的。Makefile.in其实描述的是批量处理要执行的指令,找出错误的地方手动执行一下,看看错误的细节。例如我就在一个地方发现touch setting times of ’subdirs’: function not implemented,一开始以为subdirs有问题,后来才发现单独执行touch命令,就会报错function not implemented.明显是touch这个程序有问题,再man touch查所在那个软件包,发现是coreutils,这是前两个步骤编译的东西,退回去就发现了问题。
- 需要在某个步骤退回去的时候,就要查询一下已经编译的程序都有那些,在path默认能检索到的目录,比如/tools /bin /usr/bin如果有这些程序就应该删除掉,否则退回去以后编译的过程会使用这些也许有错误的程序。
- 按照文档的提示执行测试,如果测试出现问题并且文档没有提示忽略,应该检查一下问题的根源。如果实在不行就继续,期望后面不会有问题。有些测试无法通过也是正常的现象。
- 文档的每一个章节都描述的是一个软件包的编译,测试,安装的过程,在此之前请下载并解压进入该软件包,结束所有工作后请删除此目录已经编译时建立的目录。文档中提到的某些软件多次编译,都不是在同一环境下多次编译的,都是需要重新解压一个新的、干净的目录来完成编译的,所以章节的结束就应该删除这些目录,因为此时编译好的程序已经安装到指定的位置了。
- 花费时间长的就几个包:glibc, gcc, linux kernal。你可以开着电脑睡上一觉,不然漫长的等待在漆黑的夜晚不好过。
- 虚拟机下是可以完成LFS的编译的,除非它太慢了,你无法忍受,我的系统就在vmwave中安装suse linux,并在其中编译出来的。其实运行vwwave的系统是个Mac
我也许这次算是比较幸运,没有遇到什么头痛的问题,接下来如果你在编译时出现问题,自己解决不了的话,欢迎给我留言,将详细的状态信息,错误信息提交给我,我帮你来分析问题,并且从中大家一起总结经验。就像我翻译的一篇文章所说的“教会别人是学习的最佳途径”,祝大家研究顺利,玩得开心。
Tags: bash, lfs, linux, sed, 编译
前些日子拖着病躯参加了一个北京的Hadoop User Group聚会,可以用“不虚此行来”形容。见到两位大大:负责Facebook总部backend工作的Shao Zheng和Yahoo北京研发中心首席架构师Zheng Hao。他们分别对各自在hadoop上面的工作做了介绍,让我们了解到Facebook, Yahoo使用hadoop的情况。
Hadoop是一个apache基金的项目,表面上来看是实现一个开源的GoogleFS BigTable,实际现在已经发展得有自己的特色并且希望能解决更多的应用问题。
Hadoop目前多用于后端数据统计分析等工作。此类工作的特点是数据量大,计算量大;数据写入后不变化;读取数据时多用顺序读取,很少使用随机读取;需要快速计算得到所需要的结果。
目前硬件环境的瓶颈主要在于磁盘I/O,所以利用分布式计算来进行并行计算,提高计算效率。为此,数据存储下来是不移动的,每台机器负责自己拥有的部分数据计算,这样在这上面就要实现MapReduce。
何为MapReduce? 如果你熟悉Linux下的指令的话,也许你曾经为了统计已经用过下面的命令:
“cat 日志文件.log | grep 检索条件 | sort |uniq -c > 输出”
这就是MapReduce的本质,只是将上面的步骤分拆到不同的机器同时运算而已。
Hadoop有几个子项目,分别处理不同的细节问题。Shao Zheng负责的是Hive,它是Facebook提交并维护的一个基于Hadoop的简易查询语句转换器。实际上可以理解为SQL on top of Hadoop。Zheng Hao介绍的Pig是Yahoo编写的查询语言,也是用于Hadoop中的数据查询。两个子项目都是为了将用户的查询需求变成MapReduce然后再进入Hadoop进行实际的运算。只是前者将通用的SQL来进行转换,后者使用自己定义的一套查询语法来查询。各有千秋。
我非常喜欢老外的思路,Hadoop项目组认为,效率不是问题,先要将思路走通。假设整体架构是可行的,如果那个地方有效率问题,采用不同的技术、硬件或者算法可以改善,那么整体系统都会得以提升。所以我也认同不要有事儿没事儿就批判Hadoop效率不行。起码这个项目一直在进步,有很多人参与进来贡献自己的力量,比起一个深思熟虑解决好效率问题几个苦力完成的私有解决方案要强大得多,三个臭皮匠还能顶一个诸葛亮呢。莫要忽视开源社区的力量,现在Hadoop的问题大家都能看到,每个人都在想办法解决,因此它会越来越好的。
Hadoop不是万能,我建议大家刚接触这个项目的话就将它应用于统计分析或类似的应用,如果你对Hadoop不是很熟悉的话,暂时不要尝试想Hadoop还能做什么这个问题,因为我认为在整体架构和社区任务计划不了解的情况下,很难做出这样的判断。很容易得出Hadoop不好,Hadoop不好用的结论。这些事情还是交给Hadoop解决方案专家或者顾问来做比较好。
废话了这么多,算是我自己的发泄,贴张聚会的照片,看看我在那个位置?

Tags: facebook, hadoop, MapReduce, yahoo, 云计算, 分布式计算, 数据分析
最近做一个项目需要很高的并发长连接,apache下做了一个测试,连接数不多的情况下,负载已经开始冒头了,并且worker模式下php不是很稳定,于是乎转向尝试Nginx+FastCGI+PHP的组合方案。
网上已经有很多关于这个组合的安装文档了,在这里我不重复,只是提一些我自认为比较迷惑的,网上文章没有唠叨的问题点。假设你们熟悉apache+php的组合,计划从那个平台迁移到Nginx下。Ok, Let’s go!
- php源码包中的文档描述道,有关FastCGI模式,建议使用./php-cgi -b 127.0.0.1:9000的独立模式来接受web服务器转发过来的请求。既然是推荐模式,我就运行一把,结果这个程序一没有多进程模式,二不支持进程异常退出后的重新启动。并发请求上不去,稍微给一点压力,php-cgi崩溃或者异常退出了,系统就没有php解析服务了。于是乎转到lighttpd里的一个小工具spawn-fcgi。这个工具支持设定同时打开多个php-cgi来服务,并且任何子进程终止都会新建一个补上,可以说算是比较稳定的方案了。网上还有一个php-fpm,有空我看看怎么玩的。
- 修改了php.ini配置信息,问题来了,如何重新启动这么多php-cgi呢?本以为spawn-fcgi是一个独立的进程,kill掉它就可以干掉其它呢,结果在进程列表中找不到spawn-fcgi,它隐身成php-cgi这个名字,并且是所有其他php-cgi的父进程。得,killall php-cgi,直接就杀掉所有的php-cgi包括那个父进程。重新启动spawn-fcgi,这时候就是新的配置信息了。有一点很不错的是,这时候Nginx没有重新启动,在没有php-cgi的时候,Nginx会显示一个界面友好的提示用户暂时无法访问。并且其它静态文件访问都是正常的。
- 我测试的web项目要使用rewrite,正好研究一下如何切换rewrite代码,研究半天才发现有如下需要注意的点:
1、Nginx不支持.htaccess文件,配置rewrite需要在nginx.conf里面写。
2、apache中的RewriteRule需要改写成rewrite
3、规则需要使用双引号引起来
4、apache中[R] 要改写成redirect
5、因为没有地方设置RewriteBase,所以所有的规则都要从/开始,跳转的程序地址也要从/开始
6、RewriteCond用if代替
- 调试上面的rewrite非常累人的kill掉nginx再重启,后来发现可以给nginx master一个信号kill -HUP xxx(nginx master的pid),这样服务没有停,就更新的配置。为了保证服务顺畅,执行这个之前要先执行nginx -t来测试配置文件是否有效
使用ab压了一下,在压力下,访问网页响应速度还是不错的,并且太大的压力nginx会屏蔽请求,等待系统负载降下来了才会继续接受服务请求。这样不至于将服务器搞死。
陆续我还会将测试发现的问题列在这里,对待新鲜事物总是会有这么几天不顺利,但是过去了以后自然就变成自己的经验,就不会有很恼人的问题了。坚持!
后续使用nginx的心得,不开新的文章了,直接在这里添加内容
1、日志翻滚:nginx不支持cronlog那样的管道模式,但是可以用kill -USR1 [pid]来给nginx信号来进行日志文件的刷新。将现有的日志文件改名后,给一下USR1信号,新的日志就产生了。
2、一个站点下如果开多个虚拟主机,最好是单独写server {}这个配置块,不要在里面加不同的alias,否则很多跳转都会默认跳转到第一个站点的域名下,就算你访问的是它的别名站点也没用。网上有另外一个办法是做一个rewrite的规则,让这种跳转找到自己访问的源域名再跳转,感觉rewrite方案类似凑合事儿,因此还是用直接配置多个server的解决方案。多server配置时要加一个参数在http{}中 server_names_hash_bucket_size 128;否则服务器启动会有报错。
3、spawn-fcgi -C的参数是代表开多少个后台的fcgi守候程序,一个程序同时只能服务一个请求,我的项目中有长连接的状态,所以这个参数开小了很容易导致因为后台没有足够的空闲fcgi而nginx停止响应。所以如果连接时间比较长,并发比较大的话,这里要开大点,开个几百个没有什么问题。
4、怎么不暂停服务扩充spawn-fcgi的数量呢?先用spawn-fcgi启动另外一个新的端口例如9001服务,这个服务中fcgi的数量是要扩充到的数量,然后改nginx.conf里面的端口号到9001,然后kill -HUP [pid]更新nginx的配置,这会儿就使用新的spawn出来的fcgi了。然后将旧的spawn-fcgi的进程都杀掉释放资源就好了。
5、上传文件大小在nginx的配置里也有体现,client_max_body_size 250m;可以将上传限制扩充到250M,加到http {}配置中或者server{}中都可以
6、每个location属性都可以单独设定某些请求文件条件下的特殊属性,但要注意,设定必须包含root参数指定所请求资源的物理路径,否则回报404错误。
7、可以使用expires 365d;属性来设定某些文件的缓存时间。
Tags: apache, nginx, php, rewrite