许超前的博客 - A longker in the Earth

November 24, 2009

用Hudson+Subversion+Maven搭建持续集成(Continuous Integration, CI)环境

Filed under: Daily Life — 许超前 @ 10:05 pm

为了让接下来的研发工作能更顺利地开展,花了点时间给新团队搭建了个持续集成环境。这里用到的工具主要有:HudsonSubversionMaven等等。

关于持续集成这个概念,有兴趣的可以参考Martin Fowler的一篇文章:Continuous Integration

之前,对那些JAVA编写的服务程序,我们采用的构建方式很原始:手动写Shell脚本,把SVN中的JAVA源码编译成JAVA字节码。应该说,在团队规模较小(不需要太多的协同工作)、部署环境单一(我们之前只部署在Linux环境当中)的时候,这种方式还是不错的,起码两年以来,我们并没有遇到什么问题。

那现在为什么要来搭这个环境?持续集成带来的好处有很多。。。在这我只说说我们这么做的主要原因:
一、进度控制力度增强了,需要快速迭代,也需要更快地看到结果。
二、可测试性要求提高了,所有的代码必须是自测试的。
三、对外发布的不再是源代码,而是可执行文件了。
四、给团队成员一个全新的体验,走正规化开发道路。
。。。

以下是搭建好的持续集成环境的结构图:
Continuous Integration

—The end.

November 17, 2009

试用Google Closure Compiler的API

Filed under: Daily Life — 许超前 @ 7:22 pm

原来的JS压缩服务采用的是YUI Compressor,一年下来表现还算稳定。

今天老高说想尝试一下Google Closure Compiler,翻了翻文档,看看有没API能直接用的:传一个原始字符串,返回一个压缩后的字符串。找了半天,没有此类的例子(看来设计者认为像我们这样直接采用API而不是使用命令行的用户不多)。

查看JavaDoc,定位到com.google.javascript.jscomp.Compiler,有如下描述:
Compiler (and the other classes in this package) does the following:
* parses JS code
* checks for undefined variables
* performs optimizations such as constant folding and constants inlining
* renames variables (to short names)
* outputs compact javascript code
看来就是这个了。

相中方法: Result compile(JSSourceFile extern, JSSourceFile input, CompilerOptions options) 。
input和options容易理解,extern是什么?其实类的描述里也稍微提了下:
External variables are declared in ‘externs’ files. For instance, the file may include definitions for global javascript/browser objects such as window, document.
很显然可以没有extern(但不能为NULL,设置方法参看底下的代码)。

按道理,编译后返回的result里应该就有我想要的结果了吧,结果出乎意料。Result只有编译状态的描述。难道此路不通?

又从JAR包的META-INF看到入口类是CompilerRunner,一路追踪下去。。。中间省略文字几千字。绕了半天,得出的结论是:和CompilerRunner相关的几个类很难复用,想用的方法要么是不可见,要么是包可见,要么是子类可见。代理来继承去,眼看就OK了,又发现很多状态居然是static的,这使得程序的状态是迭加的。看来此路也不通。。。又绕回Compiler。晕,原来Compiler有一个方法toSource()就是返回压缩化的代码的。

这下好办了。主要代码如下:
final ByteArrayOutputStream err = new ByteArrayOutputStream();
final PrintStream errWrapper = new PrintStream(err);
final Compiler compiler = new Compiler(errWrapper);

final ByteArrayInputStream bais = new ByteArrayInputStream(codeBytes);

final CompilerOptions options = new CompilerOptions();
final CompilationLevel level = CompilationLevel.SIMPLE_OPTIMIZATIONS
level.setOptionsForCompilationLevel(options);

final Result status = compiler.compile(JSSourceFile.fromCode("extern", ""), JSSourceFile.fromInputStream("input", bais), options);
System.out.println(compiler.toSource);

把代码布上后,测试通过。本来这是一件小事,没必要以此作为BLOG的内容。但是这件事也是给了我几个感触,觉得有必要说一说:
一、文档很重要。简单的几句话,也许就能让用户少走很多弯路。DAL后面一定要多写文档,特别是使用手册。
二、API设计要符合直觉。不符合直觉的设计会造成大量的困扰。当年Lucene的delete操作,用脚跟想想就应该放在IndexWriter里,可设计者却把它放在IndexReader里,结果邮件列表里经常出现此类的问题。当然,后续版本已经做了修改(IndexWriter有了delete方法)。用户不会关心你为什么这么做;新用户要抛弃你只要点一下鼠标就可以了;而老用户选择离开只需要皱一下眉头。
三、时间管理很重要。任何事情都应该在任务框架里进行;否则,很多东西可能会失控。

—The end.

October 24, 2009

SD2China大会演讲结束,<<手机之家的数据访问层实践>>PPT下载

Filed under: Daily Life — 许超前 @ 5:22 pm

今天去的比较晚,12点20分才到会议中心。那时还没吃早饭(刚买的一杯豆浆还很烫),已经饿得发慌了,于是去了食堂,吃了一个馒头,喝了一碗粥。

到会场12点30分左右。工作人员也到场了。放上PPT,我也坐到旁边和同事聊了会。这时发现会议室里已经坐満了人,还有因没位置站着的朋友。

演讲从下午1:00点开始,一直到下午2:10,总共需时70分钟。在演讲进行到一半的时候,我已经明显感觉到自己的肚子在叫了:)

整个演讲过程,勉强过得去,我打60分。
因为时间有限,我无法去谈一些过于细节的东西,只能是说说思路。不过其实我们做开发有时候缺的就是思路,不是吗?
对于这些,还希望当时在场的朋友多多包含,如果有比较模糊的地方,可以给我发邮件,再进行交流。

感谢我的家人和同事,感谢各位支持我的朋友,谢谢。

以下是这次演讲的PPT,点此下载

October 23, 2009

参加SD2China大会,演讲主题是:手机之家的数据访问层实践

Filed under: Daily Life — 许超前 @ 9:37 pm

演讲主题改了好几回,最后还是选了这个,简单、朴实。
和大家分享的是:DAL从无到有的演变过程,以及在此过程当中我们所获得的经验和教训,还有一些总结性的经过证明的知识点,最后是DAL的未来规划。欢迎各位朋友前来一同探讨。

地点:北京温都水城会议中心3层第32会议室
时间:2009年10月24日下午第一场
交通:乘坐地铁5号线或其他线路在崇文门、东单、雍和宫、惠新西街南口、立水桥站换成地铁5号线在天通苑北下车,出战乘坐会务组班车到达温都水城。

October 18, 2009

出差长沙第7天,所见所闻所行所思

Filed under: Daily Life — 许超前 @ 12:46 pm

算上今天,来长沙已经7天了。

此行来长沙的目的,是改造DAL以支持Oracle数据库。
由于之前在开发DAL的时候,在支持多数据库机制上面做了充分的考虑,改动起来还比较顺利。

现在想起来,如果当时不那么坚持,那这次就不是三两天能搞定的事了。“人无远虑,必有近忧”,是矣。

现在,似乎有一个风气,那就是“敏捷为王”。东西先跑起来再说,后面再慢慢改进。有人甚至主张Just-In-Time Architecture,对此,我持保留意见。也许某些领域,应该这么做;但有些领域,若是这么做,可能会产生无法预计的后果。有些东西一旦成形,才想到要除掉它,那将是成倍的代价,很难。

这种风气已经蔓延到其它领域。我就经常发现,有些产品经理对于自己提出来的需求往往是没有经过仔细思考的、没做过调查的,不管用户到底是不是真的需要,不用任何科学的分析手段,就那么一拍脑袋,“喂,那个谁,你给我改改,急用”。现代人,谁不急?

极端要不得。一个度字。不同的人,不同的角度,不同的认知,会触发不同的行为,最终产生不同的结果。

DAL的开发,暂告一个段落。最近杂事很多,对DAL的投入明显没有之前那么多了。
上次接受JavaEye采访,很多朋友对我每周工作80个小时觉得诧异。我要说的是,有段时间我每周工作的时间达到100个小时以上。
其实,之前一个长周期的项目已经让我身心俱疲了,在这种情况下了,我接下了设计和开发DAL2.x的任务,着实是出自于职业操守,而非兴趣。DAL1.0由我开发,我知道其中的问题,如果不把这些问题解决,我睡不着觉。这关乎手机之家1000万用户的使用体验。
DAL1.0到DAL2.x的跨度非常大(包括代码和思想),同时又要保证平滑升级(不改应用程序的逻辑),当然还要保证数据的一致性,关键数据一旦出错,后果很难想象。
有心人应该能了解这其中的工作量了吧。到了后期,完全是凭自己的意志在坚持了。

家里人有时候也会责备,问这么做到底是为了啥?图个啥?

一个中国的开发人员,他热爱自己的职业,他有着一个10年的梦想,他坚信自己能开发出一个有用的软件,他坚信自己能和国外的优秀同行做得一样好。这就是坚持的理由。

在长沙出差这几天,总体还过得去(在这要感谢长沙的新同事)。这里气候比北京要湿润的多,饮食也还适应,睡觉也还习惯。走在园区的小路上,还能闻到阵阵桂花香。这让我想起当年在桂子山上上学的情形,满山的桂子花,而我就在树下的凳子上读书,在林荫小道上慢悠悠地骑车。

呜忽,皆往矣。

光阴无法倒转,人得往前走、向前看。感激、赞美身边的每一个人,活在当下!

再过3天,我就要回北京了。北京虽然气候恶劣,但在北京,我有一种归属感。
赶回去有三个原因,一是长沙这边的事已经差不多了,第二是家里有一堆事,等着我回去处理。最后一个是要回去参加CSDN举办的SD2China大会,做主题演讲。不知道到时候会不会遇到老同学?

过后,就是今年的最后一个季度了。要怎么过,得想一想了。

—The End.

September 12, 2009

终于把搜索更新改成基于MQ(Message Queue, 消息队列)的方式了

Filed under: Daily Life — 许超前 @ 1:45 am

经过同事们的一番努力,终于把搜索更新改为基于MQ的方式了,大家(特别是增禄和大庆)辛苦了。

搜索更新早就想改了,因为种种限制,无法实施。这些限制如下:
一)手机之家采用混合编程(主要是PHP+JAVA)。
二)JAVA调用PHP显然不是好方法。
三)用PHP做异步触发很困难。
四)用PHP写驻留程序很困难。
五)要让PHP在消费消息失败时回滚很困难。
六)基于这些限制,DAL1.0最后用PHP和数据库实现了一个不太可靠的消息队列。这显然不太好。
“旧”更新方式如下图所示:
Screenshot-001

DAL升级到2.x后,之前的种种限制已经消失了,搜索更新一下子有了很大的改进空间。具体改进如下:
一)引入双队列,为的是能异步触发,从而节省内存、降低CPU占用率,进而提高负载能力。DAL2.x内置队列写消息非常快,写一条消息耗时不到1微秒(不是毫秒,了解个大概,省略测试环境)。
二)(部分)拉改成了推,“近实时“更新有了可能性。时间关系,留待下一步继续改进。
“新”更新方式如下图所示:
Screenshot-002

接下来可能会抽个时间试试以下的方式(废弃Crond,主要是为了减少更新延迟):
Screenshot-003

注:图中提到的M3,是Massive Message Manager的简称,是我们的大庆在近期开发的作品,在此鼓励一下:)。

—The End.

September 7, 2009

手机之家分布式(Distributed)数据访问层(Data Access Layer)软件—DAL2.1.1发布了

Filed under: Daily Life — 许超前 @ 1:10 am

DAL2.1.1是一个非常重要的版本,到此为止,当初的想法已经得到了相对较好的实现。刚来的朋友可以看这两篇Blogs:
beta技术沙龙结束,开始忙正事了。。。DAL2.0开发已近尾声,在这稍微说说情况,来了解一下DAL的相关词汇及来龙去脉。

总的来说,DAL是团队近几年在开发和运营上的经验的总结以及智慧的结晶。一年前,开发DAL是一件水到渠成的事;一年后,不断改进DAL则是为了在未来5~10年、甚至更长远的时期内,(在数据访问层)支撑着手机之家传统业务的快速增长以及新业务的持续开拓和稳步扩充。后者,决定了新版DAL的项目目标。

具体说来,当时定的目标是这样的:
一)可伸缩。
这里是指Scale Out.,即水平可伸缩。事实上,这点更应该是整个系统要考虑的目标了,而非DAL,DAL要考虑的是怎么更好地支持。举例说,我们可以一个库一个服务,甚至可以是一个表一个服务;库、表拆分后,DAL应能路由查询、合并结果,而不是让应用程序去操心这些事。

二)高可用性。
1) 我们认为出错失败是很正常的,一台机器倒下了,其它机器应继续保持系统正常运作。容错是很重要的一个要求。
2) 系统规模大了以后,很容易出现“异构“的情况,如原有模块MySQL表引擎是MyISAM的,是不支持事务的,而新上的模块又采用了InnoDB表引擎,在这种情况下,DAL应能对原有模块进行优雅降级。
3)失败恢复也是要考虑的,失败后,需要把失败前驻留在内存中的消息找回来。
4) 另外,DAL本身也在快速的迭代当中,升级是很经常的事,应能进行在线热升级(不重启原有服务)。

三)良好的性能。
对于根据Id来取记录的查询,在缓存命中的情况下,应该达到和Memcached不相上下的读取速度。在缓存不命中的情况下,则应该充分利用分库分表和并行计算的优势,最大化地提高查询的效率。对于修改型查询,挂在上面的监听器,不应该影响性能。

四)系统可监控。
资源占用情况,命中率如何,系统当前压力怎样等等,都应该是可知的。应该有报警机制,当压力到达一个阀值以后,通知相关人员进行处理。还应该有详细的错误日志,便于排查问题。

五)安全。
没有SQL注入问题;避免或尽量减少分表和索引表之间的数据不一致问题等等。

六)易于编程。
需要设计一套简单好用的API,便于应用程序的开发。API必须是自完备的,应用开发者不需要太费力就能记住的。
应用开发人员不再关心分库分表问题,不再关心缓存问题, 特别是缓存清除问题。甚至不再关心后端的数据库是MySQL,还是Oracle,或者是其它。

七)可定制、可扩展、可维护的架构设计。
像连接池组件、缓存组件、查询分析组件、消息队列组件、通讯协议等等不应该写死,应设计成可方便定制的。还应该提供足够的钩子用于扩展。只有这样,DAL的架构才是灵活的、拥抱变化的。简单说,我们定的是机制,提供的是策略;机制是软件目标和宗旨的体现,一般是不能轻易改变的,而策略则应当是能比较简单地进行切换的。

接下来我以图片、文字结合的方式稍微详细地介绍一下DAL。
一)高空鸟瞰。
1)一个DAL服务对一个DB服务。
Screenshot

2)一个DAL服务对多个DB服务。
Screenshot-1

3)多个DAL服务对多个DB服务。
Screenshot-2

4)一个Cluster对多个DB服务
Screenshot-3

—to be continued…

July 26, 2009

欢迎再次来参加手机之家主持的BETA技术沙龙

Filed under: Daily Life — 许超前 @ 9:42 am

主讲人唐福林是一个思维很活跃的人,和他交流,我相信你会有收获的。欢迎lucene爱好者、搜索爱好者前来一同探讨。

——————————————————

时间:7月26日14点30分开始
地点:奇遇花园咖啡馆http://storygarden.me/cafe/map

主题:大型网站的lucene搜索实战
演讲简介:本次活动介绍基于Lucene的站内搜索的实践,后台技术层面的一些想法与实践,包括缩短更新周期,简化重建索引流程,支持大数据量频繁更新的索引,以及在性能和可用性方面作的努力。
主讲人:唐福林
主讲人简介:从高中的 NOIP 到大学的ACM,是编程竞赛的参与者,也是算法的爱好者,以追求程序或系统的性能的极致为乐。 目前就职于手机之家,负责基于lucene的站内搜索的开发及持续改进。

May 18, 2009

DAL2.0开发已近尾声,在这稍微说说情况

Filed under: Daily Life — 许超前 @ 1:04 am

DAL全称Data Access Layer,是一个由手机之家技术团队研发的中间件,原先我们做的只是一个数据库代理层程序,作用类似于MySQL Proxy,主要功能包括数据表拆分,以及缓存更新与清除的自动化、透明化等等。但现在,我们做得更多,走得也更远,可以在这篇BLOG中了解一些这方面的信息。

DAL2.0是DAL的下一代版本。DAL2.0的规划,其实从去年在三元桥时间国际7楼的小会室里就开始了,老高提出了初始想法,经过讨论以后,一拍即合,觉得应该抽出一个整块的时间来做这个事。

正式开发,从2月底3月初开始,原定计划是4月底5月初交上答卷,很遗憾,项目已经延期了。当然,非要找一些借口的话,也是有的:找人,组建团队,沟通,第二期BETA沙龙,QCON北京大会,又找人,会人。。。但其实,最主要还是因为:我没安排好任务,没管理好时间(是情商问题而非智商问题)。

虽然公司给予了我们团队很多的理解和支持,但我认为这是有问题的。因此,从5月4号(五一节后)开始,我做了一些调整:关掉Gtalk,不访问Twitter,不收Email,专心致志开始了2周的封闭式开发。成效是明显的,不少难题都是在这2周内解决的。有些朋友可能找不到我,在这说Sorry了。

接下来,我稍微说说技术的一些情况。

与DAL1.0比起来,DAL2.0的变化是非常大的:
DAL1.0-VS-DAL2.0

显然,这些变化不是一言两语就能说清楚的,其中有很多细节问题,在这不再赘述。
虽然,和DAL2.0比起来,DAL1.0显得单薄了些,但其实她是一个非常重要的里程碑,有很多想法通过她已经被证明是可行的。
DAL2.0的开发还在继续,还需要不断地努力、持续地投入。在运行之前,需要大量的测试:单元测试、集成测试,旁路测试等等。

总之,我们是对DAL2.0是非常有信心的,欢迎大家继续关注我们,并提出宝贵意见。

May 1, 2009

DAL开发杂记 - 复数变量命名约定

Filed under: Daily Life — 许超前 @ 12:40 am

昨天写了一个方法,然后用JUnit进行单元测试,愣是通不过去。检查了半天,发现是我误解了自己一个月以前取的一个变量名了。很郁闷,一个不友好的变量名浪费了那么多时间。

为此,我觉得有必要约定一下Java程序代码里变量的命名(其它语言同样适用,无非是大小写、下划线方面的区别),以免再发生类似的错误。对于语法级的命名约定(规范)可以看Java命名约定(规范),这里要说语义级的命名约定(规范),而且只涉及复数的情况。

以下是复数变量的命名模板(含推理过程):

一个学生的多本图书 a student’s books -> studentBooks
很多学生共有的多本图书 students’ books -> studentsBooks // 强调共同财产
每个学生的多本图书的集合 the list of each student’s books ->studentBooksList // 强调私有财产
学生图书的集合 student-book’s list ->studentBookList // 强调书的集合

套用该模板的几个示例:

primaryKeyColumnNames: List< String> [a, b]
trueUniqueKeyColumnNamesList: List< List< String>> [[c], [d]]
trueUniqueKeyColumnNameList: List< String> [c, d]
trueUniqueKeyColumnNames: List< String> [c]

uniqueKeyColumnNamesList: List< List< String>> [[a, b], [c], [d]]
uniqueKeyColumnNameList: List< String> [a, b, c, d]
uniqueKeyColumnNames: List< String> [a, b]

specColumnNameList: List< String> [a, b, c, d, f] f为scatter column name

columnNames: List< String> [a, b, c, d, f, g, e, h]

注:<后加了个空格,免得浏览器把它当作HTML标签的开始。

Older Posts »

Powered by WordPress, 京ICP备09047672号