唯品会11.11:频繁黑匣架构背景下,看唯品会的革命性重构

2016 年 11 月 10 日

2016 年即将结束,但 2016 年最大电商狂欢即将开始,中国互联网电商的增长似乎永远看不到尽头,如果电商企业的技术架构撑不住海量访问,面对大促也仅有加机器的手段,相信随后不久的双 11 会暴露技术架构更多的问题。

2016 年 12 月 2 日 -3 日, ArchSummit 全球架构师峰会将在北京国际会议中心举行。本次大会设置了《电商专题:系统架构如何应对业务爆发式增长》《阿里双 11 技术架构突破》专题来深入解读双 11 等大促背后的技术故事,其中邀请了唯品会资深架构专家张广平老师前来分享唯品会选购系统架构设计实践,我们借此机会采访了张广平老师,他为我们带来唯品会革命性重构背后的架构演进过程,希望可以为大家带来启发,如果读者想了解更多唯品会电商技术,欢迎报名参加 ArchSummit 北京站并与张广平老师进一步交流。

受访嘉宾介绍

张广平,唯品会资深架构专家,2014 年前在 eBay 工作多年,先后在 eBay 商城支付平台开发管理工作、云计算部门 PaaS 平台架构师、电商交易平台高级资深工程师;2014 年加盟唯品会,作为唯品会电商平台应用架构负责人,负责唯品会电商平台应用架构管理工作,主持公司架构评审运作;主持多个公司战略级项目的架构设计和支持工作;唯品会核心系统重构总架构师。

eBay 和唯品会的电商文化缩影

eBay 是一个老牌的互联网公司,是曾经全球最大的交易平台。我有幸在 eBay 中国研发中心工作接近 10 年,曾经在支付平台、电商平台、云平台等不同开发部门工作过。eBay 电商系统设计非常复杂,将系统划分为一个个小模块,每个团队和其中每个人负责一个产品的一个小模块。这种系统结构经过长期演练已经非常严谨、成熟和稳定。

eBay 集中了很多优秀人才,通过参与其中,我个人收获很大,但由于每个人所负责的模块相对来说比较小,所做的变化被限制在所负责的模块中,很难参与到其它系统模块,而且系统改造比较保守,更追求稳定。再加上地域的原因,中国员工很难参与到核心模块当中。久而久之,对个人发展来说就产生了障碍。

相比 eBay,唯品会有了很大空间,正好碰到公司大发展阶段,业务发展非常快,但电商系统远远跟不上业务发展需要,系统面临大改造。正好把自己以前在 eBay 的工作经验发挥出来,我和我的团队深入到各个业务团队中,为公司大项目提供架构设计方案。在一年左右的时间内,主导并完成了公司电商核心系统的架构设计,随后也支持开发团队完成了大部分实施。同时组建公司架构评审委员会,对公司关键项目进行评审。

谈到两个公司的文化,eBay 有着大公司的一切特征,公司有比较严谨的组织和计划,但应对变化的节奏较慢,技术上偏保守,追求稳定,所以一些新技术的采用上比较滞后;而唯品会的特点就是小、快、灵,运营和技术方面随着市场的高速发展快速调整。两年多以前,唯品会的主流系统全部采用 PHP,而现在我们绝多大多数系统已经升级到自己研发的基础框架,比如服务化框架 OSP、日志监控 Mercury、配置中心等,这些新的框架已经适用到唯品会的核心系统中,效果非常好。

不能继续做黑匣架构

我非常荣幸能参与到公司的架构评审中去,在没有架构评审之前,我们好多项目甚至没有设计文档,开发人员甚至不清楚怎么做架构设计,开发人员只考虑自己的模块,注重快速上线,满足当前项目的短期需求,不考虑系统的集成和将来扩展性,系统变成了黑匣子。

我们制定了架构评审流程,抽调各个部门的专家到评审委员会,优化了项目管理流程,把架构评审作为项目开发的一个必需环节。

对于是否做架构评审,我们通常有个筛选标准:看看项目是否对主流程产生影响,考虑到一些关键性的修改对项目的影响,我们有以下几个比较主要的关注点:

  • 设计是否满足系统需求,包括功能、性能、兼容性、可靠性等;
  • 涉及新技术基础组件的引入;
  • 核心业务流程变动;
  • 与核心业务系统交互变动;
  • 与外部系统交互变动;
  • 主要系统的边界划分;
  • 是否符合公司制定的架构标准规范;
  • 是否符合安全规范;
  • 脱离实际情况的容量规划;
  • 是否涉及系统重复建设问题

架构的变动是针对长远的规划,为了更好支持业务,我们需要采取渐进的发展策略,不能为了一个理想的模型而牺牲整个系统的开发进程,我们只能在一定的时间范围以内做一些优化设计,使我们的系统尽量变得有序。

对于战略级项目而言,我们需要一个比较长远的思考,在需求阶段就介入架构设计,充分调研需求,了解上下文,在整个系统基础上统筹思考,要尽量考虑未来变化,使我们的系统有比较好的扩展性。

架构评审非常难,难在如何平衡,既不能影响业务系统的正常开发,又需要考虑架构的有序性,需要花大量时间,刚开始我自己花了很多时间来研究进来的新项目,我们每周都有大量的新项目出现,这些新项目在开始阶段缺乏相应的文档,给筛选工作带来很大的难度,这就需要我们架构师对系统非常了解。

至于筛选完成后,需要秘书长协助发出通知,安排评审日期,有时候还需要根据项目上线调整日程或者单独安排评审。后来我们应用架构团队参与进来形成值班制度,减少了对专人的依赖,能够保证正常的运作。如今架构评审已经运转 2 年多,提前发现了大量的问题,预防了一些比较严重的事故发生。

Venus 架构以及核心

Venus 是唯品会自主开发的一整套软件开发框架和体系,覆盖了软件的开发、测试、发布、运维四个阶段的一整套框架、中间件、平台的集合,主要由应用框架、服务框架、服务网关、服务安全中心、数据服务平台、配置中心、消息中心、定时任务平台、自动化测试平台、CI/CD 平台、监控平台等组成,着力于提升效率, 降低技术难度, 推进标准化, 提升应用的性能和可用性,整个设计框架如下图。

简单谈谈 Venus 中的开放服务平台(OSP)和 Mercury 的功能,OSP的主要目标是提供服务化的核心远程调用机制。

  1. 契约化的服务接口保证系统间的解耦清晰、干净;
  2. 基于 Thrift 的通信和协议层确保系统的高性能;
  3. 服务可以自动注册并被发现,易于部署;
  4. 配合配置中心,服务配置可以动态更新;
  5. 客户端与治理逻辑的分离使服务接入得到极大简化;

除此之外,OSP 提供了丰富的服务治理能力,如路由、负载均衡、服务保护和优雅降级等,通过 OSP,有效的实现了流量控制。OSP Proxy 具有软负载的作用,系统不需要硬件负载均衡,可以将服务请求均衡地分配到不同服务主机上。另外 OSP 还可以配置服务的路由,服务请求可以被分配到不同版本的服务中处理,这样很容易实现灰度发布。

Mercury是唯品会开发的一款分布式日志跟踪系统,它主要分为 3 部分功能,调用链跟踪,监控报警和问题定位,可以给运维和开发人员定位提供信息。可以帮助理解系统行为、用于分析性能问题的工具,该系统让开发者可通过一个 Web 前端轻松的收集和分析数据,例如用户每次请求服务的处理时间等,可方便的监测系统中存在的瓶颈。

开源 VS 自研

在公司开始研发这些基础框架之前,我们也做了大量调研,市场上的确存在一些开源框架,我们调研后发现这些框架存在一些兼容性问题,无法和现有唯品会系统无缝对接,总结一下难处:

  • 如果要对接的话,还是需要大量工作;
  • 开源方案存在以后持续发展问题,我们的基础架构本身就是一个体系工程,如何把这些独立存在的开源框架集成在一起?
  • 出现问题如何快速解决,是等开源下个版本,还是自己解决?

基于上述这些原因,我们决定自己研发,由于人力有限,我们先从服务化框架开始,研发 OSP,参考了业界比较成熟的方案,Thrift/Protocol buffers/Dubbo 等,最后方案采用 RPC 方式,基于 Thrift 的通信协议,软负载方式,通过服务自动注册和发现机制,引入本地和远程服务代理,通过长连接方式,大大提高了服务的连接速度。通过在我们系统中的实践,系统性能和可靠性得到大幅度提高,目前我们已经准备开源我们的服务框架。

有了统一的基础框架体系,通过代码生成工具生成大量代码,开发人员大部分工作只需要关注和业务系统相关的业务逻辑代码,开发效率得到了比较大的提高,通过 OSP 服务框架设计服务,数据层有效封装了数据访问,开发人员不需要关注分库分表、读写分离。OSP 特有的本地和远程代理机制保证了服务请求的快速处理。

唯品会的革命性重构

之前唯品会的绝大部分系统基于 PHP,PHP 开发简单,但最大的问题就是性能低下,另外 PHP 也缺乏体系框架的优化支持,各个团队使用不同的版本,开发和维护成本都比较高,概述这阶段我们出现过的问题:

  • 业务系统迫于业务高速发展压力,大部分改动都是为了应付短期需求,系统已经变得相当脆弱,不仅性能低下,浪费大量硬件资源。
  • 业务流程混乱,系统间交互逻辑过于复杂,系统间耦合严重,系统边界不清,简单事情复杂化,导致大量的业务分支,无法管理,业务逻辑。
  • 一个很小的改动就需要多个系统一起变更,系统集成困难,服务颗粒度过大,复用性和扩展性比较差,数据库定义不严谨,数据冗余严重,大量系统间复制,导致系统间数据不一致性频繁发生。
  • 系统维护效率低,运维成本高,故障率高。

那我们如何改造呢?

  1. 我们的重构首先基于 Venus 技术体系,需要 Venus 高性能、高可靠的系统支持;
  2. 针对业务系统的特点,对业务进行了深入分析,通过业务建模,根据业务对象的特有特征对系统做了重新拆分改造,重新系统处理业务的流程,简化系统间的交互,保证系统间的数据传递质量。定义业务的边界和交互方式,优化业务流程。以支撑未来系统为目标,支持更多的业务模式;
  3. 服务分层,业务下沉,提高服务的复用性。针对不同层次不同场景不同粒度的访问需要,将服务划分为流程层、聚合层和基础层,层与层之间相互独立,又相互依赖,通过上层对下层的依赖和调用组成一个完整的系统;
  4. 服务化解耦,采用 OSP 服务化体系,基于服务间松耦合、服务内部紧耦合的原则,在考虑系统扩展性、不同模块不同职责基础上将系统划分为若干个独立的服务,服务独立部署。全面服务化,更好的支持服务治理,服务路由,服务降级和服务限流;
  5. 系统间通讯增加异步处理,减少同步处理。对一些执行时间比较长的任务而又不需要得到立即响应的案例,通过异步消息机制,通知消息监听者,既提高了系统的响应速度,又改善了系统间的紧耦合情况。分布式异步消息队列服务器可在宕机后确保消息不丢失,从而提高系统可用性、健壮性和扩展性;
  6. 统一框架整合定时任务,时间敏感的通过异步消息机制;
  7. 优化数据访问,统一数据共享标准,明确各数据共享场景及规范,访问量大的数据库做读写分离,数据库有能力支撑时,尽量不要引入缓存,合理利用缓存提高访问性能,访问量和数据量都很大的数据库,通过数据库分库分表,不同业务域数据库做分区隔离,重要数据配置备库;
  8. 接入监控系统,通过系统监控和业务监控发现系统运行问题。

系统的改造力度比较大,重构团队面临巨大压力,通过日夜奋战,完成核心服务设计开发。但又面临新的问题:怎么确保新设计的系统没有问题,如何在不影响系统运行的情况下接入流量?

我们采取渐进的策略,从简单的模块入手逐步接入流量,验证没有问题的情况下,扩展到更多模块,通过这种方式可以尽早发现问题,及时纠正,把影响降到最低。在初期,发现单机 QPS 并没有想象的好,通过分析发现,我们的日志系统产生过多日志,占用了过多系统 IO 资源。有些正常的服务请求被误认为异常,比如返回空数。

闪购模式下如何解决超卖

在电商系统中,超卖问题几乎无法避免。

一方面是技术原因,在高并发情况下,用户涌入抢购,每次抢购请求都需要减库存,一般在减库存前,对库存数做一次查询再做减的动作,多线程情况下数据库无法保证数据库库存为 0 判断。如果贸然采取数据库行锁,性能必然引起比较大的下降。

在库存很宽裕的情况下,不存在超卖,当库存接近 0 的时候,容易出现超卖。所以我们的解决方案就是后台有一个定时任务,当库存降低到一个阈值时,根据当前 SKU 剩余库存和订单或者购物车占用的库存数量,重新纠正剩余库存,在这种情况下,虽然无法防止超卖,但可以降低超卖的发生。

另外一方面超卖是管理流程原因,除了前端售卖,后端库存变化有很多因素,如仓储入库、盘盈、盘亏等环节,需要对库存变化环节流程梳理,加强系统间对账环节,改善系统间数据不一致性发生频率。

挑战大促

唯品会作为一个特卖电商系统,其闪购限时特卖业务特点决定了网站随时都需要处理高并发、大流量的用户请求。大量买家在每次新的品牌档期上线后,大量涌入,抢购商品,造成网站承担大量流量。尤其碰到热门商品,网站并发访问剧增,会造成整个网站负载过重,响应延迟,严重时甚至会出现服务宕机的情况。双 11 销量则是平时的 10 倍以上,将会涌入大量请求,导致资源紧张,我们必须对所需资源做出评估,寻找现有系统存在的瓶颈,并对各个系统作出方案:

  1. 首先根据引流情况和预计交易量评估各个业务线可能产生的流量。
  2. 对现有各个业务系统的能力做出正确评估,通过线上线下压测,确定单台应用服务器和数据库服务器的能力。
  3. 然后计算出所需的应用服务器、数据库服务器及其网络资源等,进行扩容。
  4. 重点模块进行预演,发现问题及时整改。
  5. 同时为了确保主业务流程的正常运转,在流量过大或者出现异常情况下,可通过限流、降级等手段,通过自动熔断机制,关闭主流程以外的的服务。
  6. 做好防刷预案,设置各种维度规则判断,剔除恶意请求。
  7. 另外监控系统非常重要,通过系统监控和业务监控两个层面,及时发现问题并作出对应措施。
  8. 为了保证大促前系统稳定,限制代码版本的更迭,保证线上环境的稳定。

以我们的降级策略为例,电商系统为了保证用户体验,在资源有限的条件下,我们必须保证关键系统的稳定性。通过对不同业务级别定义不同的降级策略,对除核心主流程以外的功能,根据系统压力情况进行有策略的关闭,从而达到服务降级的目的。

  • 主流程系统包括用户注册、登陆、商品列表、商品浏览、购物车、结算、促销、支付、订单等,同样是商品浏览,我们需要保证在线商品信息始终可以访问,而对于下线的商品信息,我们可以容许在访问容量受限情况下,容许关闭某些页面的访问等;
  • 另外对于由于服务降级导致的有损服务,通过异步补偿机制实现。

在分布式环境下保证数据一致性

一般情况下,我们根据应用的需要,将系统间一些公共数据独立出来,作为独立服务模块维护,系统可以调用独立服务去读写这个独立数据,这种情况下就避免了不一致性。例如电商系统中各个子系统都需要商品数据,我们有了独立的商品服务系统,通过访问商品服务,应用系统可通过访问商品服务获取商品信息。在有些场景下,为了业务需要,需要系统间数据冗余,在一个系统保留另外一个系统的数据进行相关性计算,如果这部分数据没有及时更新会导致两个系统中的冗余数据不一致,这种情况下,可根据数据相关度需要选择同步修改或者异步通知方式修改这部分数据。

举个例子,在支付系统中,涉及到卡、币、券的扣减,涉及到多个系统的交互。在一笔交易中,如果出现其中一个步骤失败,就需要进入退款流程,回滚多个系统的数据。涉及金额运算实时性和准确性较高,接口都支持幂等,如果使用同步调用,将会出现性能问题。一个比较有效的解决方法就是采用异步消息通知机制,实现系统最终一致性。当出现某个环节失败,发送异步消息,每个相关系统收到消息通知后,做相关退款操作。并采用定时对账机制,保证各个系统之间数据最终一致性。

再一次回头看重构

首先我们架构设计源于业务驱动,我们在做设计之前必须充分了解业务,不了解业务的设计,即使再高明的技术,也没有任何价值,架构无从谈起。我们的设计必须支持现有业务,同时要考虑系统的开放性,易于扩展,同时系统的性能和高可靠性非常关键。最后一点非常重要:架构必须简单,易于维护,我们的系统不是一个个独立的艺术品,我们要考虑整个体系的运维,架构师要综合考虑开发、维护、运维成本。

大家都在谈重构,重构其实有不同纬度,有开发人员经常都在做的简单代码重构,有小团队内部做的模块重构,架构重构则不同,是对业务模块进行重新整合基础上对系统做全面整改,会对关联系统有影响,所以要比较谨慎,多个模块一起参与,制定比较严密的计划,否则做不到真正的架构重构。

通过参与唯品会核心系统重构,感触良多,如对选购线、订单、结算、购物车、促销、支付等模块做整改,团队非常辛苦,深入到业务团队中做调研,整理业务流程,无数的设计会议讨论,设计评审,接入讨论和生产环境监控,修复紧急问题等等,每个环节都很重要。

作为架构师,是产品、开发、测试、运维等环节中重要枢纽,设计方案需要充分考虑各方面因素。在设计评审会议中,我们也发现了一些模块存在过度设计问题,导致系统过于复杂。在设计中,也面临一些艰难选择,我们的设计是为了未来的业务发展,未来很多情况不明朗,如果过于理想化,对现有业务会有比较大的冲击,也会影响实施进程,在业务压力的基础上,为此做了很多折衷方案。

在充分考虑未来系统发展基础上,大模块进行划分,模块内部则着眼于现实,先确定大系统间交互逻辑,未来模块内部可以进一步重构而不影响其它业务模块。在实施策略上,采取渐进的策略,分阶段实施。

我相信只要我们架构师坚持自己的信念,策略得当,我们的重构一定会达到预期目标,我们会为将唯品会做大做强持续努力,也希望通过本次分享给大家带来一定收获。

2016 年 11 月 10 日 21:483771

评论

发布
暂无评论
发现更多内容

最完整的PyTorch数据科学家指南(2)

计算机与AI

学习 PyTorch

设计模式第三周总结「架构师训练营第 1 期」

天天向善

Week 3 Assignment

Yinan

Mongodb异常关闭,再次启动报错

MySQL从删库到跑路

mongodb

【读书笔记二】《企业IT架构转型之道-阿里巴巴中台战略思想与架构实战》

Man

中台 分布式 研发管理

极客时间 - 架构师一期 - 第三周作业

_

极客大学架构师训练营 第三周

架构师训练营第 1 期 week2

张建亮

极客大学架构师训练营

架构师训练营第 1 期 week2 总结

张建亮

极客大学架构师训练营

Linux忘记root密码怎么办

MySQL从删库到跑路

Linux 服务器 root密码 root

架构师训练营 1 期第 3 周:代码重构 - 总结

piercebn

极客大学架构师训练营

第三周总结

_

极客大学架构师训练营 第三周总结

架構師訓練營第 1 期 - 第 02 周作業

Panda

架構師訓練營第 1 期

设计模式第三周作业「架构师训练营第 1 期」

天天向善

单例模式 组合模式

第三周作业

icydolphin

极客大学架构师训练营

week03

……

架构师训练营第一期 - 第三周课后 - 作业二

极客大学架构师训练营

架构师训练营—第三周学习总结

Geek_shu1988

架构师训练营第 1 期 - 第三周学习总结

Anyou Liu

极客大学架构师训练营

架构师训练营第三周命题作业

成长者

极客大学架构师训练营

架构师训练营第三周学习总结

成长者

极客大学架构师训练营

LeetCode题解:242. 有效的字母异位词,数组计数,JavaScript,详细注释

Lee Chen

LeetCode 前端进阶训练营

spring-boot-route(二)读取配置文件的几种方式

Java旅途

Java Spring Boot

为什么go中的receiver name不推荐使用this或者self

新世界杂货铺

go 后端

架构师训练营第 1 期 -- 第三周学习总结

发酵的死神

极客大学架构师训练营

架構師訓練營第 1 期 - 第 02 周總結

Panda

架構師訓練營第 1 期

架构师训练营—第三周作业

Geek_shu1988

架构师训练营第 1 期 -- 第三周作业

发酵的死神

极客大学架构师训练营

集中日志系统ELK

青乡之b

ELK

Golang单例模式手写稿

Jacky.Chen

Springboot 邮件任务

hepingfly

springboot 发送邮件

Ui Automator 框架和Ui Automator Viewer你会用吗?附送「必备adb命令」拿走不谢 !

清菡

android

唯品会11.11:频繁黑匣架构背景下,看唯品会的革命性重构-InfoQ