Ebay架构特点(HPTS 2009)

在HPTS 2009上,ebay 架构师Randy Shoup又列出了五个lessions.它们分别是:
1 Expect (R)evolution
2 Dependencies Matter
3 Be Authoritative
4 Never Enough Data
5 Custom Infrastructure

我结合ebay以前的5个lessions,总结一下可伸缩性和高性能的系统架构的一些最佳的实践:

一 Partition Everything
在一个大型的系统架构和设计当中,我们会面临各种各样的挑战,而为了能使得系统具有良好的可伸缩性,我们就需要“切分系统的每个部分”,而这里的切分又设计到一些方向性或者是策略性的问题,也就是如何切分,从什么方向入手去切分,我结合Ebay架构师的经验以及自己的想法,总结如下。
1 垂直切分
说到垂直切分,我们能第一时间想到的就应该是系统的分层架构,一个系统可以分为好几个层次,比如在目前J2EE系统的开发当中,我们一般都会分为表现层,应用层(负责业务逻辑的封装),领域模型层(负责业务逻辑的实现),和持久层(负责对底层数据源的访问),按照分层的思想切分以后,系统的每一个层都是高内聚的,同时层与层之间也是低耦合的,这就符合了高内聚和低耦合的原则,这样以来系统的各个层次可以独立的进行维护,扩展和伸缩。分层的好处还可以参见我的一篇文章:系统为什么要分层?

2 水平切分
当系统按照分层原则分为几个层次以后,虽然层与层之间实现了好的解耦,从而容易维护和伸缩,但是随着负载的增大,单个层的伸缩也会遇到瓶颈,而这个时候就需要水平的切分各个层次,这里又可以按照逻辑功能的水平切分和存储或者数据的水平切分。
2.1 应用水平切分
在一个大型的系统中往往会涉及到很多的错综复杂的逻辑,这个时候架构师就需要根据不同的功能对系统进行水平的分割,比如按照用户,商品,交易,搜索等功能进行水平的分割。而在具体架构和设计的过程中,我们一般通过不同的jar,bundle等来进行分割。
在按照功能进行切分以后,我们还需要注意一点就是尽量采用无状态的架构,因为系统的伸缩性很大部分上取决于你的应用的状态如何保存,因此无状态的架构对于水平的切分很重要,只有尽量的采用了无状态的架构以后,我们的系统才可以能更好的进行水平的伸缩。
2.2 存储水平切分
一个大型的系统往往会有非常巨大的数据量,这个时候就需要对数据也进行切分,比如将数据分为用户数据库,交易数据库,商品数据库等。在对数据按照功能进行切分以后,我们还可以对其进行进一步的切分,此时一般采用“Shard”技术,比如将前100W条记录保存一台主机上,然后依次类推。

二 Abstract and Virtualize at all Levels
在第一条"Partition Everything"中,我们将切分融入了系统的血液当中,这样也就使得系统具有了良好可伸缩性的血液,但是在切分以后,也给系统带来了其它的问题,比如在存储层面上,因为我们将数据按照功能进行了水平的切分,这样以来就使得数据的访问复杂了起来,这个时候就需要对数据访问层进行合理化得抽象和虚拟化,使得物理上分割的数据库对外界来说是一个统一的整体,而此时一般我们可以采用DAL技术。
在Ebay的系统中,搜索是系统很重要的构成部分,一般用户的一次搜索都需要通过一个聚合器对搜索结果的进行整合,对于外界来说通过聚合器封装和抽象了搜索系统。
因此抽象和虚拟化使得系统的伸缩性的实现更加容易和方便,它使得伸缩性的系统更加方便管理和维护。

三 Asynchrony Everywhere
目前J2EE其实都是同步的API(JMS除外),同步必然带来组件与组件之间的紧耦合,而组件之间的耦合度越高,也就不能独立对其进行伸缩,从而影响到伸缩性。假如系统中A组件调用了B组件,从基本的数理逻辑来看,如果A可以用,那么B可以用,反过来,如果B不可以用,那么A也不可以用,这就是同步调用给系统可用性带来的损失。因此只有通过一种松耦合的方式对组件进行解耦,这样才能使得系统具有好的可用性和伸缩性,而通过异步的方式是一种比较好的解耦的方式。关于异步大家可以参考一下这篇文章:Java EE meets Web 2.0

Ebay将异步这条原则贯彻的非常彻底。在组件内部使用SEDA来实现异步性,组件和组件之间的交互也尽量的采用异步,将业务程分为很多个阶段,各个阶段通过异步的方式连接起来。在Ebay中,异步主要用了Event Queue和消息多播等技术。
说到这里,我也想说说目前比较流行的DDD如何引入异步,在传统的J2EE开发当中,业务逻辑实现都是在action或者service,业务逻辑的实现是通过action和service这些技术的组件来驱动的,而采用领域驱动设计以后,业务逻辑的操作是由Model驱动的,Model触发Domain Event,然后异步的驱动技术性的组件来完成对Domain Event的响应,这样以来整个系统的核心就是Domain Model,而各种的技术性的组件仅仅是一种为Domain Model服务的tools。Domain Model和Domain Event的结合可以参见下图:



四 Avoid Distributed Transactions
事务和性能,伸缩性是相互矛盾的,系统中事务用的越多,那么性能和伸缩性就会变得越差,因此必须合理化得利用事务。尤其是传统的分布式事务,采用2PC来提交,这样就要求所有事务性的资源必须都准备好,只要一个有问题,那么整个事务都会受到限制,更严重的是2PC是非常影响性能和伸缩性的。从Ebay的架构中也可以看的出来,Ebay坚决的杜绝分布式事务。如果不采用传统那种分布式事务,那么采用什么样的事务策略呢?答案就是BASE策略。
在说BASE策略之前,我们有必要来了解一下Eric Brewer大叔的CAP理论,CAP是Consistency,Availability,Partition_tolerance的缩写,Consistency表示系统的状态对所有的Client都是即时一致的。Availability表示系统的可用性,它主要是指任何一个业务操作都能在预定的时间内完成。最后一项Partition_tolerance表示分区容错性,它主要是指业务操作不能受单个系统组件的影响,即使某一些组件不能使用,业务操作也必须完成。理解了CAP理论的这三个方面以后,我们来看看CAP理论到底告诉了我们什么,CAP告诉我们,任何一个分布式系统不可能同时满足这三个条件,最多只能同时满足两个(是不是有点失望*^__^*).

我们现在明白了CAP,那么BASE是什么?它和传统的ACID又有什么区别和联系呢?接着往下看,你就会明白啦呵呵。BASE是basically available,soft state,eventually consistent的缩写,BASE表示基本可用,事务软状态和最终一致性。BASE采用一种乐观策略,它不要求事务状态的即时一致性,而是要求一种最终一致性,也就是说事务状态在某一个用户可以接受的范围内是不一致的,但是最终会变的一致,而传统的ACID事务策略采用一种悲观的方式,它要求事务状态在业务操作结束以后必须是即时一致的,是一种Hard state,一种面向连接的状态,炫绷得越紧越容易断,事务也是一样的,ACID这跟炫也非常容易断,但是无论是BASE还是ACID都无法摆脱CAP的限制,BASE通过一种事务的软状态和弱一致性换来了可用性,同时也满足分区容错性,而ACID采用了强的一致性,而牺牲了可用性。因此BASE适合于对可用性最求大于一致性需求的场合,而ACID适合于对一致性要求很严格的场合,比如一些股票软件系统就适合ACID。最后,如果大家对BASE和ACID还是不理解的话,推荐大家看看Dan Pritchett大哥的这篇文章:BASE: An Acid Alternative

五 Cache
缓存存在的地方很多,系统中用缓存的方式也有很多方式,我这里仅仅说说我自己的理解,这些也是我目前公司的项目中所运用的方式。
我这里主要说一下缓存如何和领域模型进行结合使用。在传统的软件开发中,我们一般采用action -->service-->Dao中,系统的业务逻辑充斥在action或者Service里面,最终的结果就是action和service非常庞大,非常的难于理解,尤其是在设计到事务的时候,到项目后期,你会发现事务配置在action和service都不合适,这都是传统的SSH传统的开发带来的负面影响。那么采用DDD以后,业务逻辑的实现是通过Domain model驱动技术组件去完成功能,所有的业务逻辑都被封装在了Domain Model里面,但是这样也带来一个问题,Domain model一般聚合了很多的东西,如果我们用完了就是把它扔掉,那么每次从持久层构建它是非常耗费性能的,因此这就需要将其放在某一个地方,这个地方就是缓存。当然了随着目前KEY-VALUE存储系统的不断发展,以后我们可以直接用KEY-VALUE存储系统来讲Domain Model保存起来。如果大家对于Domain model为什么要聚合很多东西,为什么也不能聚合不该聚合的东西,请大家参考另外一篇文章:Improving performance and scalability with DDD

六 Not Only SQL

对象和关系数据库很矛盾,这个经常来本站的人应该都清楚。我就不多说了,目前随着KEY-VALUE存储系统的逐渐成熟和普及,我想一种:面向业务的分析和设计(DDD)+面向业务的存储(KEY-VALUE)的新的架构方式也将会诞生。

七 Embrace Inconsistency

这一点其实也还可以算是一致性和性能,伸缩性,高可用性之间的博弈
八 Automate Everything
九 Remember Everything Fails
十 Expect (R)evolution
十一 Dependencies Matter
十二 Be Authoritative
十三 Never Enough Data
十四 Custom Infrastructure

先写这么多,以后的慢慢补上,难道是自己老了?打这么几个字手困(*^__^*) 最后,欢迎大家讨论。


[该贴被admin于2009-11-27 09:46修改过]

差点忘记传Randy Shoup大哥的lessons了。

由于附件最大限制100K,所以分卷压缩。见谅(*^__^*)


[该贴被xmuzyu于2009-11-26 23:21修改过]
attachment:


eaby.part1.rar
eaby.part2.rar

写得不错,看来Ebay实践和我们理论探索碰到一起了。

EBay经验特别值得大多数企业软件借鉴,因为Ebay不是facebook之类社区网站,只要性能,不要一致准确性的,EBay是一个交易和钱打交道的系统,对数字的精确性一点不亚于银行系统,所以,Ebay如何在Cap原则指导下进行权衡的难度更大。

不过有一点是明确的,虽然我们注重交易的一致性和准确性,但是分布式事务不能采用是绝对一个基本原则。

而EJB(EJB2/EJB3/EJBX)是整合分布式事务和分布式缓存一个中间件,我们可以划分一个发展阶段:关系数据库 ===>EJB ===> NoSQL(Ebay架构)

EJB是基于关系数据库的一个提高,而Ebay这样Base架构则是一个打散的EJB,是基于EJB的一个提高。因为对于Ebay这样产品,没有一个通用的解决方案,必须靠架构师人脑智慧。


[该贴被banq于2009-11-27 11:21修改过]

2009年11月26日 23:20 "xmuzyu"的内容
在传统的J2EE开发当中,业务逻辑实现都是在action或者service,业务逻辑的实现是通过action和service这些技术的组件来驱动的,而采用领域驱动设计以后,业务逻辑的操作是由Model驱动的,Model触发Domain Event,然后异步的驱动技术性的组件来完成对Domain Event的响应,这样以来整个系统的核心就是Domain Model,而各种的技术性的组件仅仅是一种为Domain Model服务的tools。

所以我在做dddsample jdon版本的时候也豪不犹豫的把原版dddsample在service中的类似代码移到Model中了,要是spring是一个DDD框架,也许就不会那么写。
xmuzu继续呀,还有很多没写完呢!

收藏

1.Expect (R)evolution
2 Dependencies Matter
3 Be Authoritative
4 Never Enough Data
5 Custom Infrastructure
这5个课题我感觉没有前面提出的Partition Everything等四个架构原则具有重大意义。

这5个原则比较高,已经涉及非架构因素,包括项目管理,认为唯一不变的就是变化,这已经上升到哲学了。

xmuzyu写的四个原则容易让人明白,是一个全新异步细分思路,我以前说过,衡量你的系统是否足够细分,就看是否能够插入负载平衡,我们要怀疑代码中任何地方都可能是性能瓶颈,将来都有可能将其割裂,植入异步或负载平衡。

所以,Ebay这里谈的异步已经不是通常意义上的异步,比如我要发一个Email,那么使用异步实现,这是业务异步,而我们这里谈的是架构异步,架构异步比业务异步更广泛,它是在Base思想上的异步,是一种讲究最终一致的异步,而发Email是不讲最终一致的,什么时候发对于系统其他地方不重要,也不用Email发送完,给一个确认(如果是,就是要最终一致了)。

异步和并行编程思路是一脉相承,同一个范畴,就是和我们通常的Spring/Session Bean等同步架构思维做斗争,驱赶他们到一个小的系统,而讲究scalable的系统必定是追求BASE思想的异步架构。

这里的异步和NOSQL模式中的一致性模式是同一个意思:
1.严格一致:序列化立即拷贝更新。(quorum based 2PC )
2.自己读写一致:能够立即看到自己的修改结果,但不一定及时看到其他人修改结果。
3.Session一致性:当客户端被重定向到同一个服务器时,自己读写一致。
4.读系列一致:保证客户端发出多次请求能够看到数据的更新。
5.最终一致Eventual Consistency:需要等待一段时间才能看到以前修改结果。

[该贴被banq于2009-12-01 14:13修改过]

在交易支付系统中有个问题,比如用户通过银行支付,钱已经付出去了,但是银行没有返回相应的成功信息,如何保证这种信息的一致性(没返回成功信息就不付款)?

2009年12月06日 19:53 "xmuzyu"的内容
可伸缩性最佳实践[该贴被xmuzyu于2009-12-06 19:55修改过]

来这里的人都很牛啊,可伸缩性讲的不错

讲得非常好!个人理解这也是大部分大型应用需要去努力的方向。
随着业务的扩展,性能的需求提升,异步是必然的结果。这样系统职责分割,异步消息引入,领域模型设计都是不可或缺的。对于帐务类相关的业务,抛弃了传统的分布式事务,但可以用系统意义上的分布式事务完成(靠数据库的执行状态来推动帐务的提交还是回滚)。

挺好的文章,理念可以帮助我理解一些服务器的东西

2009-11-26 23:10 "@xmuzyu"的内容
2.1 应用水平切分
在一个大型的系统中往往会涉及到很多的错综复杂的逻辑,这个时候架构师就需要根据不同的功能对系统进行水平的分割,比如按照用户,商品,交易,搜索等功能进行水平的分割。而在具体架构和设计的过程中,我们一般通过不同的jar,b ...

功能模块的划分属于业务的垂直分割,
而业务的水平分割是依一定规则对数据进行聚合,
如楼主的例子,可根据用户注册顺序(用户ID),把用户的商品,交易,评价等相关数据聚合到一起(如放同一个库),尽量避免跨库调用的机会,这样的设计,随着用户注册数量的增加,只需要简单添加数据库即可。。

不论是业务水平分割还是垂直分割都十分考验业务建模的功力

个人体会,应用模块可以分开,是分布式的,但其存储结构不一定是分开如楼主的用户数据库,商品数据库,交易数据库等,
这也是一个有趣的问题?banq可以讨论以下吗?