eBay Architecture(10)–Remember Everything Fails[Overview]

Remember Everything Fails,这句话我想不单单适用于IT系统,以前学C++的Exception,或者Error Handling的时候,我记住了一句话,每一条代码都有可能出错,做一个系统,如果没有Error Handling,那就不是一个良好的系统。当初我和焦祯老师一起设计LabVIEW to C Instrument Driver Converter的Error Handling的时候就费了好大心思。好,回到eBay的系统中来,我们知道不但软件会出错,硬件也靠不住,你不能指望说不发生磁盘坏掉之类的事情,而且硬件这东西,到了高端以后,稳定性提高1个百分点,价钱有可能要提高好多。我们的目标就是Use Unreliable Components to Build a Reliable System!

那如何做到这点呢,这里先把刚要摘录一下:

• Build all systems to be tolerant of failure
– Assume every operation will fail and every resource will be unavailable
– Detect failure as rapidly as possible
– Recover from failure as rapidly as possible
– Do as much as possible during failure
• Motivation
– Availability
• Failure Patterns
Failure Detection
Rollback
Graceful Degradation

要做到tolerant of failure,在Application Lier要做很多事情。所以eBay的应用层代码是很复杂的。但是在这里,我们着重要讲的是3个Failure Patterns。接下来Randy为每一种Pattern都给出了具体的例子。

 

 

Everything Fails[Central Application Logging]

在大型多线程并发的程序中,logging是非常重要的,从Developer的角度看,我个人觉得什么东西都没有logging来的有用。这里要谈的是:

需要log些什么东西,以及怎么利用这些logging。

Pattern: Failure Detection

Randy在这里介绍的是一个利用logging进行Failure Detection的例子。

  • Application servers log all requests
    1. Detailed logging of all application activity, particularly database and other external resources.
    2. Log request, application-generated information, and exceptions.

Log几乎记录所有发生的事情,特别是数据库操作,对于Search的app server,特别要记录发往搜索引擎的Query。所有资源的require和release都要记录,注意在记录中必须包括,发生的时间,发生的内容,发生的错误。我们还可以加入错误级别…当系统的复杂程度达到一定程度后,时间点非常重要,因为有些问题只在一定的时间内发生,我们如果把那段时间内涉及的所有的组件上出现的错误联系起来看,就比较能够在全局上对这个错误认识更加深刻。

但是到这一步,还谈不上Detection,只能说是当我们手动去进行trouble shooting的时候,我们可以查看‘犯罪’现场到底发生了些什么了。下一步就是要建立monitor机制,能够自动发现甚至预报错误。

  • Messages broadcast on multicast message bus

这点是比较特别的,logging信息会广播出去,写到这里,估计大家也就知道大概的做法了,既然广播,那一定在别的地方部署有listener来监听这些logging,并且做相应的检查,一旦发现问题,马上报警。

  • Listeners automate failure detection and notification
    1. Real-time application state monitoring: exceptions and operational alerts.
    2. Historical reports by application server pool,URL, database, etc.

这里还提到一点,我们保存了历史logging,这些是分析系统性能的很好的资料。这些数据是海量的,每天eBay记录的log超过1.5TB。我们还可以开发很多工具来方便的处理这些log,方便快速定位。

我自己还有一点理解,一般我们自己开发比较小的系统可能没有这么完善的系统,但是对于多线程,或者死锁之类的问题,log真的是非常有用,当然什么东西都不是免费的,log会对系统速度,磁盘都造成压力。所以写一个高效的log layer也不是容易的事情。还有就是学习一点perl对于分析log非常有好处。我把perl比喻成文本处理的瑞士军刀,短小精悍,方便快捷。

 

 

Everything Fails[Code Rollout/Rollback]

其核心思想非常明了:就是要支持Undo!

Pattern: Rollback

Absolutely no changes to the site which cannot be undone(!)

  1. Entire site rolled every 2 weeks: 16,000 application servers in 220 pools.
  2. Many deployed features have dependencies between pools.
  3. Rollout plan contains explicit set (transitive closure) of all rollout dependencies.
  4. Automated tool executes staged rollout, with built-in checkpoints and immediate
    rollback if necessary.
  5. Automated tool optimizes rollback, including full rollback of dependent pools.

简单的说就是因为系统太大了,首先要制定roll out plan,什么先roll,什么后roll,在roll out的时候当然是有工具的。其中需要牢记的一点就是一定要制定roll back plan。万一代码上线出问题,能够迅速的恢复到以前的状态。

Rollback非常重要,如果出现site issue。Operation Team最关心什么,他们最关心的就是能不能尽快的恢复正常,Operation并不关心到底是哪行代码出了问题,那是developer关心的问题。一般情况下,Operation会看一下,最好能马上解决,如果不行,那就先"保留现场",然后roll back,如果roll back以后系统正常了,那Operation会松一口气,然后再和developer一起去调查什么地方出了问题。

可见,如果没有roll back的机制,就没有退路了。整个site的availability就会下降。不单单是Code上面,每次我们做DB Schema的修改,都要写verification plan和roll back plan,就是这个DB的改动一但完成,如何来验证你所做的改动正得生效了,如果出了问题,如何回退。

我有一次碰到一个问题,假设当前的代码版本是100,运行了一段时间,我们发现有个BUG需要紧急Fix,然后我们将带有BUG FIXING的代码(版本101) roll out,但是我们一 roll out,就发现系统非常不稳定,查了一下,原来那个BUG FIXING带来了一个更为严重的BUG,怎么办?FIX那个新的BUG需要时间,但是如果Roll Back那以前的那个BUG就会再出现。需要马上做决策!最后还是决定,先roll back,毕竟site的稳定最重要,同时抓紧时间fix bug。对于以前的那个bug,在新的bug没有被fix之前,采用别的办法避免其出现。

如何避免其出现呢,这里倒是正好引出了关于roll back pattern的第二个例子: Feature Wire-on/Wire-off,并不是所有的时候都严重到一定要code roll back的。

 

 

Everything Failes[Feature Wire-on/Wire-off]

Pattern: Rollback

就模式来说,Feature Wire-on/Wire-off还是采用了roll-back的思想。我们看两种情况:

  1. 如果现在在新的版本的应用程序中新增了2个Feature,A和B,上线后发现其中有一个Feature工作不正常,或者由于Operation和Business的原因,希望禁用其中的Feature A,如果采用code roll-back的方法,A和B就会同时失效,如何禁用A,而保留B。
  2. 如果Pool A和Pool B同时有个新Feature要上线,但是Pool A和Pool B在新版本中的Feature互相依赖,如果完整的roll-out一个Pool需要1天,那无论是先roll pool A还是先roll pool B就不能解决依赖问题。

解决提出的两个问题是非常典型的两个问题,解决的思路就是:将代码部署和Feature部署脱离!

  • Decouples code deployment from feature deployment

在具体实现上,我们为每一个Feature设定一个"软"开关,然后统一在一个地方设置所有Feature的开关状态。

  • Every feature has on / off state driven by central configuration
    1. Allows feature to be immediately turned off for operational or business reasons.
    2. Allows features to be deployed “wired-off” to unroll dependencies.

如此一来,对于情况1,只需要简单的把开关关掉,那个Feature就wire-off了。对于情况2,我们可以把两个Pool的代码都roll 完,但是两个Feature都是  Wire-off的。然后等到要让feature生效的那一天那一秒,同时把两个feature wire-on。

最后要提到的一点是:

  • Applications check for feature “availability” in the same way as they check for
    resource availability

Randy举了一个例子,假设在页面上要显示一个widget,这个widget依赖于数据库A,和Feature B。必须两个资源都Available的时候,这个widget才显示出来。从应用程序检测资源是否可用这个意义上来讲,数据库资源A和Feature B是等价的。

对于如何检查资源是否有效,我自己有一点猜想,但是不肯定他们真的是这么做的。比如Feature,我们可以检查开关状态。对于如何检查数据库资源是否有效,会不会是采用Failure Detection的方法?还是说就是根据数据库返回的错误代码进行Error Handling?Randy在下一讲给出了答案 : Everything Failes — Resource Mark Down

 

 

Everything Fails[Resource Markdown]

Pattern:Failure Detection

前面已经提到过Failure Detection,那次给出的例子是如何利用Central Logging进行检测Application的问题。其实这里的基本思路是一样的,如何检测资源的Availability,比如数据库资源。

  • Application detects when database or other backend resource is unavailable or
    distressed
    1. “Resource slow” is often far more challenging than “resource down” (!)

这里用数据库资源作为例子,应用程序发起数据库操作,失败了,但是一次失败很有可能是有偶然因素造成的。于是应用程序试图重试,如果重试一定次数以后还是返回同样的错误,我们有理由怀疑该数据库资源已经unavailable了,于是将该数据库资源标记为"不可用",同时报警。应用程序不会向标记为不可用的数据库发送数据库操作。

Randy特地提到了,资源使用速度非常慢要比资源彻底不可用难处理的多!所以在写应用程序的时候要注意这一点!

[注:]我这里倒是有一个问题:资源标记为不可用以后,由谁负责再将资源标记为可用呢,还有就是在资源标记为不可用以后,应用程序的行为是怎样的?

Pattern:Graceful Degradation

其核心思想是我们不希望"部分"失效导致"整体"失效。

  • Application “marks down” the resource
    1. Stops making calls to it and sends alert.
  • Non-critical functionality is removed or ignored
  • Critical functionality is retried or deferred
    1. Failover to alternate resource.
    2. Defer processing to async event.
  • Explicit “markup”
    1. Allows resource to be restored and brought online in a controlled way .

前3点写的已经很清楚了,我就不多说了。主要说一下第4点。我们有Automatic Markdown,为什么没有Automatic Markup。这是有教训的:

如果数据库服务器A因为Load太高而崩溃了,被Markdown,或者是因为A被Markdown后不再Take Traffic而缓了过来了,或者是因为我们采取了措施使得A 又Back to alive了。如果Application Tier使用了Automatic Mark up,那最坏的情况是什么呢?是eBay的16000台app server在很短的时间内将大量的Traffic又送到那台刚缓过劲来的数据库上,于是那台数据库在很短的时间内由于Load过高而再次极速死去,再次被Mark down,以此往复…down-up-down-uo-down-up…

所以,Automatic Markup是很有风险的,比较好的办法是当资源back up后,控制Application Tier中markup的速度和比例,使系统的恢复在有序,受控的情况下进行。

 

eBay Architecture–Racap

至此,Randy的关于eBay Architectural Principles的介绍都"翻译"完了,我从上个周末开始的,每天写一点,还是花了不少时间的。在重新看这些Slides的时候自己也很有收获,网上的Slides一共25张,除了最后一张Q&A以外,我一共写了24篇BLOG,是严格得按照Randy的PPT写的,一一对应。我应该没有透露不该透露的东西,也希望没有误导大众。最后,Recap,一共就是4句话,整个presentation都是围绕这4句话在谈:

  • Strategy 1: Partition Everything
  • Strategy 2: Async Everywhere
  • Strategy 3: Automate Everything
  • Strategy 4: Remember Everything Fails

不废话了,整个关于eBay Architecture的介绍到此暂告结束。

Thanks Randy~~

 

 

你可能感兴趣的:(eBay Architecture(10)–Remember Everything Fails[Overview])