【IT168 评论】时下大多数开发人员对持续集成(Continuous Integration,CI)的基本原理已经很熟悉,但是他们中只有一小部分人能够从优化CI设置中彻底受益。毫无疑问,一个有效的持续集成环境可以帮助你的团队节省时间、金钱甚至减少存有的顾虑。通过持续集成,我们可以更早地发现bug,更轻松地找出导致其发生的原因并最终有效地解决。持续集成可以更好地管理源代码,更有效地使用自动化分析工具,鼓励编写好的测试,跟踪进度以及突破开发人员活动中的瓶颈。持续集成可以让部署过程更简单且发布过程更加平稳和可靠。借助于CI,管理者可以得到更多的图表,了解到更多的信息,而开发者可以专注于开发而不用过多交流,也就更加高兴。换句话说,如果不使用持续集成,那么就好比用记事本编写代码来开发软件,虽然可行,但是太低效了。
在本文中,来自Wakaleo咨询公司的首席咨询师John Smart,将给我们介绍如何把持续集成由一个名义上的定时作业,变为开发活动中一个有效、而且能提高生产力的“中枢”。
持续沟通流
一个良好的敏捷开发环境本质特征就是尽可能的最大化小组成员间的信息流。每一个开发人员都需要尽快地知道何时构建过程失败,或者何处改动可能会对应用程序质量造成不良影响。如果构建过程失败了,那么首要工作就是要知道做了什么改动,以及为什么做这些改动:所有的这些信息都应当在开发人员敲击几下键盘后就能通过最直接的方式获得。
即便是最基本的CI设置,它也会在构建失败后给开发人员发送电子邮件。其实我们只需要下一点点功夫,就能比现在做得更好。一个良好设置的持续集成服务器应该像团队沟通中的中枢一样工作。除了简单地发送电子邮件进行提醒外,其实还有很多事情可以做,例如现代CI工具中的许多特性——这些特性可以更平稳、更有效地向开发人员反映代码改动和构建失败的情况。
电子邮件大概是CI提醒方式中最古老也是最常使用的方式了,只是因为它非常普遍而且易于建立。尽管如此,事实上电子邮件是一个相对低效的提醒机制。当构建过程失败时,你希望被尽快地通知到,越快越好。如果因为电子邮箱客户端的更新而等上15分钟的话,那么可能就浪费掉了开发时间。除此之外,电子邮件通常很容易让开发人员分心。在企业里,每天的非紧急的或是不重要的电子邮件都会有很多。事实上许多优秀的开发人员都禁用电子邮件提示,只是在一天中定时地去检查几次邮件。
即时消息相对而言则是一个更加合适的提醒方式,有好几个原因。最重要的原因是,即时消息是(几乎是)即时的。你不再需要因为电子邮件客户端在服务器中检查新邮件而等上10分钟,使用即时消息,你能被立即通知到。它比电子邮件更加方便,而且阅读起来也更快。不仅如此,来自构建服务器的IM消息也会得到相关的重视,而不会淹没在大量无用的其它信息中。
当事件发生后,使用快速即时信息和花上10到15分钟使用电子邮件也许只相差几分钟,但千万不要低估这几分钟的重要性。那几分钟足够让一个开发人员丢失重点,并转移到其它的一些事情上,结果就导致了开发人员很难回到上下文中去修复问题。
使用即时消息的另外一个好处在于它可以扩展到桌面以外的应用。像BeeJive这样的应用程序可以将即时消息用到移动设备诸如Blackberries(黑莓)和iPhones上。这样的话,即使开发人员不在办公室,也能收到至关重要的构建失败提醒。
即时消息提醒比起电子邮件,可以更多的与CI服务器进行交互。作为一个沟通的中介,即时消息要比电子邮件更加的自然,它还可以进行比 Subversion 提交信息还要多的交互。通过利用即时消息,如今的一些CI工具不仅仅可以发送提醒,还可以让开发人员更轻松地与构建服务器以及其他组员进行交互与交流。
当然,即时消息不是仅有的另一种可用提醒方式。如果想让大家注意到构建信息的话,最好采用一种能够融入相应企业文化的提醒机制。例如,一些公司使用社交网 络的工具例如Twitter,作为一个有效的内部交流渠道。对于这些组织而言,使用Twitter也可以算是一种有效的构建提示方法了。
保持构建过程高效率
持续集成环境的一个首要目标是要保证开发过程的顺利进行,并且避免由集成问题带来的障碍和开发延期。当集成问题突然出现时,开发人员应当有义务尽快的去提交一些代码来解决这个问题,以避免影响到其他开发人员。如果没有持续集成环境,开发人员通常会在集成问题上卡住而找不到一个解决方案(“嘿,它在我的机器上能运行啊!”)。
想让开发过程一直保持最好的状态,那么团队成员(尤其是团队领头人,流程专家等)需要能够监测构建过程,这样他们可以识别定位出日常生活里降低开发人员效率的问题。其中最好的方法就是去了解如何最好地使用构建感测。
构建感测是一个用在持续集成周期中,用以描述构建长期统计数据的术语。Bamboo,一个不错的CI工具,它就提供了很高级的构建特性。构建感测为你提供了关于构建要花多久,是否成功,以及解决这个构建失败问题要花多久等等之类的信息。这些数据很重要,因为它可以让你知道构建过程长期以来是什么样的。正是这种数据,而不是单个构建的结果,可以帮助你最终优化构建过程。
一定数量和频率的构建失败一直是一个不错的着手点。隔离的构建失败通常不需要担心——你需要做的是去检查一系列反复出现的构建失败。当一个构建反复失败时,也许是因为开发人员正纠结于一段非常麻烦的代码,或者是团队人员没注意到。虽然这两问题的原因不一样,并且解决的方法也不一样,但是它们都应当被进一步地调查。
通过深入分析测试结果,你可以了解到更多关于为什么构建会失败的信息。许多现代的CI工具都可以让你研究长期的测试行为,例如,把一直经常失败的、或者要花很长时间进行解决的测试跟其他测试隔离开来。如果同样的测试不断失败的话,这就意味着有什么地方过于复杂或者代码过于脆弱,这种情况下可以做一些重构。除此之外,这些工具还能让你知道测试运行了多久,这是另一类问题发生的源头。
事实上,构建失败不是唯一减慢开发进程的原因。缓慢的构建过程是另一个罪魁祸首。
导致构建过程缓慢的最常见的原因是结构混乱的测试套件。经验丰富的Java开发人员常常会将单元测试与集成测试分开来。虽然两者区别大差不差,但是一般来说,单元测试是相对孤立的、较小的、快速的、轻量级的测试类。它们主要是用来确保类能够独立地正常工作。而另一方面,集成测试则较为缓慢,需要更长的运行时间,并且可能会访问外部的资源如测试数据库,或者加载复杂的配置文件。它们被用来测试应用程序中的不同模块和类如何共同工作。性能测试和集成测试差不多,但是两者的目标有所不同。
轻量级且运行很快的单元测试可以被迅速的执行完,并且在测试失败时能够给出很快的反馈。而另一方面,如果缓慢运行的集成测试与单元测试混在一起,那么单元测试将要花上更多的时间来执行,结果开发人员也要等更久才会得到关于单元测试失败的消息。解决之道就是为单元测试和集成/性能测试创建单独的构建计划。使用这种方式后,如果单元测试构建计划失败了,由于其执行速度很快,开发人员不再需要等待很长时间才被通知到构建失败。如果单元测试成功后,集成测试和性能测试计划才会开始。
另外,还有一个补充方法——可以使用分布式构建。例如,如果你的web功能测试由于要在几个不同的浏览器上运行并因此消耗很长时间,那么可以为每一个浏览器设立一个构建作业,以让它们(可能在不同的机器上)并行运行。
另一个问题来自过于缓慢和效率低下的测试用例。有许多方法可以用来监测这些缓慢的测试。提高测试上的运行时间意味着有些测试会由于所需时间太长而无法运行。这也许是因为它们被设计的很糟糕,也许是因为性能问题(需要进步一深究),抑或是集成测试伪装成了单元测试。
密切关注代码质量
持续集成服务器不应当仅仅是一个自动构建的机器。它应当成为你团队中沟通的中枢,尤其在代码质量上。时刻关注编码规范和一些度量值如代码覆盖率以及代码复杂度,它们可以让应用程序更加可靠且更易维护。
有很多优秀的工具可以帮助维护应用程序中良好规范的代码。静态分析工具如Checkstyle,PMD和FindBugs,它们根据代码标准、非常好的实践或者潜在的bug来对代码进行分析。你所有使用的工具如何配置要依赖于你想要达到的目标。例如,Checkstyle更多关注于编码规范和非常好的实践,而 Findbugs则更倾向于找出错误的,破碎的或者危险的代码。所有的这些工具可以很轻松地集成到自动化构建过程中,并且可以和Ant、Maven一起很好地工作。
覆盖测试率是代码质量的另一个方面。它用来衡量测试执行时所访问的代码行数。Java开发人员中对覆盖测试率统计的相对值仍然有着分歧。事实上,虽然它可以告诉你应用程序中的哪些行被执行,但是没法知道那些测试是写得很彻底、写得很好,抑或仅仅是简单的走马观花。总而言之,测试覆盖率不能保证你的测试质量很高——只有人工进行代码检查时才能确保如此。然而覆盖测试率的度量值可以很好地展示出哪些代码没有被测试过。在Java世界里,用得最多的代码覆盖率测试工具当属Clover和Cobertura,前者是一个非常强大的商业代码覆盖率测试工具,而后者是一个更加轻量级的开源工具。它们都可以很容易地集成到基于Ant和Maven的构建脚本中。
编码规范也可以作为非常有效的培训支持和指导活动,尤其对于那些经验不足的开发人员。CI工具可以提供这些数据如何随着时间的推移而变化的高层次图片,还可以关注开发人员在应用他们学到的技巧时做得有多好。例如,如果一个类只有很低的代码覆盖率,甚至没有,就意味着某个新的开发人员在消化吸收小组培训的测试驱动开发和测试实践上出现了问题。这种方法还可以通过代码审查和定期的代码质量会议(讨论任何新问题或者动向的会议)完成。
一旦构建结束——自动化部署过程
构建应用程序只是开发生命周期中的一部分。一旦代码编译测试后,需要进行其他的活动,例如部署到阶段性(staging)环境、冒烟测试、功能测试和性能测试、准备发布说明和提醒QA人员最新的发布。
将最新的构建结果自动部署到集成服务器上是一件相对简单的事情。而将其部署到阶段性环境或者生产环境下,则需要涉及一些与常规构建作业不一样的工作。一般而言你需要一个更加严格,更加正规且有更多可跟踪性和问责的过程。它通常涉及到的任务如下:
* 为阶段发布标记源代码
* 编译测试应用程序
* 发布构建产品
* 将应用程序部署到阶段性环境中
* 运行数据库更新脚本或者其他特定环境脚本
* 运行冒烟,功能和性能测试
* 准备并发布产品说明
* 提醒关于最新阶段发布的相关利益人
这通常是一个手工任务,但是其中的大部分工作没有理由不能自动化。事实上,开发生命周期中的自动化包装,部署和发布具有很稳固的商业意义。一方面自动化能够得到更加可靠的构建:计算机不会忘记部署过程中的某一步,也不会在发生测试失败后继续进行。它还能够节约开发人员的时间:阶段性发布由之前几小时的 shell脚本编程变为了只要点击一下按钮。它比以前的速度要更快,并且可以在没有人的情况下完成工作(例如,通宵或者午休时间)。
像Maven 2这样的工具也能够帮助自动化一些步骤。Maven Release插件使得Maven的用户能够自动化处理一些如“更新版本号”,“Subversion中新增标签”,以及“向Maven存储库中发布构建产品”的工作。它可以用来管理阶段构建,并决定在不同的环境部署不同的发布产品。尽管如此,一旦产品构建结束并且可以部署到阶段性环境者生产环境时,这个过程就会变得更复杂。
千真万确,现实世界中的部署步骤数目经常要比简单的用一个WAR文件多。根据应用程序架构和产品平台,你可能需要在阶段性环境者生产环境下的数据库中运行SQL更新脚本、用一个专用的工具部署web服务、运行自动化的冒烟测试或者做一定量服务端的工作。
CI可以帮助简化比这些还要复杂的步骤。例如通过分布式构建,你可以设立阶段性环境或生产环境上的构建代理,并在该机器上直接运行相应的任务。几乎所有的 现代CI工具都支持相当好的安全模型,目的是为了将应用和产品环境限制给一些特定的人,以及跟踪谁在什么时间运行了什么构建。
这是CI的一个相对较新的应用,不同的工具在处理应用程序部署时使用的方法也不一样。有一些,如Hudson,允许在构建作业中定义多个步骤,只有当前一 个步骤成功后,才能执行后续步骤。其他的像Cruise和Anthill Pro,都尝试将部署生命周期中的如阶段和生产环境直接集成到构建工具中,尽管有时候这样会带来额外的复杂度开销。
还有更多低层次的操作可以和CI服务器联合起来使用。一个选择是使用诸如Ant或者Maven的构建工具。Ant对于这种类型的脚本特别灵活。另一个流行的选择是古老的Makefile,或者Unix上的shell脚本。它们的缺点是操作系统相关的,并且对于那些不熟悉shell脚本精髓的Java 开发人员来说很难掌握。想要与Java更加友好,可以选择动态语言诸如Groovy或者Gant(一个使用Groovy而不是XML来制作Ant脚本的工具)。 Groovy在提供所有轻量级的动态脚本语言中所有的优点的同时,也保留了对Java开发人员的熟悉程度和可读性。
总结
这些只是使用现代持续集成环境完善构建过程和增强团队的几个方法。持续集成环境绝对不仅仅是一个构建计划表,它还可以用来帮助打开队伍内部的沟通渠道、保持构建过程平稳有效的运行、时刻监控代码质量以及自动化发布和部署过程。