InfoQ:在应用程序稳定性方面我们面临哪些共通的问题?怎样解决这些问题?
Michael Nygard:在书中我阐述了很多这类问题,我把它们叫做“反稳定模式”,并且提出了一组“稳定模式”来一一化解。
根本上还是如何平稳退化的问题。错误源自将系统看作统一的实体。从用户的角度和从投资者的角度看,系统实际是一个由相关功能组成的集合,有些功能非常重要,有些却不那么重要。我们所要做的就是尽量保持重要功能的正常运转,同时抛弃掉行为不当引起系统不稳定的功能。
例如,如果你问“在餐厅搜索功能失效的情况下是否还能预定宾馆房间”这样的问题,可能会受到嘲笑。这两个功能看上去并没有直接联系啊!为什么在第三方搜索功能不能正常工作的时候要停止预定服务呢?然而,这些功能是互相耦合的,因为页面请求都是同一个应用服务器上的同一个请求处理线程池来处理的。在搜索服务变慢了或者停止响应了的时候,如果你让所有请求处理线程都阻塞起来等待响应,那就是让核心业务被次要功能所累了。
InfoQ: 在应用程序容量方面我们面临哪些共通的问题?怎样解决这些问题?
Michael Nygard:我也经常见到很多这类问题,我把它们叫做“容量反模式”。它们大都源自于那些忽略了乘法效应的开发人员。
此方面我也将举个例子来说明。每个零售商都会以某种方式来组织其商品目录,并将之以菜单的形式放在自己的网页上作为导航。每当看到这类东西的时候,我都会问“这些目录结构会多久变化一次?它的访问频率是多高呢?“,而答案往往是”一月一次“和”每秒四百次“。既然如此,为什么要动态产生呢?仅仅因为不这样做的话,当其更新后显示新的结构会有几百纳秒的延迟吗?这简直是毫无意义的事情。好的做法是在目录内容变化时生成一次HTML,然后每次显示页面的时候读取这个缓存起来的片段。(更好的方法是交给Akamai公司,让他们帮你通过Edge脚本将其填充到你的页面[译注:Akamai公司和Edge Side Includes标记语言请参见
http://www.akamai.com/html/support/esi.html]) 因此,在系统消耗资源的每一个地方,都要问一下自己它的乘法效应。
InfoQ: 除了稳定性和容量外,还有哪类问题必须注意的?
Michael Nygard:通常我们还会遇到以下两类情形:不透明系统和刚性系统。不透明系统就像生病的金鱼,我们只能任其生死,没法对其做任何补救工作。对此类的系统,你无从了解它是怎样运行的,也无法知道它是否在正常运行。
幸运的是情况正在发生变化,诸如Nagios和Zenoss之类的免费监控软件陆续出现,对系统进行监控的能力日益变得强大。
刚性系统就是无法演进或者很难演进的系统,只能停止服务才能实施更改的系统也属于其中。刚性系统需要重启才能部署新代码或者更新其内容。此类系统通常会进行版本锁定,这往往会对企业信息系统中的诸多方面产生影响,因为对其进行更新,不仅仅需要承担很大的风险,而且需要协调受影响的各部门以统一升级时间。
InfoQ: 运营人员是怎样与产品级软件相配合的呢?他们是否牵扯进开发过程,是否帮助定义产品,是否是在产品接近发布的时候接受训练?
Michael Nygard:运营人员被整合进创建产品级软件的过程。开发和构架有一种趋势,就是在很长时间里都过于抽象。我认为把部署的架构具体化一些是非常有用的,运营人员在此方面可以起到作用。你需要设计好目录结构。你要如何发布代码才能容易地回滚呢?你如何分别激活不同的代码?只要与运营人员进行一些沟通和商议,这些问题都有简单的答案。
举个简单的解决案例,假如工作在UNIX平台,你可以用目录名来解决发布版本问题,用一个符号连接指向当前的版本。如果某网上店铺的目前的版本是1.5.2,你可以用“store_1_5_2”来命名发布目录,然后用一个名为“store”的符号连接指向它。如果版本发展到了“1.6.0”, 你就应该将其发布到“store_1_6_0”目录,等待运营人员更新符号连接来切换应用服务器。建立诸如此类的构建机制应该不是一件非常困难的事情。
还有一件事情值得注意,那就是你需要支持很多运营部门已有的系统。比如他们很可能已经有一个监控方案,因此你必须让新系统能与之好好地配合。他们甚至可能已经采用配置管理数据库(CMDB)来控制版本和应用之间的依赖关系的情况。
总之,你对运营部门支持得越好,他们对你的系统支持得就越好。如果你想睡个囫囵觉的话,就好好做到这一点吧。
InfoQ: 开发产品级软件中倡导的预先工作的原则似乎与敏捷思想中的需要的时候再动手和必要时再重构的原则有些冲突,你是怎么看这个问题的?
Michael Nygard:作为一个敏捷开发人员,我也时常会面对这个矛盾。对此问题,目前还没有好的解决方法,但是要看到,这两个原则的目的是一致的,那就是设计出好的面向对象的产品。
当完成一段逻辑代码及其单元测试代码后——不管你先写的是哪个——为了改善质量,重构是免不了的。嗯,“改善”,你能告诉我“改善”的具体涵义吗?这是否意味着,对面向对象的设计你心中必须有一个好坏的标准?是的,而这个标准就是Martin Fowler《重构》一书中所说的“变味代码”。“变味代码”只是一个定性的衡量。这里硬要塞进一个严格的定量衡量标准是没有必要的。
我认为对于架构同样如此。对我来说,一个没有时间限制的远程调用,一个未加限制地取回客户所有订单的SOAP调用或者REST调用,都可以认为是“变味架构”。
因此,虽然不赞成预先做大的设计,或者说“大范围的预先架构”,但我认为在系统内部定义合理的边界,设计合理的异常处理机制并且排除“变味架构”是非常有必要的。
InfoQ: 你是否认为某些软件或者API(例如Hibernate Shards、Puppet)会有助于开发产品级别的软件?如果答案是否定的,那么你认为是否会出现那样的软件或API?也就是说开发产品级别的软件是一个架构问题,还是一个学习某软件的问题?
Michael Nygard:这是一个卖广告的好时机,不过我并没有这样的软件。我认为两方面的问题是正交的。无论框架开发人员,产品厂商的开发人员还是应用开发人员……我们都是搞开发的。在产品型软件的代码和框架代码中遇到的产品安全问题,在应用型软件的代码中也会同样遇到。因此,框架对编写产品级软件也就无所谓是否能提供帮助了。
好的框架的确能提高产品的稳定性,例如Doug Lea的线程类库就显著提高了Java的线程安全性和同步功能。但是,我们最终还是要从产品和框架本身上来获取信心。验证过的总比没有验证的好,开放的总比封闭的好,经过长期市场验证的各类产品比其他任何的说辞都来得实在。