2010年十一月月 发布的文章

尝试博客搬家

早在若干年前就有朋友建议我搭建一个独立博客,可当时的我觉得blogbus提供的服务很不错,自己没有必要去折腾,费钱又费力,所以我选择了继续留在blogbus。

这两年blogbus服务一直在不断的提高,自己也一直很欣赏blogbus的简单、清新、无广告的风格,大巴后台管理中心的功能也变得越来越强大了。不过这期间blogbus也出现过几次较为严重的故障,导致长时间的无法提供服务。上周blogbus再次出现文件服务器故障,导致上传的图片不能正常显示。这次我做了另外一个选择:尝试搬家。之所以称为“尝试搬家”,是因为搬家可能成功,也可能失败。

上周末经朋友推荐,我购买了dreamhost的主机空间,注册了独立域名,并花了周末两天的时间搭起了一个wordpress博客,这个过程是一波三折,还好我的这位朋友是建站方面的高手,经他指点,我少走了许多弯路。但博客搬家最难的地方不是建站,而是后续数据的迁移和整理。

搬家过程大致如下:

1、创建mysql数据库;

2、安装wordpress;

3、从blogbus后台管理中心将数据导出,导出一个blogbus自定义格式的xml文件;

4、下载bus2wp.py

5、按照bus2wp.py的说明,执行bus2wp.py将blogbus自定义格式的xml文件转换成wordpress标准xml文件;

6、转换后的wordpress数据文件有4M多,我用DivXml将该文件拆分成四个1M左右的xml文件;

7、通过wordpress后台提供的导入功能将数据文件导入

这里我安装的wordpress是2.8.6中文版(据说高版本的wordpress再导入bus2wp.py转换后的数据时会出现各种各样的问题)。导入过程很顺利,导入的大部分数据的格式都还是可接受的。

8、选择wordpress themes

2.8.6版本wordpress默认的Kubrick主题我一眼就看中了,不过该主题页面宽度不足,看起来很别扭,遂自己查资料,终于找到了一个Wide版的Kubrick的主题,下载后,替换了默认的主题。

9、安装必要插件

wordpress做得很强大,插件很多,根据朋友和网上推荐安装了Akismet、Add Post URL、Google XML Sitemaps、WP-Syntax和WordPress Database Backup等这几个插件。虽说安装过程都很简单,但是每个插件都要配置和测试,还是耗费了我不少精力。

10、整理文章

这是最痛苦的事。wordpress自带的默认编辑器很不给力,在“可视化编辑器”和“HTML编辑器”之间切换居然还会导致格式变化,导致刚整理好的格式瞬间丢失,还得重来,很痛苦。另外我还是一个追求完美的人,我最初计划将搬来的600多篇博客文章都整理一遍,修改每篇文章的永久链接地址、重新分配标签、更改文章内容中的所有链接(指向新博客站点中的文章),可昨天刚整理了三篇文章,我就发现这几乎是一个不可能完成的任务,我目前确实没有精力折腾这些事儿。

到此为止,我开始反思:我真的需要这样一个独立博客吗?独立博客有诸多好处,这个不用我说。但是这些好处中哪些是我真正需要的呢?顶级域名和稳定服务也许是我更看重的。但是国外提供的虚拟主机空间就一定比大巴稳定么?这个用过才知道,我还没有发言权。至于顶级域名其实blogbus也可以做绑定。

整理数据的这几天耗费了我很多精力,很多事情都因此耽搁了。我决定不再整理了,本次尝试搬家宣告失败!继续遵循多年前的那个选择:只要blogbus还继续提供服务,我就一直扎根这里。

给assert加上返回值,不给力!

众所周知,assert是程序调试阶段的一柄利器,可以帮助程序员快速的定位代码问题。但一般来说当程序部署到生产环境的时候,我们会选择关闭assert。不过由于历史原因,我们运行在生产环境下的程序中的assert依旧发挥着作用,这样一把双刃剑就悬在了我们头上。

我们用的是自己的assert实现,这个实现没有C标准库中assert实现那么普适,不过可以满足我们自己需要的功能,它在运行时可以将断言失败信息记录到文件中以便事后分析,并调用了C运行库的assert让进程退出。

在生产环境下开启assert似乎确非惯例,但这也许还谈不上对与错,更多可以看成是当时开发者们的一种选择,在“让程序带着bug继续运行”和“遭遇bug时尽早让程序退出”两者之间,他们选择了后者。当然这也可能是当时他们的一个无奈的选择:在产品诞生初期,Bug较多,为了快速在生产环境定位Bug而开启了assert。

不过时过境迁,客户对产品质量的要求越来越高,我们除了在线下通过各种方法提升产品质量外,在线上当程序出现Bug时的处理方式也要考虑做适当变化,遭遇Bug直接主动退出导致业务瞬间中断的方式被越来越多的开发人员质疑。或者现在的开发人员也是迫于一些外部压力,宁愿选择让程序带着Bug运行下去。

昨天我们针对这个问题做了一个内部讨论,考虑修改自有assert的实现,去除对C库assert的调用,只保留断言失败的信息记录。这样就不会导致程序遇Bug立即退出的情况。不过仍有一个项目组认为这种方案不能解决他们系统中遇到断言失败时无法自恢复并继续健康运行的问题,希望能在代码中感知断言失败,并对错误现场进行一些修复性处理,比如如果断言失败发生在加锁后,希望断言发生后能直接走到解锁环节。最后居然给出了一个让人哭笑不得的方案:给assert加上一个特定的返回值表示程序出现异常(多为Bug)。

先不谈给assert加上返回值有悖assert设计的初衷,如果真的给assert加上了返回值,那意味着什么呢?据不完全统计有如下几点需要改动:
1、assert的实现彻底颠覆,如果按照C标准库中对assert设计的要求,甚至可能是很难实现的;
2、你完全不能忽视assert,因为它还有返回值,甚至当断言失败时,会直接退出使用assert的那个函数;
3、大量遗留库代码和业务层代码需要判断assert的返回值以及使用assert的函数的返回值,增加对assert返回的所谓特殊错误码的处理;
4、实在想不出这个"恐怖"的想法还能带来什么改变。

对这样一个费力不讨好、不给力的想法我除了反对,还是反对。有这些时间还不如仔细琢磨一下如何提高设计能力和产品质量呢。如果非要这么做,建议放过assert吧,请重新实现一个能检查感知Bug的函数接口吧。

有选择的保留遗留“惯例”

在工作中,我们常常会听到这样的声音:“原来的系统就是这么做的!”。

没错儿,在工作中我们潜移默化地受到了遗留系统的一些设计和实现的“惯例”的影响,另外天生携带的懒惰基因使我们很少去思考和判断这些惯例的正确性和保留的必要性。但事实上,我们确应该经常重新审视这些遗留的“惯例”,有选择的保留,并敢于放弃。

每种“惯例”的引入和使用都是有其特定原因的:或是可以简化代码编写,或是便于代码跟踪,或是利于代码调试,或是迫于对外部工具的妥协,也有的是为了偷懒儿^_^,甚至有些惯例的引入本身就是不妥当的甚至是错误的,只是当时无人喊出反对的声音,也就被保留了下来。

新项目启动已两月有余,除了前期参与一些编码外,现在的我更多是代码检查和评审者的角色。在这个角色上,我看到了一些本不该保留下来的遗留“惯例”,这里挑了几个拿出来说说。

#1 – 在源代码文件中记录代码变更信息
在遗留系统的一个典型的“惯例”就是在源文件中(包括.h和.c文件)记录代码变更信息,比如下面的例子:

/*
 * foo_test.c
 *
 * NUM  | description    | by     | date      |
 * 001  | 添加XX业务逻辑 | xx     | 20100805  |
 * 002  | 修改YY业务逻辑 | yy     | 20100809  |
 * 003  | 删除ZZ业务逻辑 | zz     | 20100809  |
 * … …
 */

在foo_test.c的开始处,开发人员记录了所有关于这个文件的所有变更信息索引,此外在该源文件中充斥着诸如:001+、002*和003-这样的注释信息,以跟踪源码的变动。

这个遗留“惯例”的出处已无法追溯了,也许是当初没有对版本控制工具的功能特性有着很充分的认识,也许还可能是当初干脆就没有引入版本控制工具,不过无论如何,在subversion、git等版本控制工具大行其道的今天,这样的“惯例”已不再合适,它会使我们的代码不再clean。

现在我们一般推荐采用版本控制工具的commit log与ChangeLog相结合的方式来管理和跟踪代码变更,更精确的说是使用commit log详细跟踪代码变更,而使用ChangeLog来跟踪功能变更和某些重要的bugfix。 在提倡频繁提交与集成的今天,ChangeLog会显得尤为重要,它与commit log相辅相成。如果没有ChangeLog粗线条地记录项目功能变更,指望大家从数量繁多的commit log中提炼出功能变更是不现实的、不具备可操作性的、也是不可接受的。

#2 – 过度使用续行符
在新项目代码里,我发现了一些使用续行符的代码,诸如:
void foo(int a, int b, \
         char *p, \
         struct foo_t *f);

printf("%s\n", "Foo Test, \
               ,Test Foo");

printf("%d, %s, %d\n", \
        a,\
        "Foo Test",\
        b);

我之前只是在遗留系统中见识过类似的代码,显然这是受到了遗留代码“惯例”的影响了。

续行符,顾名思义,当一行代码过长,影响代码整体美观或超出编译器每行最大字符数限制时采用的方法,用于指示编译器续行符前后的两行实际上应作为一行处理。以前的编译器不足够智能,甚至每行支持的最大字符数也很少,有些时候确要使用续行符来辅助编码。但是如今编译器愈来愈强大,续行符的使用场合已经不多了。将上面代码改造为如下代码编译器也完成可以处理:
void foo(int a, int b,
         char *p,
         struct foo_t *f);

printf("%s\n", "Foo Test,"
               ",Test Foo");

printf("%d, %s, %d\n",
        a,
        "Foo Test",
        b);

其中第一个printf中分属两行的字符串会被编译器自动连接成一行字符串对待,这还可以解决使用续行符导致的"Foo Test"和",Test Foo"之间出现多余空格的情况。续行符可以用到的场合似乎只剩下多行宏定义中了。

#3 subversion源码库中保存二进制文件
我们的产品一直运行在Sun小型机上,CPU是Ultra Sparc,OS为Solaris,长期如此也导致了我们很少考虑可移植性问题,甚至在遗留系统中,我们直接将第三方库的sparc solaris版本的.a文件放入了Subversion的源码库中管理,这样我们Checkout出来后便可以直接链接生成可执行文件。

这样的“惯例”延续到了新项目中,不过我们的新项目近期修改了目标,可移植性列上了日程,目标平台不再只是单一的Sparc Solaris了。如果我们继续坚守这个“惯例”,那么将会给我们带来不小的第三方库版本管理上的麻烦,甚至连Makefile都要跟随着不断做调整。所以是时候将特定目标平台的二进制.a文件从源码库中移除了,具体方法这里不赘述了。

变化是永恒的,经常反思一下你日常工作中那些所谓的“惯例”,它们真的值得继续保留下去吗?




这里是Tony Bai的个人Blog,欢迎访问、订阅和留言!订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:


如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:


以太币:


如果您喜欢通过微信App浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:



本站Powered by Digital Ocean VPS。

选择Digital Ocean VPS主机,即可获得10美元现金充值,可免费使用两个月哟!

著名主机提供商Linode 10$优惠码:linode10,在这里注册即可免费获得。

阿里云推荐码:1WFZ0V立享9折!

View Tony Bai's profile on LinkedIn


文章

评论

  • 正在加载...

分类

标签

归档











更多