1号店技术部从1个人做起到今天千人级别的规模,系统支持每天亿级的访问量、单Service支持每天亿级的请求、订单支持每分钟几万单级别、Service服务可用性达到99.9999%,架构上也经历了历次演进,今天我们就从应用架构历次演进的落地点谈起。
1号店应用架构的演进大致经历了以下历程:强依赖-> Service化->业务解耦->读写分离->异步->水平/垂直拆分->服务逻辑分组等。
当然这个过程从不是串行的,永远是并行的,并且这个过程永远是在1号店这辆系统大巴行进过程中进行的,因为我们不能停车也不能刹车,而且还必须不断提速。
2.1 应用架构的最大演进-SOA治理
早期的1号店系统,遵循简单的MVC架构,Controller层处理了所有的业务逻辑包括与DB的交互,在系统初期这种Simple的架构方便快捷,成本低业务响应快。但当业务开始变得复杂、人员规模爆发式增长,这种强耦合强依赖带来的弊端就成了巨大的瓶颈,代码耦合度高互相冲突、出错概率和事故概率明显提升,业务需求不能快速响应,SOA治理迫在眉睫,解耦和去依赖成为第一需求,于是Service化成为第一前提。
2.2 SOA治理- Service日志是保障
1. 做Service首先是规划,Service规划的第一步首先考虑什么?大家可以先自行考虑下
- 很多人想的是采用什么RPC框架、采用什么技术,怎么让性能更高;也有人首先想的是业务怎么拆分,怎么才能更合理。
- 我们首先想到的是如何做监控和问题定位。
- 高可用不是一步做到的,我们的Service可用性不是一步达到99.9999%的,在这过程中一定会有很多的问题出现,怎么提前发现这些问题、出现问题后如何快速定位,这才是最重要的。这只能依赖日志,这是监控和问题定位的基础。
2. 下单接口业务性强,其对可用、并发、性能的要求作为技术人你懂的。我们就从这个接口开始下手,但我们没有先去分析业务,首先想的是如何定义日志系统,让以后的监控和问题定位更简单更快捷。事实证明这个决定是对的,直到现在1号店的大部分订单相关的监控、预警、问题排查定位等完全依赖这个日志。
3. 日志系统的设计基于以下:一是进数据库、持久化有序化,二是分类化、层次化、错误code唯一。
- 进数据库、持久化有序化这个大家都理解,我曾经接手过的一个应用系统,一天下来Tomcat的log文件大小超过1G,会让你崩溃的。
- 分类化、层次化、特别是错误code唯一这个是关键,它是大海航行中的那盏灯塔,让你可以瞬间定位问题位置,它给监控预警带来的好处同样伟大,可以从各个维度去做监控预警。
4. 1号店现在有了很好的SOA中间件 – Hedwig,可支持百亿级的访问请求,它有SOA级别的日志,也很完善。但应用级别的日志我们还是建议各应用系统自己做,它的业务性、个性化是公共架构永远代替不了的。
2.3 应用架构演进之落地
一定有人要问1号店采用的什么RPC框架,好吧,是Hessian,这不是什么秘密。
为什么要用Hessian?我偷偷告诉你,PHP是最伟大的的开发语言。
2.3.1 业务垂直拆分
万事具备,草船已借箭,要从业务角度规划Service 了。
我们从产品、用户、订单三个维度上对Service进行了规划,构成1号店应用架构上的三架马车,确立了SOA治理的框架基础。
在此基础上,又陆续衍生出促销、积分、支付等众多Service业务,在三架马车中同样会细分至如文描、价格、库存、下单、订单查询等垂直服务。
Service化是前提,Service化完成后,后面可以大刀阔斧地干了,因为业务独立了、DB读写权限收回了,哈,好像整个天下都是我的了。但,得天下易治天下难,真正的大戏在后面。
2.3.2 读写分离
读写分离的重要性不需多讲,除了最简单的读写分离,写可以从应用层面继续细分,读也可以从应用和DB层面再细分,如订单的读写分离:
2.3.3 水平拆分
早在2013年,1号店就实现了库存的水平拆分,2014年又一举完成订单水平拆库&去Oracle迁Mysql项目。
订单水平拆库&去Oracle迁Mysql两个重量级的大项目合并为一个项目并完美无缝上线,在经济上时间上节省的是成本,在技术上体现的1号店的整体技术实力和水平。
2.3.4 服务逻辑分组
前面说到了读写分离,大家也注意到了,这是物理隔离。
物理分离好处明显,但其硬件成本、维护成本高的弊端也显而易见。这时候,我们的大杀器-Hedwig又上场了,有兴趣多来了解下我们SOA中间件-Hedwig,这可是获总裁奖的项目。
Hedwig提供了服务自动注册发现、软负载均衡、节点踢出与复活、服务动态逻辑分组、请求自动重试等众多SOA框架所需的强大功能,支持并行请求、灰度发布,其背后提供的调用链路及层次关系、日志分析、监控预警等更是为SOA治理提供了强大的后勤保障。
2.3.5 业务解耦/异步
上面提到的读写分离、水平拆分、逻辑分组等主要是从技术层面保障,也是技术人员最喜欢的话题。业务流程的梳理、优化、改造往往不被重视,但作为应用架构,我们最不能忽视的是业务。
- 我们的一个核心Service服务,性能从2年前的近200ms到现在的几十ms,从代码上、sql上的优化提升顶多占到10%,更多地优化都在业务流程的梳理改造上。
- 我们曾经花费两周多的时间将一个系统的整体性能优化提升了近10倍,最后总结下来,纯技术的优化(代码或sql质量导致的性能差)几乎没有。
优化主要在两方面,一是架构上,如使用缓存、单SKU的循环查询改成批量查询等,这个能优化的也并不太多,因为我们的架构整体还是比较合理的;最大的优化还是在业务梳理上,很多地方我们使用了重接口,接口里很多逻辑计算和DB查询,这些逻辑并不是所有的业务场景都需要的,但开发人员为了简单没有将业务场景细分,导致大量不合理的调用,既浪费了服务器资源又严重影响用户体验;同样,很多地方为了一个不重要的展示或逻辑也产生大量不合理的调用,反而影响了核心业务,这些都是最需要优化的,也是优化效果最明显的。
- 异步本身不是什么高深的技术,关键是哪些业务可以走异步,这更体现架构师的业务理解能力和综合能力。
如下单成功后给用户的消息通知、发票详细信息的生成等都可以异步,我们在这方面做了很多工作,包括和各业务方的大量沟通制定方案等,在不牺牲用户体验又保证业务流程完整的情况下,尽量走异步解耦,这对可用、性能都是极大的提升。
3. 追求极致
开放、共享、追求极致是1号店技术人的理念。我们在追求极致上做了很多,简单举几个例子:
3.1 一个下单接口定义了135个错误编码
前面提到过日志和错误编码的定义,大家一定想象不到,我们仅一个下单接口就定义了135个错误编码。接口上线后至今出现的错误编码在50-60个,也就是说有50-60处不合理或错误的地方,这个不合理或错误既有业务的又有程序的也有我们对编码定义的不合理。
出现一个我们就解决一个,系统自然越来越健壮和稳定,目前日常每天下单出现3-5个错误编码,主要为业务性如特价商品已售完无库存等。
那没有出现过的几十个编码是不是就意味着我们工作白做了?
功夫下对了永远不会浪费,在下单接口上线近2年后,一个之前从未出现过的错误编码跳出来了,是一个很难出现的业务场景,但通过这个编码,我们马上定位了问题,都不用去看代码。
我们永远不能保证系统没有bug,bug可以藏的很深埋的很久,但我们不怕,因为我们的伏兵也一直在,你一跳我们立马抓,毫不犹豫。
3.2 Service服务可用性99.9999%
6个9的Service服务可用性,可能有人不信,看看我们真实的监控邮件,这是每天亿级的调用量。
功夫永远在戏外,结果仅仅是一个结果,一步步踏实走过来的旅程才是我们收获最大的。
3.3 销售库存准确率99.9999%,超卖率为0
做过电商核心系统的人都明白库存控制的难度,库存不准甚至超卖的问题至今还有很多电商公司没有完全解决。
这个问题也曾经困扰我们,为此还专门写了一个库存刷子的程序来刷数据,现在这个程序已基本宣告废弃了,因为我们的库存准确率达到了6个9,超卖率为0。
怎么做到的?业务、业务、业务,重要的事说三遍。
我们团队花了3个多月的时间,对所有库存应用场景逐一排查,最终梳理出16个有问题的库存场景,并逐一协调解决,让库存形成严格的闭环线路,保证了库存的准确性。在这过程中,对库存闭环方案和对业务的理解成为关键,纯靠技术,我们能做的并不多。
4. 应用场景
4.1 业务监控>
业务监控首提订单监控,对订单我们从实际订单数据和Service接口调用量两个维度去做监控,保证了监控系统的稳定和准确(监控系统也会出错的),其中下单接口调用量使用的就是Service日志数据。
4.2 服务监控
依赖查询
(点击放大图像)
服务监控
(点击放大图像)
5. 后言
电商核心交易系统的高可用、高并发、高性能不是一朝一夕的,需要好的技术,更需要好的架构,如何找到落地点并一步步踏实落地,这是关键。有想法、有目标、有执行力,必有所成。
我们是技术人,但我们的很多工作并不一定要多高深的技术,业务和技术的平衡点才是最重要的。业务敏锐性对应用架构师和开发人员来说都尤为重要,因为更多的时候我们要的是解决方案而不是技术方案。