不做运维,不懂日志——日志编码

April 22nd, 2014 No comments

一个成功的软件,全力开发的时间可能占其整个生命周期的1/4还不到,软件发布后要运维(Operation),运维的视角和开发的视角是很不一样的,但是有一点,运维的数据能反哺开发,同时,开发的时候也得考虑可运维性,其中非常重要的一点是日志,没有日志,运维就瞎了大半。怎么写日志,就得从运维的需求来看了,通常会有以下一些常见的场景(已典型互联网应用为例):

1. 访问来源,包括访问量,访问者数据,如用户名、IP等等。
2. 基于上一点细化,访问的接口,读、写、删……
3. 软件系统内部的核心链路,比如我这有个系统要在中美直接同步文件,那同步的情况运维的时候就要掌握。
4. 软件系统对其他所依赖系统的访问情况,比如我这个系统依赖一个分布式缓存,那访问缓存的量、是否超时等情况需要了解。
5. 系统异常,比如磁盘满了。

记录这些信息的目的大抵有:帮助分析系统容量方便扩容;在系统某些部分工作不正常的时候及早发现;发生严重故障后方面定位问题原因。认识到这些需求后,下一步就是怎么实现的问题了。

前面提到的5点,有些可以通过抛异常实现,例如访问分布式缓存超时,有些则显然不是异常,例如就是正常的缓存访问。我觉得可以用一种统一、规范的方式记录,这种方法就是打码。我记得以前用Windows 98/2000的时候,经常会遇到蓝屏,蓝屏上会有一堆我看不懂的英文,并且总是伴随着一个错误码。

bluescreen

虽然我看这玩意儿没一点好心情,但我相信微软的工程师肯定能从那个奇怪的状态码上判断出是哪里出了问题,硬盘坏道?光驱卡死?诸如此类……其实类似的做法数据库也用,比如MySQL

用统一的代码表示错误(也可以表示正常但核心的业务点)最大的好处就是便于搜索、统计和分析,在动辄数以万行记的日志文件中寻找感兴趣的信息,一页一页翻看是不现实的,稍微做过点运维的必然会用上 grep,awk,wc 等工具,这个时候如果信息都有代码标识,那真是再方便不过了!例如,我用代码 FS_DOWN_200 表示对系统的正常下载访问,日志是写在 monitor.log 文件中的,我就可以使用一行shell统计4月22号5点到6点之间的正常访问量:

具体每条日志记录什么,那就是更详细的了,基本就是时间、日志编码、额外的有用信息,如:

除了时间和日志编码外,还有响应时间(216ms)和具体访问的文件名。

当然如果你有日志监控和分析系统就更棒了!你就可以在系统中录入关键字监控,比如每分钟统计次数,然后看一天、一周的访问量趋势图。进一步的,如果这个量发生异常,让系统发出报警。如果没有关键字,从海量日志中分析纷繁复杂形态各异的信息,再监控,是非常难的一件事情。

为什么要把日志代码设计成 FS_DOWN_200 这样子的,下面稍微解释下,这个代码分成三段:
1. FS: 表示我们的系统,这是最高的级别,公司中有很多系统,那各自定义自己的标识。
2. DOWN: 表示我们系统中的一个核心业务点或者对其他依赖系统的访问,还可以是UP(上传),SYNC(同步),或者TAIR(对缓存系统访问)。
3. 200: 具体健康码,参考HTTP规范,200表示OK,其他包括404(不存在),504(超时)等等。

有了这些代码,再结合公司的监控系统,我们做统计分析就非常方便了,每天多少下载、多少上传、多少成功、多少失败、对其他依赖系统访问多少量、多少失败率,一目了然。进一步的加上监控,当某些值突然发生变化,比如下载量/上传量暴跌、访问其他系统依赖超时大量增多,就能及时响应。

日志对于运维实在太重要了,而如果不接触运维,又怎能理解其真正的需求,因此我说,不做运维,不懂日志

本实践受 @linux_chinaRelease It! 17.4 小节启发,表示感谢。


原创文章,转载请注明出处, 本文地址: http://www.juvenxu.com/2014/04/22/log-with-code/

Categories: DevOps Tags: ,

使用Hystrix提高系统可用性

March 19th, 2014 No comments

今天稍微复杂点的互联网应用,服务端基本都是分布式的,大量的服务支撑起整个系统,服务之间也难免有大量的依赖关系,依赖都是通过网络连接起来。

hystrix-healthy

(图片来源:https://github.com/Netflix/Hystrix/wiki)

然而任何一个服务的可用性都不是 100% 的,网络亦是脆弱的。当我依赖的某个服务不可用的时候,我自身是否会被拖死?当网络不稳定的时候,我自身是否会被拖死?这些在单机环境下不太需要考虑的问题,在分布式环境下就不得不考虑了。假设我有5个依赖的服务,他们的可用性都是99.95%,即一年不可用时间约为4个多小时,那么是否意味着我的可用性最多就是 99.95% 的5次方,99.75%(近乎一天),再加上网络不稳定因素、依赖服务可能更多,可用性会更低。考虑到所依赖的服务必定会在某些时间不可用,考虑到网络必定会不稳定,我们应该怎么设计自身服务?即,怎么为出错设计?

Michael T. Nygard 在在精彩的《Release It!》一书中总结了很多提高系统可用性的模式,其中我认为非常重要的两条是:

  1. 使用超时
  2. 使用断路器

第一条,通过网络调用外部依赖服务的时候,都必须应该设置超时。在健康的情况下,一般局域往的一次远程调用在几十毫秒内就返回了,但是当网络拥堵的时候,或者所依赖服务不可用的时候,这个时间可能是好多秒,或者压根就僵死了。通常情况下,一次远程调用对应了一个线程或者进程,如果响应太慢,或者僵死了,那一个进程/线程,就被拖死,短时间内得不到释放,而进程/线程都对应了系统资源,这就等于说我自身服务资源会被耗尽,导致自身服务不可用。假设我的服务依赖于很多服务,其中一个非核心的依赖如果不可用,而且没有超时机制,那么这个非核心依赖就能拖死我的服务,尽管理论上即使没有它我在大部分情况还能健康运转的。

断路器其实我们大家都不陌生(你会换保险丝么?),如果你家没有断路器,当电流过载,或者短路的时候,电路不断开,电线就会升温,造成火灾,烧掉房子。有了断路器之后,电流过载的时候,保险丝就会首先烧掉,断开电路,不至于引起更大的灾难(只不过这个时候你得换保险丝)。

cb

当我们的服务访问某项依赖有大量超时的时候,再让新的请求去访问已经没有太大意义,那只会无谓的消耗现有资源。即使你已经设置超时1秒了,那明知依赖不可用的情况下再让更多的请求,比如100个,去访问这个依赖,也会导致100个线程1秒的资源浪费。这个时候,断路器就能帮助我们避免这种资源浪费,在自身服务和依赖之间放一个断路器,实时统计访问的状态,当访问超时或者失败达到某个阈值的时候(如50%请求超时,或者连续20次请失败),就打开断路器,那么后续的请求就直接返回失败,不至于浪费资源。断路器再根据一个时间间隔(如5分钟)尝试关闭断路器(或者更换保险丝),看依赖是否恢复服务了。

超时机制和断路器能够很好的保护我们的服务,不受依赖服务不可用的影响太大。然而具体实现这两个模式还是有一定的复杂度的,所幸 Netflix 开源的 Hystrix框架 帮我们大大简化了超时机制和断路器的实现。

hystrix-logo

先上POM依赖:

使用Hystrix,需要通过Command封装对远程依赖的调用:

然后在需要的时候调用这个Command:

上述是同步调用,当然如果业务逻辑允许且更追求性能,或许可以选择异步调用:

该例中,不论 dependencyService.call() 自身有没有超时机制(可能你会发现很多远程调用接口自身并没有给你提供超时机制),用 HystrixCommand 封装过后,超时是强制的,默认超时时间是1秒,当然你可以根据需要自己在构造函数中调节 Command 的超时时间,例如说2秒:

当Hystrix执行命令超时后,它会抛出如下的异常:

注意异常信息中包含“MyCommand timed-out and no fallback available.”,也就是说 Hystrix 执行命令超时或者失败之后,是会尝试去调用一个 fallback 的,这个 fallback 即一个备用方案,要为 HystrixCommand 提供 fallback,只要重写 protected String getFallback() 方法即可。

一般情况下,Hystrix 会为 Command 分配专门的线程池,池中的线程数量是固定的,这也是一个保护机制,假设你依赖很多个服务,你不希望对其中一个服务的调用消耗过多的线程以致于其他服务都没线程调用了。默认这个线程池的大小是10,即并发执行的命令最多只能有是个了,超过这个数量的调用就得排队,如果队伍太长了(默认超过5),Hystrix就立刻走 fallback 或者抛异常。

根据你的具体需要,你可能会想要调整某个Command的线程池大小,例如你对某个依赖的调用平均响应时间为200ms,而峰值的QPS是200,那么这个并发至少就是 0.2 x 200 = 40 (Little’s Law),考虑到一定的宽松度,这个线程池的大小设置为60可能比较合适:

说了这么多,还没提到Hystrix的断路器,其实对于使用者来说,断路器机制默认是启用的,但是编程接口默认几乎不需要关心这个,机制和前面讲的也差不多,Hystrix会统计命令调用,看其中失败的比例,默认当超过50%失败后,开启断路器,那之后一段时间的命令调用直接返回失败(或者走fallback),5秒之后,Hystrix再尝试关闭断路器,看看请求是否能正常响应。下面的几行Hystrix源码展示了它如何统计失败率的:

其中 failure 表示命令本身发生错误、success 自然不必说,timeout 是超时、threadPoolRejected 表示当线程池满后拒绝的命令调用、shortCircuited 表示断路器打开后拒绝的命令调用,semaphoreRejected 使用信号量机制(而不是线程池)拒绝的命令调用。

本文并不打算完整地介绍 Hystrix,这里只是介绍了为什么要用 Hystrix 以及使用它需要关心的一些基本核心概念,Hystrix 是 Netflix 的核心中间件,在保证他们系统可用性上起到了非常核心的作用,它还有更多的功能都在文档完整地介绍了:https://github.com/Netflix/Hystrix/wiki,其中最重要的而且本文没有介绍的,可能就是监控功能了,当系统足够复杂,相互依赖错综发杂的时候,快速定位到故障点,是运维非常关心的问题。


原创文章,转载请注明出处, 本文地址: http://www.juvenxu.com/2014/03/19/improve-system-availability-with-hystrix/

Categories: 架构与性能 Tags: ,

记一次关于TIME_WAIT TCP连接数的性能优化

January 19th, 2014 8 comments

一天下午3点多,我们的OPS打电话给我(你懂的,OPS打电话过来不会是请你喝咖啡),我连忙接起电话,电话那头的声音很急迫:“content集群有报警,CPU负载已经到了50多,你快过来一起看看”,我挂了电话连忙赶过去,OPS坐在另一幢楼的同一层,好在两幢楼是相连的,过去很方便。边走我心里边嘀咕,content集群实际上是一个反向代理,上面配置了上百条apache http url rewrite 规则,每条规则的作用就是把一个url路径转换成另外一个url路径,发送到某个后端的集群上,然后把返回结果转出去,几乎没有计算量,不应该有高负载啊。 

来到OPS座位上,他已经在往content集群里加机器了,这个集群为网站提供图片读取服务,负载这么高,服务肯定不稳定了,因此当务之急肯定是让服务恢复稳定。对任何线上故障来说,恢复服务肯定是第一优先级,问题排查的优先级排在后面。大概5分钟之后,新加的20%机器开始对外服务了,幸运的是,效果立竿见影,集群负载立刻降到了5以下,我输入了几条url测试了下,服务已经恢复稳定了,我俩都松了口气,这次故障没有造成长时间大范围的影响。

接下来我们开始分析造成CPU高负载的原因,这个集群是有监控的,报警也是监控系统发出的,于是我们一个个看监控项:CPU、内存、磁盘消耗、网卡流量、QPS(每秒处理请求数)、响应时间、TCP连接状态等等……然后我们发现了几个问题:
  1. QPS和响应时间没有监控,妈的,这意味着我们无法快速得到访问量和服务稳定性的直接数据。
  2. 从网卡流量我们发现,下午3点左右是访问高峰期,这很可能是诱发CPU负载过高的外因。
  3. TCP连接数中,TIME_WAIT连接数异常地高,扩容前最高到了30k多!扩容后降到了27k以下。

关于第一条,我给自己记了个任务,待当前的问题处理完毕之后,花点时间配合OPS把这个监控完善起来。

第三条关于TIME_WAIT的数据非常可疑,可惜的我我大学学习的那些网络知识早还给老师了,于是只能现补了,开Google查资料,花了点时间了解了TCP连接的TIME_WAIT是怎么回事。TCP连接建立起来传输数据,这个时候对两边来说,连接的状态是ESTABLISHED,数据传输完毕后,连接的其中一方会主动发起断开连接,断开连接的整个过程是四次握手,握手完毕后,断开连接发起方会进入TIME_WAIT的状态。默认情况下,TIME_WAIT的连接是不会被操作系统回收的,只有到了CLOSED状态后,操作系统才会回收,而且默认情况TIME_WAIT连接会持续两个MSL时间,然后通过如下命令我发现这个时间是60s:

因此如果每一个请求都新建一个连接,服务完之后断开连接,一分钟内就会积累大量的TIME_WAIT连接。监控数据显示,单台机器在比较高峰时刻ESTABLISHED数为1.5k的时候,TIME_WAIT连接数接近30k。正是当TCP连接数超过这30k的时候,系统变得不稳定,CPU负载变得很高,为什么会连接数超过30k会使得系统不稳定,我还不清楚,但我需要让TIME_WAIT连接数降下来。
 
第一反应的解决办法是使用TCP的keep-alive模式,让出问题的content集群在访问后端服务器的时候,不要每次都新建连接并断开连接,而是采用keep-alive长连接模式,content集群和后端服务器集群都是内部机器,IP相对固定,长连接完全合情合理,重用连接不仅能够节省建立连接和断开连接的消耗,而且必然能大大降低TIME_WAIT连接的数量。我先看了后端服务器,都是支持keep-alive的,现在就看content怎么主动开启keep-alive请求了。
翻开配置看了下,发现content集群做反响代理是用了apache的mod_rewrite模块,具体配置大概是这样的:

意思就是说把符合前面正则表达式的请求,转化url之后发到back-end-server,而由于历史原因,这样的配置有100条左右。那怎么配置mod_rewrite模块让它支持keep-alive呢?我捜了半天的Google,把mod_rewrite模块的文档前前后后翻了好几遍,得到的结论是:mod_rewrite不支持keep-alive。

这时候该怎么办?综合各方面意见,有三种比较直接的解决方案:
  1. 啥都不干,直接加机器让平均每台的TIME_WAIT达不到30k,简单粗暴,但显然会造成很大的机器浪费。
  2. 用Nginx替换Apache,很多人都这么和我推荐,但是,抛开Nginx是否合适这个场景不说(我还不太了解),光迁移这100条正则表达式转发规则就要命,更何况还有一大堆其他配置,成本和风险都很高的。
  3. 让操作系统主动回收TIME_WAIT连接,可以通过配置系统的tcp_tw_recycle和tcp_tw_reuse来实现,网上有一大堆这方面的资料。这么做也有缺点,运维成本增加了,每台机器都得去做这个特殊配置,重启后还会丢失,会比较麻烦,我总觉得这是治标不治本的方法。
抓破脑门想不到更好的办法,我就在扩容20%机器的基础上,再调节了tcp_tw_reuse,我没有调节tcp_tw_recycle是因为查阅文档发现当后端有负载均衡的时候,开启它有潜在的风险,此事就此告一段落,然后我始终放不下。
 
一个月之后,由于同事遇到了类似的问题,激起了我想再次优化TIME_WAIT连接数的欲望。又花了半天看了很多文档及邮件列表之后,我想,作为一个完全合理的需求,为什么mod_rewrite不支持keep-alive呢?而与之功能类似(但不支持复杂正则表达式匹配)的mod_proxy模块,是能很好的支持keep-alive的。从apache的用户邮件列表的一篇帖子中我发现,keep-alive功能是被有意地从2.2版本开始移除的,而且apache的维护者Igor Galić还说了这么一句:“你为什么要用mod_rewrite做代理?”,莫非我们之前的用法都是错误的?忽然间,思路有了,mod_rewrite的强项是url重写,mod_proxy的强项是代理,那就把代理交给mod_proxy,mod_rewrite只管url域名后面相对路径的重写,不就可以了吗?
 
尝试了一下,得到下面的配置:

对比之前的配置,现在的RewriteRule用了PT模式而不是P模式,P模式是强制代理网络连接,而PT则只是做路径转换,另外代理后的路径以/fs1开头。在此基础上,这里又配置了ProxyPass(mod_proxy),所有/fs1路径开头的请求,转到back-end-server上,并开启keep-alive。拿几条规则做了测试之后,发现TIME_WAIT数有一些下降,于是我在集群中找了一台机器,调整了几条访问量比较大的规则,放线上观察。一天之后,没有收到任何问题,我就把全线的配置都更新了,效果非常明显,之前ESTABLISHED连接1.5k的时候,TIME_WAIT连接数近30k,现在由于keep-alive的开启,TIME_WAIT连接数最多只到6k左右,降到了原来的1/5,也几乎不再可能成为系统的瓶颈。之后我们在类似的场景中发现,如此优化能降低系统响应时间,这也是好理解的,因此不用重复建立连接了,响应自然更快。
 
至此,短时间内我们基本不用考虑content集群的扩容了,因为这是集群的主要作用是反向代理,之前遇到了网络瓶颈现在得以解决,而其他系统资源如CPU、内存之类都还是比较富足的。考虑到网站发展比较迅猛,流量每个月都在上涨,而这个集群的规模比较大,因此这次优化实际上节省了不小数量的机器,那可是白花花的银子啊。
 
这是我第一次在机器量级比较大的系统上做性能优化,有几点可能已经被前人说烂了的总结:
  1. 系统出问题的时候,第一优先级的任务是恢复系统服务。
  2. 系统监控必须要完善,否则做优化的时候简直是抓瞎。
  3. 找到瓶颈,优化才有意义,否则基本是浪费精力。
  4. 计算机系统的基本功还是必须的,该例中就需要对TCP协议有了基本的理解。
  5. 虽然计算机越来越便宜,但是在该例的分布式的服务场景中,一堆便是数十台高性能的物理机,加上运维成本,加上性能差给用户体验带来的损害,其实性能差的代价非常昂贵。因此,设计系统、编写代码的时候,还是要想着节省计算资源。
Categories: 架构与性能 Tags: ,

2013年我读过的书

December 27th, 2013 1 comment

Reading

2013年我完整读过的书有13本,当然除此之外也翻开了很多其他的书,但基本都没有读到1/3,因此算下来其实阅读量不多,也就平均每个月1本的样子,阅读量不多的原因无非三个:第一是懒,刷微博打游戏看电影都要时间;第二是专注不够,很多书翻开了就没下文;第三是身体原因,前三个月基本没看书。

在看书之前我会花不少工作做挑选的,因此书的总体质量不会太差,能坚持读完的,都是我觉得很不错的书,以下分技术和非技术两类,做个介绍,顺带回顾。

非技术类

自私的基因

这是一本讲进化论的科普书籍,文笔非常生动,有很多实际的动物的例子,大部分可以推导到人身上来。我们身边各种或高尚、或自私、或冲动、或无奈等行为,都能在进化论中得到实在地解释。这本对我最大的意义是让我能更理性地看待自己的想法和行为,古人说日三省吾身,进化论为自省提供了一个可靠的理论支撑,而这本书绝对帮助我更深刻地理解了进化论。

众病之王——癌症传

读了《自私的基因》后再读这本书,理解会更深入一些。这本普利策文学奖得主告诉我们,癌症本质上就是一种及其强大的生命。书中记述了千百年来无数杰出的灵魂与癌症斗争的历史,坚毅、希望、傲慢、绝望、震惊、无奈、压抑、热情……在进化论面前,情感显得苍白无力……在这个人人谈癌色变的时代,各种与之相关的信息扑面而来,而其中大部分都是不科学的,翻开这本书,去了解这个人类有史以来最大的敌人吧。

哲学的慰籍

我们每个人都会面对挫折,贫穷、对世俗不满、对自己身体不满、对情感的不满……我们想追求内心的快乐和安宁,却苦于不得门道、毫无章法,那么不妨看看先贤哲人是怎么想怎么做的,苏格拉底、伊壁鸠鲁这些名字你可能熟悉可能陌生,但这不重要,难的是这本散文集完全没有哲学通常那种扑面而来的晦涩难懂,阿兰·德波顿的文字简单还不乏幽默,资中筠的翻译功底也着实了得,花一杯咖啡的钱,找两个或阳光或阴霾的周末,暂时原理匆忙,和哲人对话,和自己对话。

宽容

你还记得读书时学的那篇“宽容序言”吗?房龙在这本书里讲述的是西方天主教/基督教的历史,整段历史大部分时间其实并不怎么光彩,杀戮、迫害、禁言,都是家常便饭,房龙认为一切都来源于不宽容,而不宽容的原因有三:无知、懒惰、自私。因为无知,你可能会觉得那穿着怪异的人不是善类;因为懒惰,你不花精力去了解事实就妄下判断;因为自私,你滥用权利让别人闭嘴甚至永远闭眼。这也是一本帮助自省的书。

老人与海

如果说阿兰·德波顿的文字简洁,那可以夸张的说海明威的文字几乎没有形容词了。这是一个讲述老人与大鱼战斗的故事,尽管常识告诉这个老人他已经不再年轻,大鱼向他证明自己的力量,但他用自己的坚毅给了自己一个最好的证明。当今世界,我们每天忙忙碌碌,忙物质也好、忙子女也好、忙事业也好,是为了向社会证明自己,还是在想自我实现?不妨对着镜子看看自己的眼睛吧。

丧钟为谁而鸣

还是海明威,故事的主人公换成了西班牙内战时期的一名士兵,他沉着坚毅,但终究没有活下来。书中混合着大量对政治、使命、生活、爱情的思考,让我觉得战争真不是那么说得清楚的事情,在那个环境中,你为了爱人、为了孩子、为了朋友能够很好的生活下去,你杀人,你被杀,你没路可退……其实你枪口下的敌人,和你并没有太多的两样。但是,软弱的人,终究什么都实现不了。

悟空传

中学时读过,印象中只有三三两两的几句了,比如八戒说“(师傅)死了好,大家分行李”,当时是戏谑的心态,现在读来,笑还是笑了,但更多的是感动、是悲哀。你开四个轮子的汽车了、你坐上高铁了、你坐飞机去国外旅游了,但总有一个圈圈划在那里,你碰不得,一碰就头破血流。但终究有人就算头破血流也要走出去,这个人可能是你,可能是我,可能是被我们所嘲笑的某某某。谈自由的时候,猪也许在笑你。

Drive

这本书的中文名是《驱动力》,是分析我们每个人工作生活的内在动力的。为什么流水线工人要打卡,要加班付薪资?为什么程序员最好是灵活工作时间?管理团队的时候,怎么奖励?怎么惩罚?怎么安排工作?给多少自由度合适?等等问题,书中都有解释。书中总结了三个内在驱动力源泉,自制、精通、及人生目的。还是那句话,帮助自省。

One Billion Customers

这本书对我来说最大的价值让我明白了市场的力量,它足以撬动一个大国的政治。作者是个中国通,讲述了改革开放之后中国主流行业如金融、电信、媒体的变化,以及变化背后发生的重要人物以及相关的事情细节,这绝对能帮助我们更好地了解中国。这书在国内无法出版,有兴趣的可以网上找翻译版本。

技术类

技术类读完的书少,但其实翻了不少,比如 Netty,ZooKeeper, NoSQL, Scala, Clojure, Sonar, Performance 等等,以下是读完的几本。

Release it!

这是我2013年读过的最好的技术书,它根本上会影响我之后写代码的习惯,几个月前一次线上故障排除也是直接受本书模式启发。这是一本讲软件非功能需求的书,包括稳定性、可用性、可扩展性、可运维性等等,书中记述了很多真实的故事,例如网站可用性出问题的时候会造成多少损失,大家的压力多大,如何排查修复问题的。书中总结了不少模式和反模式,都是难得的经验之谈。真希望作者 Michael T. Nygard 能再写本精彩的书。

The Phoenix Project

我是陆陆续续当小说读完(其实就是小说),比较有意思的是能看到大型外企中高层的政治斗争,然后全书描述了主人公如何用神一样的精益方法挽救了死亡边缘的 Phoenix 项目,然而实际场景中未必会有这么神奇的功效,我体会更为深刻的反而是几位主人公的敬业精神,为了项目、为了公司,真可谓殚精竭虑,值得学习和敬佩。最后,书最后说了IT是业务的核心竞争力,事实也的确如此,越来越多的公司离不开IT,所以我们码农不必担心吃饭问题啦。最后,这书帮我更好地理解了精益思想。

Cucumber:行为驱动开发指南

这本书是我和好友江平一起翻译的,翻译是件苦差事,但也很诱人,因为翻译的过程中能学到不少东西。Cucumber是及棒的工具,理念也非常先进(可能超前了),我衷心希望有更多的人来阅读本书并在工作中实践,这样我翻译的工作也能得到更多的价值体现。

理解Unix进程

ScrumGathering上 @图灵社区 送的,回家途中飞机上读了大半,之后很快读完余下的部分,基本上算是复习了大学Unix进程有关内容,@iLRainyday 的翻译相当到位。全书只有115页,因此读起来很轻松,虽然代码是Ruby,但其实适用任何语言,Ruby的好处是阅读方便。附录用4个实际的例子把全书内容串联复习,很给力!对Java程序员来说,跳出单进程的思维模式,也许会有豁然开朗的感受吧,就我来说,工作这么多年再看Unix进程知识,感觉也更实在了,对了,你知道 kill -9 的 9 为什么是 9 吗?


原创文章,转载请注明出处, 本文地址: http://www.juvenxu.com/2013/12/27/my-reading-in-2013/

Categories: Tags: ,

敏捷的反馈原则与非功能需求

December 2nd, 2013 No comments

在敏捷软件开发理念中,我体会最深的一点是反馈。 结对编程的过程中,我敲下的每个字符都有可能得到同伴的反馈; 用测试驱动开发的方式写代码的时候,代码会反馈我说设计有问题或者实现错误; 当我的代码不小心破坏了同伴的测试用例,持续集成也会在数分钟内给我反馈; 当系统间的集成有问题的时候,自动化部署加上自动化验收测试也能在几十分钟内把问题反馈给我。 只要实践了上述方法,短则数秒钟、多则数十分钟,我都能得到真实地软件状态的反馈,从而我能够及时修复错误, 使得错误引入后所造成的损失能被压得非常之低。

上述讲的都是技术手段,非技术手段的反馈作用也是类似的。 例如每日的站立会议能帮助团队成本之间及时发现潜在的问题: ”什么?你打算修改线程池配置?你得当心,我之前测试过……“; 迭代的计划会议和回顾会议能让团队不要浪费时间在不必要的事情上,即消除浪费。 站立会议的反馈周期是天,迭代的反馈周期通常是一周或者两周。

反馈是如此重要,当反馈来得太晚,或者说在损失已经很严重才得到反馈的时候, 我会想这个时候敏捷能帮我做什么,或者说这个时候是不是敏捷方法已经无能为力了?

在一些访问量比较大的互联网应用场景中,对于很多非功能需求是比较难得到全面快速的反馈的。 例如用户登陆,对于这个功能本身,使用单元测试/持续集成/集成测试就能给我们即快又全的反馈。 然而,实际环境中用户数量可能从预期的100万变成实际的200万、用户的网络环境千差万别、 还有很多搜索引擎爬虫、可能还有恶意的攻击、服务器可能挂掉一台…… 诸如此类的因素会导致线下测试完全OK的服务到了线上可能会不稳定甚至挂掉。 我们称这一类需求为非功能需求,或者说是ility需求(security, scalability, reliability…), 虽然名为非功能需求,但是这类需求的重要性一点都不低,想像一下如果你登陆一个网站要花3分钟,你还会用它么? 然而不幸的是,要快速获取非功能需求的反馈,传统的敏捷方法似乎略显苍白无力。

“ility”测试

敏捷测试会强调所谓的“ility”测试,即专门测试非功能需求,模拟海量用户、模拟攻击,以尽早暴露非功能需求的问题。 这是一个很好的方法,切实实践益处良多。(想像一下,应用上线前一天你测试发现当同时在线用户数到1万的时候,网站会无法响应) 然而“ility”测试和难做到完美:

  1. 要模拟真实访问量,往往需要和实际生产环境一致的机器,这可能是几十台虚拟机,也可能是多个物理机集群, 实际工作中,由于成本考虑,实际工作中往往难以做到,因此造成测试的真实性大打折扣。
  2. “ility”测试的实施成本往往比一般的功能测试高很多,具体场景也更难预测,因此一般大家只会做主要场景的测试, 但是实际线上一些非典型性场景也会导致严重问题。
  3. 如果“ility”测试的实施时间比较晚,那么往往会发现,网站的性能问题是由架构决定的,而这个时候修改架构的成本已经很高, 这个调整不仅仅是代码结构层面、还包括运维层面的调整。

”ility“测试能提供很多反馈,然后很显然,完全靠它是不行的。那么实际中大家是如何保证网站平稳运行的呢?

经验与计算机理论

在实际工作中,我遇到有一些人,他们并不关心、也不了解敏捷开发方法,他们也不遵循一套严格的流程, 但是,他们能够很准确的判断系统哪里可能会遇到性能问题,并且能在架构初期就考虑到这些。 我们常常说,这是架构能力,那么架构能力是怎么来的?其实答案无外乎两点,一是经验、二是计算机理论。 他们大多都经历过很多类似的场景,因此即使说不出一二三所以然,感觉也能告诉他们风险可能在哪里, 当然仅仅经验还是不够的,伴随着经验的积累还需要计算机理论的补充和学习,包括计算机结构、操作系统、网络、JVM等等。

如果你的团队有这样的人在,那么恭喜你,你能在最早就避免掉很多非功能需求风险。

如果像我这样缺乏架构能力怎么办?答案很明显,抓住机会参与到具体工作场景中去,去理解整个系统是怎么运转的, 伴随地再学习相关的计算机理论,同时多阅读相关资料,理解业界大家遇到什么场景,具体怎么解决。

我们还能依靠什么?

你做了很完备的测试,经验和理论也告诉你一大堆坑已经避开了,但同时经验也告诉你,应用上线后总有无法预知的问题可能发生,怎么办?

我们不妨向运维去取取经。

作为开发,我曾经修改了应用之后和运维沟通希望他们做运维调整能让我的变更尽快上线,我们有一段对话是这样的:

开发:我的代码做了一些修改,我希望你能帮我调整集群A…再调整集群B…
运维:这么做太危险了,我觉得方案应该是 A‘… B’…. C‘… D’….
开发:这么做太复杂了,直接 A… 再 B… 简单得多
运维:你怎么保证上线后不出问题?
开发:我经过了完备的测试,理论验证,可以保证99%不出问题。
运维:换句话说,没人能保证上线后100%不出问题,对吧?
开发:这…
运维:如果万一上线真出问题了,怎么办?我是说万一。
开发:厄————
运维:我的方案虽然实施起来更复杂,但是能保证如果上线出了问题,系统能快速回滚到之前状态。

对于运维来说,他不知道应用程序内部是怎么运转的,他必须随时预期应用程序会出严重问题, 这时候,回滚通常是最快速,最有效的恢复服务的手段。

另外一种有效避免风险的方法是Staging环境测试,Staging环境是一个与生产环境一致的环境, 唯一的区别是没有实际用户访问,把代码发布到这个环境之后,可以自己模拟用户访问,从而尽早发现由环境引入的问题。 由于Staging环境和生产环境一致,必然会有更多的控制及相关流程,导致基于此环境的测试成本比较高, 因此不宜在这个环境上运行过多测试,而应该以测试环境为重点。

不论测试多完备、流程多完善,实在是有那么些问题是我们预想不到的,这个时候就需要上让实际的用户去用从而发现问题。 金丝雀部署正是这样一种做法, 具体做法是在生产环境中部署两个版本的代码,通过负载均衡让小部分用户先访问较新版本的代码,其余大部分用户访问较稳定的老版本代码。 这样即使新版本代码出了问题,也能让保持影响面在比较小的范围。有了一定比例的真实用户访问,非功能性需求的反馈就会非常全面, 而且把用户切回旧版本的成本也非常的低。

小结

敏捷软件开发理念中的反馈原则真是再怎么强调也不为过,然而正如你看到的,面对很多软件的非功能需求,传统的敏捷实践方法能量有限, 这个时候我们就需要在现有方法的基础上去思考和总结新的方法。业界在DevOps这方面积累了不少经验,值得我们关注和学习。

Categories: 敏捷 Tags: , , ,

持续交付离我们有多远

November 18th, 2013 No comments

上周我在2013苏州软件开发者大会发表题为‘持续交付离我们有多远’演讲,主要介绍了我实际工作中的一个案例,并辅以归纳思考和总结,以下是Slides。


原创文章,转载请注明出处, 本文地址: http://www.juvenxu.com/2013/11/18/how-far-is-continuous-delivery-away-from-us/