基于Cucumber和Scala的验收测试

July 8th, 2014 No comments

Cucumber是一款富有盛名的 BDD(行为驱动开发)框架,特别是在 Ruby 社区,它有着很高的知名度,然而大家可能不了解的是,Cucumber 通过 cucumber-jvm 很好地支持了 jvm 平台上的各类语言,包括 Java、Clojure、Scala、Groovy 等等。我工作中的产品代码是 Java,直接用 Ruby 测试 Java 应用程序有很多不便(比如用调用 Java 客户端,使用 Java 工具类库),而用 Java/Scala 等语言测试就比较方便,实际中我们使用的是 Scala,主要是它更简洁,这也是一个我们锻炼学习新兴语言的机会。本文介绍如何搭建一套基于 Cucumber 和 Scala 的测试环境。

首先,基本的 POM 依赖如下(这里使用 Maven,如果你用 Gradle 或者 sbt,只能自行搜索方案了):

上述三个依赖分别是 cucumber-jvm 的核心类库、scala支持库、以及junit集成库,集成junit主要是方便IDE和Maven通过JUnit运行Cucumber。除此之外,你当然还需要Scala依赖,包括 scala-compiler、scala-library和我们后面会用到的 scalatest:

为了让Maven能够编译Scala,还需要配置 maven-scala-plugin:

基本的项目配置就绪,接下来编写我的 cucumber feature 文件,sample.feature,该文件位于项目 src/test/resources/ 下的 features/ 子目录下,为了方便组织 feature 文件,我一般把所有 feature 文件都放在这里,由于如何写 feature 不是本文重点,例子就很简单了:

该 feature 对应了两个 StepDef,我用 Scala 编写,位于 src/test/scala/目录,具体类为 com.juvenxu.cucumber.scala.steps.SampleStepDefs

* 该例中 ScalaDsl 定义了 Given, When, Then 这些 DSL,When 和 Then 的参数是一个用来匹配 Step 的正则表达式,Scala 的三引号用起来更方便,再也不用给 String 中的引号转义了。
* trait EN 定义了 DSL 的英文表达,如果你想写“假设”、“当”、“那么”等中文,可以用 trait ZH_CN。
* ShouldMatchers 是 scalatest 的东西,代码中的 result should be(arg0) 就是基于 scalatest 的断言,对比 Java 的测试框架,表述力更强大。

最后我们还需要一点简单的代码来驱动 Cucumber,它也用 Scala 编写,位于 src/test/scala/目录,具体类为 com.juvenxu.cucumber.scala.RunCukesTest

* @RunWith 就是告诉 JUnit 用我的 Cucumber 这个 Runner 来跑测试。
* @Cucumber.Options 中的 format 指定生成的测试报告,后续可以 target/ 目录下找到。
* @Cucumber.Options 中的 features 指定 features 文件的路径,该例中是 classpath 下的 features 目录下。

至此,整个项目的目录结构是这样的:

在命令行运行 mvn clean install 能看到如下输出:

关于和 IDE 的集成 IntelliJ IDEA 做得很棒,只需要安装一些插件就可以了,需要安装的插件包括:Scala,Cucumber for Java,以及 Cucumber for Scala,效果如图:

cukes_features

cukes_feature_sample

实际工作中使用 Cucumber 的体会,主要还是它会迫使我们把测试写清楚,用自然语言地方式描述测试,就必然要求你把测试想得比较清楚,对比我们在其他代码库中随处可见的各种莫名其妙的 TestCase,Cucumber 测试的可维护性高很多,通过测试就能快速了解一个系统的各类功能。技术上来说,它并不比纯的 JUnit TestCase 高明多少。

最后,我和江平兄一起翻译的《Cucumber:行为驱动开发指南》是本不错的学习 Cucumber 的参考书籍,虽然书中的代码都是 Ruby 的,但思路完全是一致的,特此推荐。

cucumber_book


原创文章,转载请注明出处, 本文地址: http://www.juvenxu.com/2014/07/08/acceptance-test-with-cucumber-and-scala/

Categories: 测试 Tags: , , , ,

ScrumGathering上海2014

June 8th, 2014 4 comments

6月5-7日是一年一度的、以敏捷为主题的ScrumGathering大会,今年我作为讲师身份参加,除了能够免费参会这一好处之外,还可以总结下自己最近一年的思考,另外还能见到不少许久未见的朋友。然而令人意外的是,这次参会得以有机会和自己一直很崇敬的Dave Thomas详谈,这远远超出了我对大会的期望。

如果你参加过以敏捷为主题的大会,你就会发现很多热度很高的词:Agile、转型、Scrum、CSM、Lean、TDD…… 这些名字都是领域的专家发明的,专家们信誓旦旦地告诉你,他们能解决你面临的问题。但是不知道大家想过没有,在软件开发这个行业,解决问题最终基本都会落实到写代码上,然后在大会上,能看到代码的地方聊聊无几,这是不是有点问题?

第一眼看到 Dave 的时候,他腿上放着个Macbook Air在休息区和朋友申健在聊天,一开始没留意,后来有人告诉我这是 Dave,我略吃惊,忙求引荐。Say Hi 之后立刻说我看过 The Pragmatic Programmer,喜欢 Pragmatic BookShelf …… 一堆真心的“奉承”话,我的确曾经有一段时间把 The Pragmatic Programmer 一书放床头,每晚翻翻,细细体会,这书也是我看过的最好的IT书籍之一。客气完毕之后,我问了他对 Reactive思想和 Akka 的看法,Dave肯定了我的想法,认为这是编程模型发展的方向(虽然他也说了,这玩意儿几十年前就有了),然后他就开始high了,开始给我推荐 Elixir,我说这语言我没听说过啊,哦,记得好像有本书,当时以为是一个什么类库呢。Dave于是就打开Mac给我写Elixir代码,尼玛,写代码!在一个以敏捷为主题的大会上,一墙之隔的会场内,有讲师在讲酷炫的名词呢。Dave顺手给我写一段Elixir代码,类似于Java 8 Stream API的东西,基于消息传递,看起来非常简洁,很像Bash。我们还聊了Pragmatic Bookshelf,说起《七周七语言》,说起《番茄工作法图解》,Dave还透露了他们计划出版的《more seven languages in seven weeks》,里面会介绍 lua,elixir,和其他另外5种我都没听说过的语言。

Juven_and_dave

大会上我的话题是”务实地拥抱极限编程”,大意摘要如下:

1. 极限编程作为一个比较流行的敏捷方法,大家都比较熟悉了,尤其是TDD、持续集成、结对编程、重构能具体实践。
2. 但是大家是否忘记了极限编程的价值(Values)?
3. 极限编程推崇的价值是 Feedback, Communication, Simplicity, Courage, Respect
4. 极限编程实践都有其对应的价值,例如TDD就对应了Feedback
5. 我们不应该只盯着实践,我们应该思考价值,并总结能实实在在解决自己问题的实践
6. 演讲用实际的例子介绍我们怎么遵循价值、总结实践处理具体问题。


(slideshare被墙,点此下载pdf)

敏捷要解决实际问题,现有实践能解决问题那当然就直接用,现有实践搞不定的时候,我们需要寻找其他办法,寻找其他办法的过程中,也要有指导,解决完问题了,也得有总结,这个时候,极限编程的Values就是很高的指导及方向。

有幸我把自己的topic提前和Dave分享并得到了他的肯定,并且后续他也走进会场听了我的中文演讲,据我观察这是他在整个大会中听的唯一的一场演讲,这给我受宠若惊的感觉。Dave的演讲是《敏捷已死,但敏捷精神长存》,核心思想还是围绕了Pragmatic这个词,他的意思说,大家别听一像火鸡一般自以为是的专家兜售玄乎的方法,大家要自己实践、自己思考、自己总结,这才是敏捷精神,如果大家只做专家们所兜售的敏捷,那敏捷就死了。另外他介绍自己的时候,说自己是个Programmer,而且到现在还是每天在写代码。Dave是敏捷宣言的作者之一,反观我们周围,那么多说敏捷的人,多久没写过一行代码了?

我认真反思了一下自己,很多时候我宁愿看书而不愿拿电脑敲一个DEMO,觉得弄明白理论后,代码自然写起来很简单,然后实际会发现写代码很慢,很吃力,看似简单的理论,看似理解了,写代码的时候还是磕磕绊绊。另外,随着工作年限的增加,很多时候解决问题更多是协调沟通而不是写代码,这样练习的机会就渐渐变少,看来需要刻意弥补。

有时候我会问自己,为什么做程序员,很大一个原因就是一开始编程所感受到那种神奇,而现在,有多久没那种感受了?生活虽然不易,还是不能忘了初心。


原创文章,转载请注明出处, 本文地址: http://www.juvenxu.com/2014/06/08/scrumgathering-shanghai-2014/

Categories: 敏捷 Tags: , ,

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

April 22nd, 2014 1 comment

一个成功的软件,全力开发的时间可能占其整个生命周期的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: ,