软件开发管理
淘宝的架构
淘宝用的是JBoss,框架是,缓存服务器是自己开发的,基本遵循SNA架构,水平扩展,数据库是,阿里巴巴集团的DBA几乎是国内最强的。目前淘宝的系统架构正在重构,计划两到三年后重写一次。有两个目标:
1.水平扩展已经满足不了需求,还需要水平和垂直扩展
2.开放API,让商家可以将外部网站资源集成到淘宝,无需直接在淘宝上开店
淘宝首席架构师是前JBoss员工,他目前正招募技术专家加入公司,承担这一极具挑战性的任务:设计下一代支持数十亿访问量的开放式在线电子商务网站。
关于淘宝的架构,目前不方便透露更多详细信息。
淘宝是一家大型电子商务网站,在线商品超过1亿,日均交易额超过2亿人民币,注册用户近8000万,是亚洲**的购物网站。对于淘宝这么大的一个网站,我想大家一定很关心整个网站用的是什么样的技术、产品、架构,也想知道淘宝是用开源软件还是完全商业化的软件。那么我就简单介绍一下淘宝用到的开源软件吧。
对于稍微大一点的网站,它的IT肯定是服务器集群来提供网站服务,而且数据库肯定是和应用服务分开的,有单独的数据库服务器。对于淘宝这种规模的网站,应用也是分成很多组的。接下来我会从应用服务器操作系统、应用服务器软件、数据库、开发框架等方面来介绍开源软件在淘宝的应用。
操作系统
我们先从应用服务器的操作系统说起,从软件角度来说,应用服务器最底层就是操作系统,首先要选择操作系统,然后才是基于操作系统的应用软件。在淘宝,我们的应用服务器采用的是Linux操作系统。Linux操作系统从1991年正式公布到现在已经有17年了,在互联网上应用非常广泛。在硬件方面,我们选择的不是小型机,所以一般可以选择的操作系统是Linux、Linux+ ...这个熟悉一方面是系统管理,另一方面是熟悉内核。熟悉内核对性能调优、定制操作系统都会有很大的帮助。而应用程序的全面优化、性能的提升也都是从操作系统的优化开始的。
应用程序服务器
确定了服务器硬件和服务器操作系统之后,我们再来说说业务系统的搭建。淘宝有很多业务系统应用都是基于JEE规范的,也有一些用CC++或者Java搭建的应用,所以我们要选择一个实现了JEE规范的应用服务器,我们的选择是r,它是一个支持JEE规范的开源应用服务器。在几年前,如果用Java技术搭建互联网应用或者企业级应用的话,在开源软件中的选择一般就是组织的、JBoss的、Resin,严格来说,JBoss的、Resin都不能算是应用服务器,它们都是实现一些J2EE规范的容器。商业软件的选择就是IBM的、BEA的了。现在除了、、Sun的也都是很优秀的JEE应用服务器,也为现在的开发者提供了更多的选择。具体对于现在的JEE应用服务器的对比,这里就不多说了。
在应用服务器的前端我们用了转发,选择的web服务器都是比较出名的,在几年前在linux系统上几乎是开源的**选择,虽然那时候也有一些其他开源的,但是功能和稳定性是比不上的。放到今天来看,也会是一个非常不错的选择,非常轻量,占用内存资源少,功能虽然不算强大,但是性能非常好,在很多场景下都更强。微软的IIS只能在系统上工作。而如果使用IIS,那么进行web应用开发基本上都会选择ISAPI,ASP或者ASP.NET。
数据库
说完了我们使用的操作系统和应用服务器,再来说说我们的数据库。在淘宝的应用中,使用了两个关系型数据库管理系统,一个是公司的,一个是MySQL,它是一款非常优秀并且被广泛使用的商业数据库管理软件,功能强大,安全性高,可以处理比较大的数据量。MySQL是一款非常优秀的开源数据库管理软件,非常适合使用多台机器组成多点的存储节点阵列(这里我不是指MySQL本身提供的集群功能),单位数据存储成本也非常低。使用多台MySQL安装组成存储节点阵列,通过MySQL自身或者应用程序自身的处理,可以很好的保证容错性(允许部分节点失效),保证应用的健壮性和可靠性。可以说,在关系型数据库管理系统的选择上,可以考虑应用本身来做决定。
一个互联网应用除了服务器操作系统、软件、应用服务器软件、数据库软件之外,还会涉及到一些其他的系统,比如一些中间件系统、文件存储系统、搜索、分布式框架、缓存系统等等。在淘宝,这些系统都是自主研发的,没有使用任何商业或者开源的产品。有些系统可能有一些开源产品或者商业产品,但是考虑到淘宝自身的需求,以及大并发的压力,这些系统都是自主研发的。
开发框架
前面我们讨论过系统级的产品,现在我们来谈谈开发框架的使用。有的朋友可能要问,作为这么大型的网站,淘宝的Web表现层是用的什么框架,是如何实现的?曾经有淘宝的一位求职者问过我这个问题,问我有没有用过,我告诉他没有,其实淘宝的Web表现层的框架是没有的,没有的,等等。淘宝的Web表现层的框架是采用了集团内部自主研发的一套Web框架,这套框架可以解决一些其他Web框架无法解决,但是在淘宝的应用中会出现和需要解决的问题。在淘宝的很多应用中,也用到了一些开源框架,比如,jBPM,,Mina等等,这些开源软件的使用,为我们构建应用系统提供了很大的帮助。
我认为使用开源软件构建系统有两大好处:
一是降低成本,假设你有1000台应用服务器,如果每台服务器都使用商业软件或者IBM的软件,而不是开源软件,那么这1000台机器购买应用的成本就会非常高。
还有一个好处(我觉得是**的好处)就是可以看到软件的源代码,可以研究了解软件内部的工作流程和原理,这对于应用程序的设计、开发、查错、优化等都有很大的帮助。
淘宝的开源视角
关于开源软件的应用,有人担心质量,有人担心软件本身的开发和更新等等。关于质量问题,我觉得很多开源软件,特别是一些知名的开源软件,都有很完善的组织,有完整的开发、测试、发布流程。一个新版本在完成之前,都会有多个测试版本发布,最后才是正式版。这跟商业软件是一样的。而且因为代码是开放的,更容易发现错误,提高质量。至于第二个问题,我觉得跟**个问题一样,关键是组织和规划,而不是是否开源,而且很多知名的开源软件背后,都会有厂商在支持。软件本身的发展应该不会有问题,软件突然停止发展的可能性也不大。
未来我们会持续关注开源软件的发展,并根据需要采用不同的开源软件。在选择开源产品时,我会考虑以下几点:
1. 本软件的现有功能及其
2. 软件本身的架构
3.软件开发活动
4.开源软件是否符合该领域的国际标准
5. 选择同类产品中具有比较优势的产品。并考虑可能产生的迁移成本。这种迁移是指采用该开源软件后对现有系统的迁移,或者从该开源软件迁移到其他软件。
对于企业级系统和互联网应用来说,使用开源软件不仅可以降低成本,更重要的是可以真正了解软件内部的工作机制。还可以在现有的基础上进行增强和定制,很多好的设计和实现都可以从开源软件中学习到。希望国内更多的企业在使用开源软件的同时,能够开源一些自己的软件,或者成为一些开源软件的贡献者。作为淘宝,我们也会积极参与开源活动,努力为开源的发展做出我们应有的贡献。
探索淘宝高性能可扩展架构技术
今天我们继续探索大型网站的秘密,探索淘宝的架构技术。作为国内**的B2C网站,淘宝的网站架构一直承受着数据量快速增长的压力。要保证良好的负载和流程体验,可扩展、高性能的网站架构必不可少。
1. 应用程序无状态
一个系统的扩展性取决于应用状态是如何管理的。试想一下,如果我们在应用中保存了大量的客户端状态信息,那么当这些状态信息宕机时,我们该怎么办?一般来说,我们通过集群来解决这个问题,而所谓的集群不仅仅具备负载均衡,更重要的是具备故障恢复,比如集群节点广播复制,jboss配对复制等状态复制策略,但是集群中的状态恢复也有它的弊端,那就是严重影响了系统的扩展性。系统无法通过增加更多的机器来实现很好的水平扩展性,因为随着节点的增加,集群节点之间的通信也会随之增加。所以,为了实现应用本身的扩展性,我们需要保证应用的无状态性,让集群中每个节点都一样,这样系统才能更好的进行水平扩展。
上面已经说了无状态的重要性,那么具体怎么实现无状态呢?这时候一个框架就会发挥作用了。幸运的是公司已经有了这样一个框架。公司的框架采用的实现,主要把状态保存在里面,这样应用节点本身就不需要保存任何状态信息了。这样当系统用户增加的时候,只要增加更多的应用节点就可以达到水平扩展的目的。但是使用客户端的方式保存状态也会遇到限制,比如每个大小一般不能超过4K,很多浏览器限制一个站点最多保存20个。公司的框架采用的是“多值”,也就是一个组合键对应多个值。这样不仅可以避免数量超过20个,还可以节省存储有效信息的空间,因为默认每个都会有50字节左右的元数据来描述它。
加上公司目前的框架实现方式,其实是通过集中管理的方式完成的,具体来说,多个无状态应用节点连接到一个服务器上,服务器会将其保存到缓存中,服务器后端配备底层持久化数据源,比如数据库、文件系统等。
2. 有效利用缓存
做互联网应用的都应该知道缓存对于一个互联网应用有多重要,浏览器缓存、反向代理缓存、页面缓存、本地页面缓存、对象缓存等等都是缓存应用的场景。
一般来说,缓存按照距离应用程序的远近可以分为: 和 。一般情况下,系统中要么使用 要么使用 。如果两者混用, 和 的数据一致性处理会变得比较麻烦。
大部分情况下我们说的缓存都是读缓存,还有一种缓存叫写缓存,对于一些读写比不高,数据安全性要求不高的数据,我们可以将其缓存起来,减少底层数据库的访问。比如统计某个商品的访问次数,统计 API 调用次数等,可以先写入内存缓存,再延迟持久化到数据库,这样可以大大减少数据库的写入压力。
以门店线下系统为例,用户在浏览门店时,门店介绍、门店沟通区页面、门店服务条款页面、门店试衣间页面、门店搜索界面等更新频率都不是很高,适合放入缓存,可以大大降低DB负载。另外商品详情页相对更新频率较低,也适合放入缓存,降低DB负载。
3. 应用程序拆分
首先在讲解应用拆分之前,我们先来回顾一下一个系统从小到大过程中遇到的一些问题,通过这些问题,我们会发现拆分对于构建一个大系统有多么重要。
系统刚上线的时候,用户还不多,可能所有的逻辑都放在一个系统中,所有的逻辑都运行在一个进程或者一个应用程序中。这时候因为用户比较少,系统访问量较低,所以把所有的逻辑都放在一个应用程序中也未尝不可。但是兄弟们都知道好景不长,随着系统用户的不断增加,系统的访问压力也越来越大。同时随着系统的发展,为了满足用户的需求,原有的系统需要增加新的功能。当系统越来越复杂的时候,我们会发现系统越来越难维护和扩展,系统的可扩展性和可用性也会受到影响。那么这个时候我们该如何解决这些问题呢?明智的做法就是拆分(也可以看作是一种解耦),我们需要将原有的系统按照一定的标准,比如业务相关性,划分成不同的子系统,不同的系统负责不同的功能。拆分之后,我们可以对各个子系统进行扩展和维护,从而提高系统的可扩展性和可维护性。同时,我们系统的水平扩展性也大大提高,因为我们可以针对性地对压力大的子系统进行水平扩展,而不会影响其他子系统。而不像拆分之前,每次系统压力增加,我们都需要对整个大系统进行扩展,成本比较高。另外,拆分之后,子系统之间的耦合度降低了,当某个子系统暂时不可用时,整体系统还是可用的,因此整体系统的可用性大大增强。
所以一个大型的互联网应用是必须要拆分的,因为只有拆分,系统的扩展性、可维护性、可扩展性、可用性才能变得更好。但是拆分也给系统带来了问题,那就是子系统之间如何通信,具体的通信方式有哪些呢?一般有同步通信和异步通信,这里我们先讲同步通信,后面专题“消息系统”会讲异步通信。既然需要通信,那么一个高性能的远程调用框架这时候就很重要了,所以我们公司也有自己的HSF框架。
上面说的都是拆分的好处,但是拆分必然会带来新的问题。除了刚刚说的子系统通信问题,最值得注意的问题就是系统之间的依赖问题。系统越多,系统之间的依赖就会变得复杂。这时候就需要更加关注拆分的标准,比如有些依赖的系统能不能垂直化,让这些系统的功能尽可能垂直化。这也是公司目前正在做的系统垂直化。同时还要注意系统之间的循环依赖,如果出现循环依赖,就要当心了,因为可能会导致系统链启动失败。
现在我们了解了拆分的重要性,让我们看看公司本身在发展过程中如何拆分其系统。
在这个演进过程中,所谓的拆分发生在V2.2和V3.0之间。在V2.2版本中,几乎把公司所有的逻辑都放在了一个系统中,这样带来的问题就是系统扩展和修改非常麻烦,更致命的是随着公司业务量的增加,如果按照V2.2的架构,根本没办法支撑公司以后的快速发展。所以大家决定对整个系统进行拆分。V3.0版本的系统对整个系统进行了横向和纵向的拆分,在横向上按照功能分为交易、评价、用户、商品等系统,同样在纵向上分为业务系统、核心业务系统和基础服务。这样每个系统都可以独立维护,可以独立进行横向扩展。比如交易系统可以独立进行横向扩展和功能扩展,不影响其他系统。
从上面我们可以看出,一个大系统要想可维护、可扩展、高可用,就必须拆分。拆分必然会带来系统如何通信、如何管理系统间依赖关系等问题。关于通信,公司自主研发了自己的高性能服务框架 HSF,主要解决公司各子系统间的同步、异步通信(目前 HSF 主要用在同步场合,调用场景比较少)。至于系统间的依赖管理,目前公司做的还不够好,这也应该是我们今后要努力解决的问题。
淘宝架构师岳旭强的年度展望
2009年是充满挑战和机遇的一年,对于大多数人来说,金融危机已经习以为常,并且努力去化解危机。科技圈也是如此,那些被裁员的人肯定都找到了工作,所以都在努力做技术。现在,我们回归正题,讲一些2009年的故事,一起来回忆一下,一起来娱乐一下。
数据可扩展性的讨论与总结
金融危机是电商的机遇,所以2009年是淘宝高速发展的一年。一个网站从几百万、几千万条的数据规模,到几亿、几十亿、几百亿条的数据规模,是一个量变到质变的过程。单纯的硬件升级已经到了瓶颈,需要在整体架构上下功夫。2009年,大部分的时间都花在了数据扩展性上。
对于一个电商网站来说,订单是最核心的数据,也是增长最快的数据。对于数据扩展性,最传统、最简单、最有效的模型就是对数据库进行分片。当订单和分片相遇,会迸发出什么火花呢?2009年初,它们碰撞了许久,但产生的火花却非常少。**的问题就在于数据切分的规则。不规则的水平切分肯定会带来数据合并的成本,而按照业务规则去拆分又会因为买家和卖家不同的查询需求导致数据无法切分。**可行的火花就是保存两份订单,一份给买家,一份给卖家,但是成本比较高,对数据同步的要求也很高。
所以我们初步决定采用订单拆分的方式,采用重复保存的方式。有一天我们仔细查看订单访问情况,发现订单库90%以上的压力来自于查询,而查询中90%以上的压力都来自于非核心业务,只是订单数据的展示,对一致性和实时性的要求很低。
因为数据量大,数据库压力很大,自然想到的就是分散压力,解决的办法就是分库分表。有时候我们不妨更直接的去思考问题,既然压力大,能不能减轻压力?通过了解订单访问情况发现,昂贵的主库80%以上的压力都给了不重要的需求,这个就是我们优化的关键。所以订单最终采用了读写分离的方案,高成本的主库解决事务和重要的查询业务,80%以上的不重要的读取交给低成本的数据库服务器去解决。同时对数据复制的要求也很低,实现起来也不是太难。
还有一个有意思的案例是商品数据的扩展。商品的水平切分很简单,按照卖家来切就可以了。有了订单的先例,首先想到的就是读写分离,因为可以降低成本。实施一段时间后,我仔细思考了商品的整体需求,突然发现商品并不需要和订单一样的要求,一定要用高成本的主库吗?用低成本的普通服务器做数据库可行吗?仔细评估后发现可以接受,而这导致之前启动的商品读写分离项目的一部分工作被白费了!
讲完故事,总需要有个总结。先来虚化一下:对原始需求理解清楚是系统决策的前提,不然一定会走弯路。理解原始需求并不容易,中间会有很多干扰和阻力。前面的例子看起来很简单,但是在一个已经运行了5年的系统上,想要理解本质、做出改变,并不是那么容易的。另外,经验有时候也会成为系统决策的阻碍。这个很矛盾,所以需要有一种从零开始的心态去思考问题,归根结底还是回归原点。
再说点比较实际的东西,对于大型分布式系统的数据访问,一个统一的数据层是非常必要的,应该封装好数据水平和垂直切分、读写分离、数据访问路由、复制、合并、迁移、热点处理等功能,并且对应用透明,比如如果是应用相关的,可以封装在JDBC层面,如果是数据库相关的,可以封装在数据库协议层。