2013年6月30日星期日

挑战空中加油——1号店B2C电商系统演进之路

一、 面临的挑战

  • 发展速度快:每年近十倍的业务增长。既要及时响应业务发展的新需求,又要对系统平滑地进行技术升级重构,其难度堪比飞机空中加油。
  • 系统越来越复杂:
    1. 多渠道: 1号店、掌上商城、第三方商城、多个分站
    2. 产品多样: 既有各类百货,也有各种虚拟票劵,还有机票充值等
    3. 多种经营模式: 有自营也有代售,有自配送也有第三方配送
    4. 多部门协同:采购、市场、销售、仓储、配送、客服等多部门需高效协同。
    5. 更智能化:随着业务和系统的复杂,各部门完全人工的运营决策变得十分低效,需要系统更加智能化。
  • 流量越来越大:流量每年增长10倍。

二、演进的策略

  • 新旧兼容,平滑升级:我们在实施任何技术升级时,首要强调的就是新系统要尽量兼容旧系统,以支持平滑升级。完全新的架构看似很吸引人,比较容易开发,但是不兼容原来的系统可能使得新系统风险非常大。所以1号店采用兼容的方式,滚动开发,把架构从原来的合成一团的小系统方式逐步改变为子系统独立,多数据库的模式。这好比一边高速飞行,一边加油。
  • 系统拆分:解决系统复杂性的唯一方法是分解。同时拆分后系统也使各自进行新的技术改造变得相对平滑。我们从终端渠道、业务逻辑、使用部门等多个维度进行了系统拆分。 例如:

    相关厂商内容

  • 适当放弃一致性:在一些实时性要求不高的场合,我们适当放弃一致性要求。这样就可以充分利用多种手段来提高系统吞吐量,例如页面缓存、分布式数据缓存、数据库读写分离、查询数据搜索索引化。
  • 系统共用组件Service化:随着系统的分拆,各子系统间的重复代码越来越多,增加了很多不必要的维护成本。为提高代码的复用,降低维护成本,我们将多个子系统都需要用到的业务组件,进行了Service化。例如:
  • 中间件的研发使用:随着子系统和Service的增多,面临很多共同的技术性挑战。为降低应用系统人员的开发难度,我们在一些开源系统的基础上,研发了多个适应我们的中间件系统。例如:ESB平台(支持远程服务、异步消息)、分布式缓存、数据访问层等。大大简化了应用系统的开发。使用中间件后的总体示意图如下:
  • 系统自动化:为进一步提高运营效率,我们采用了很多自动化的设计理念,尽量减少人工配置的需要。例如:根据比价系统和策略自动调整商品价格,根据投放效果和策略自动调整广告投放,根据库存和销售预测自动安排采购等。

三、 经典案例:数据访问层YhdDAL 1.0设计与实施

1) 实施前旧系统状态与结构:
  • 整体已拆分有多个子系统和多个数据库。
  • 每个应用系统与多个数据库直连,使用iBatis进行O/R Mapping。
  • 各应用系统代码中,使用MemCached服务器进行数据缓存。
2) 旧系统面临的问题:
  • 数据库连接数大,数据库服务器负担重。
  • Cache代码繁琐: 本来一行代码的事需要写四五行。下面是典型的示例伪代码:
    result = getFromMemCached(key);
    if (result != null) return result;
    result = getFromDatabase(...);
    writeMemCached(key, timeout);
    return result;
    
  • Cache 管理困难: 失效时间代码分散,不便管理。不同系统还容易key冲突。
  • 数据分库和读写分离代码繁琐,某库故障时无法自动切换到可用库上。
  • 系统Service化改造困难: 原Web层与Dao层代码耦合度高,不易实现Service化。拆分粒度太细连接数上升和故障概率增加,粒度太粗又效果不佳。
  • 无法充分发挥Cache潜能:某个Cache项失效时,因前端的并发性会导致多次数据库请求。到期Cache无法延期使用,数据库故障时应用系统就立即故障。
3) 备选方案的优缺点:
  • 基于iBatis本地扩展: 优点是工作量小。缺点是连接数和Service化等问题无法很好解决。
  • 基于jdbc驱动的DAL:优点是客户端代码兼容性好,原代码改动工作量小。缺点是实现一个完整的jdbc驱动本身代价很高。
  • DAL服务化,接口自定义: 优点是代码可控性高,各项需求特性易实现。缺点是有一定的迁移成本。
4) YhdDAL 1.0设计方案:
  • 引入DAL服务器。应用系统仅访问DAL服务器,DAL才连接数据库。
  • 客户端与DAL采用成熟的远程调用协议,接口简化为一个:
  • Object execute(functionName, parameters...)
  • 参数和返回值一律采用Map或基本类型,必要时可在客户端转换为Java Bean
  • 支持iBatis格式的sql脚本定义和字段映射,以降低iBatis代码迁移成本。
  • DAL层支持JavaScript脚本语言编写的类似存储过程的计算代码,简化业务层数据处理代码实现。
  • 因DAL服务器数量明显小于应用服务器数量,可有效减少数据库连接总数。
  • DAL层封装Cache处理机制,一方面简化应用层代码。另一方面可采取缓存主动更新,失效延期等机制,充分发挥Cache潜能。
  • DAL层封装分库和读写分离处理机制,简化应用层代码。
  • 参考数据库存储过程的设计思想,客户端仅传入过程名称和参数,DAL层计算完成后返回结果。有利于实现Service化。
  • 大量简化应用层代码,很多简单的Sevice/Dao类可以完全省略。
  • 1.0的缓存和负载均衡采用相对成熟简单方案,以降低风险。结构图如下:
5) 实施与升级过渡步骤:
  • 实现DAL核心功能。进行压力测试,评估系统性能。
  • 先对一个风险相对较小的应用系统进行改造,评估系统稳定性和改造成本。
  • 采用iBatis兼容模式,直接转换迁移旧代码。以快速将原有系统升级至新平台。
  • 采用新平台的设计理念,如读写分离、Service化,进行旧代码改造。
  • 扩展DAL附加功能,如日志记录、权限控制、缓存优化、连接分组等。
通过一系列的方法与手段,1号店基本实现了从小系统向大系统演变,使得1号店的系统能够支撑大量高并发的访问,同时满足了业务的需要。

关于作者

韩军,1967年出生。1989年毕业于上海交通大学计算机系, Monash MBA
  • 1号店的第一个员工,CTO, 设计并开发了1号店所有的系统
  • 2004年,设计了 美国著名的比较购物系统 smarter.com
  • 1999年加入51job,设计并开发了中国最为成功的工作网站与系统

没有评论:

发表评论