写点什么

全流程重构京东服务市场系统

2018 年 1 月 09 日

京东服务市场(fw.jd.com)是为第三方软件服务商和京东商家提供服务的交易平台。京东服务市场是一个业务极度复杂的系统,在业务上涵盖了服务类商品、促销、计费、订购、订单、支付、结算、退款和发票等逻辑,几乎涉及到电商的所有元素。

京东服务市场架构

如上图所示,商家订购服务从单品页进入,然后查询很多信息,如价格、评价。在商家点击立即订购之前,商家会不停地对比各类服务,从前端到后端所有的服务基本上都会刷新,那么,每一次刷新,调用服务就会承受一次服务的调用。当订单量增加一倍的时候,实际服务访问量最少是 10 倍。

那么为了应对如此大的调用量,每年的 618、双 11,京东服务市场又做了什么?下面我们会讲讲 618、双 11 备战后面,系统进行重构,都从哪些方面进行了优化,去提高了系统的容灾性,提高了系统应对峰值流量的能力。

首先要整理自己的重构思路,大致整理为几个阶段:梳理薄弱点、系统改造、上线和复盘

详细来说,重构开始要对系统做一次全面的梳理诊断,其目的就是要找到系统薄弱点。而梳理方法可以从系统部署耦合、UMP 报警 & 日志、慢 SQL & 外部依赖等不同层面作为切入点进行。

在系统改造阶段,通过对系统薄弱点的梳理,进行系统架构改造方案的设计,利用二八原理,集中精力到最重要的环节,即黄金流程的优化,最后制定计划排期。当然,我们可以利用敏捷的思想,小步快跑,持续优化改进。

上线就是临门一脚,台上一分钟,台下十年功。为了保证上线成功,要进行充分的上线筹备。首先要整理上线计划和切流方案,其中包括分阶段上线、灰度上线等。计划准备好之后就要进行反复的压测和演练,包括极端情况的降级和预案的启用等等。经验来讲,大多数上线失败,反复回滚的案例,大多一无计划,二无预案。

即使进行了周密的上线筹备,上线仍然可能出现意想不到的问题,所以我们要对每一次上线进行复盘总结,从教训中成长,并总结出快速定位问题的技能,以及提升工具使用的能力。

我们以此为思路,通过京东服务市场进行逐一介绍。

一、梳理薄弱点

找出薄弱点的方法有很多,服务市场作为一个前台系统,我们从最影响用户感知和体验的角度进行梳理。

二、系统改造

通过梳理系统薄弱点,甄别出确认的改造点。

1. UMP 监控报警 & 日志

我们常说,研发人员有两只眼睛,一只是监控报警,另一只就是日志,所以无论什么情况监控报警日志一定不能少。通过采用 AOP 的方式,对工程所有层进行统一切面添加监控报警和日志。

特别要说的是,设置了报警一定要即时处理和优化,无论是性能报警还是可用率报警,需专人跟进推动优化,如果改动量很大或风险很高,可调整报警阈值备注后期优化,警醒狼来了的情况。

2. 确认大流量页面超时时间

超时时间的设置采用少超时、多重试,这实际上是一种快速失败策略,如第三方接口调用超时,如果设置过长,在访问量大的时候,就会导致请求线程积压、CPU 飙高等问题。

超时设置一是全面检查 MySQL、JimDB、JSF 等 RPC 调用的超时设置,尤其是大流量入口的调用链路,二是根据压测结果结合具体业务场景进行设置调整。

3. 解决慢 SQL 问题

慢 SQL 问题大多数情况下都是没有索引或者索引使用错误引起的,如索引字段是 varchar 类型,但是程序中请求 DB 的时候传的是 long 类型,造成索引失效。

首先通过 DBA 找出慢 SQL,其中重点关注调用次数高和响应速度慢的 SQL,通过 Query ID 找到对应的 SQL,然后通过 EXPLAIN 执行计划查看 SQL 命中的索引。添加索引一定要结合 MySQL 执行计划来判断,同时添加 Index 要注意区分度,区分度 =count(Distinct 索引值)/总条数,区分度越接近 1,说明区分度越高,查询的时候就会过滤掉更多的行数据。如果某些 SQL 操作有大量的 JOIN 操作,就要想办法拆分 SQL,修改代码逻辑,这也是一种平衡的过程。

4. 降级开关

降级开关可以防止问题发生的时候准备好的功能不可用,以下图 Solr 降级开关为例,当 so 出问题时,我们可以关闭 so 的写逻辑,sa 和 sb 不影响继续写,同时将读逻辑切换到 sa,做到平滑切换。当 so 恢复之后,开启 so 的写逻辑,将读逻辑开关切换到 so,也能做到平滑恢复。当然,要注意 so 故障时段可能出现的数据不一致问题。

5. 读写分离 + 多级缓存策略

缓存策略可以有效防止请求直达数据库,造成数据库压力大的问题。本次重构采用的缓存策略是 JVM+JimDB+DB,缓存的数据主要是列表页 / 频道页和单品页的服务类目和服务信息。在启动缓存策略的过程中,也要考虑缓存的穿透率,以此来调整缓存最优的过期时间。

不仅如此,我们还要将缓存 JimDB 中间件的不稳定因素的考虑放到备案中,如多机房的部署采用几主几从,主从之间是否支持自动切换等等。

服务信息多级缓存策略架构

在使用 JimDB 缓存时:

  • 要注意大 Key 问题,否则量一上来很容易引起缓存集群的单片热点问题,如服务信息可以根据 SpuId 的纬度来设置 Key,但缓存服务信息会造成实时价格延迟,可以通过数据异构的方式同步价格数据。
  • 要注意缓存过期的问题,不建议使用 JimDB 的过期设置,而是自定义 timestamp 由应用程序判断是否过期,这样可以在 DB 宕机不确定恢复时间的情况下,仍能从缓存获取数据。
  • 对于那些“尺寸较小”、“高频的读取操作”、“变更操作较少”的数据应全部由 JimDB 来抗量,如服务类目,每个类目 ID 作为缓存 Key,可以通过双写或数据异构的方式。

6. Solr 灾备策略(列表页 / 频道页)

Solr 的使用主要服务于搜索和列表页多维度的检索,但是 Solr 集群情况非常不乐观,如果 Solr 宕机,不仅搜索不可用,更糟糕的是服务市场列表页完全不可用,所以对 Solr 的灾备成为当务之急。

当然 Solr 的灾备策略可以参考服务类目和服务信息的多级缓存策略,但是列表页可能涉及到的热点问题和分页逻辑都使问题变得更加复杂。其实 Solr 的最优替换方案应该是 ES,但一方面限于资源问题,另一方面原检索逻辑复杂,改造限于时间条件,又可能风险极大,所以主要考虑用 DB+JimDB 进行容灾。

如果用 Solr 搜索切 DB&JimDB 拖底,如果 Solr 降级 DB,那 DB 是否有足够的抗压能力支持多维度的检索?无论怎么想,这都不是一个好主意,而且经验告诉我们,DB 就不是用来抗量的。那如果 Solr 降级 JimDB,如何针对多维度检索设计 JimDB 的 Key?过多的 Key 不仅会产生大量的数据,还会有相当的成本保证数据一致性,所以 JimDB 拖底作为一个过渡方案,当 Solr 降级 JimDB 时,同时也进行了降纬,只保证通常检索方式。

综上,虽然 Sorl 可以降级 JimDB,但 Solr 的单机问题是必须解决的问题,所以 Solr 集群部署采用二主一备的灾备架构,当廊坊机房 Solr 主 s0 或马驹桥的 Solr 主 S1 出问题,可以切换 Solr 备,如果此过程中,Solr 备直接被流量击垮,则直接降级切换对应机房的 Jimdb 从,如果还是扛不住,就启动静态页拖底。

7. 首页分流加载

官网首页是一个网站的门户,如果首页进不去,那作为一个交易平台更不能进入列表页、单品页或结算页了,所以特别需要注意首页的加载性能和开天窗的问题,也正基于此,对首页的加载采用异步分流加载,不同的区域调用不同的请求,不同的请求数据又相互隔离,并通过分流加载提升加载速度,同时不把鸡蛋都放在一个篮子里,保证页面的容灾和降级。

8. 单品页加载优化

分流加载的思想也可以应用在单品页中,以保证可细粒度地降级。单品页的特殊性在于实时价格,直接采用缓存可能会造成价格延迟,导致在单品页看到的价格与结算页不一致,所以对单品页添加缓存时处理实时价格需要进行双写操作,以此保证单品页价格的实时性。

发布服务更新价格,写 MySQL,通过异步任务更新主 JimDB 价格数据。服务信息读取主 JimDB 中价格,无过期则直接返回,过期或未命中则访问主 MySQL,获取最新数据返回用户,同时异步更新主 JimDB 价格。

三、上线

1. 压测

通过梳理系统薄弱点并进行系统改造部署上线之后,我们就要对线上真正能承载能力进行压测,通过压测知道系统的极限值是多大,当系统承受不住访问时,就会再暴露出瓶颈,如服务器 CPU、数据库、内存、响应速度等,从而促使我们再进行优化。线上压测是在凌晨一两点,从线上剥离出一小部分集群,所有服务器和配置使用的都是线上真实的场景进行压测,压测场景分为读业务和写业务。

首先,我们进行了两次压测,在未优化前进行了一次压测,通过对压测结果的分析,看看系统瓶颈主要出现在哪里。第一次压测结果发现大量请求穿透直接调用 DB,造成 DB 的性能急剧下降,数据库服务器的 CPU 多次飙高,这成为我们重构优化的重点,优化慢 SQL,进行数据库读写分离,添加多级缓存,优化系统调用等。

根据第一次压测结果结果进行优化后,第二次压测性能有了很大的提升。

2. 演练

在压测演练过程中,也暴露出很多问题,如数据配置错误未校验、服务器内存未调整、使用新扩容机器压测等,这导致出现了一连串的问题。压测开始服务器 CPU90%,数据库无任何响应,因为数据库配置错误导致服务器根本没有连接到数据库。服务器内存 1G 造成频繁 Full GC,性能总是提升不上去。新服务器造成很多配置未同步、权限未申请,花费很多时间解决,影响压测主流程。

3. 预案

预案的执行包括发现问题、定位问题和解决问题。发现问题要结合软硬件问题,定位问题包括监控报警和日志分析,这就要看之前添加监控的粒度和日志是否打的有用,最后就是解决问题。

系统上线之后,系统性能负载也略有飘高,UMP 报警也接踵而至,通过监控和日志迅速排查线上隐患和风险,供不同程度启用降级预案。

四、复盘

服务市场这次系统重构还是非常顺利的。而在整个过程中也暴露出了很多问题,有一点是上述没有提到的,那就是心理因素的培训。如在压测演练时,前期由于遇到各种问题导致结果迟迟不能到达预期效果,整体团队开始出现急躁,处理操作开始变形,出现质疑声音进行自我否定等,还好后期即时调整,过程逐渐进入正轨,大家开始慢慢恢复常态。

所以,真正上线前我们就开始进行了小复盘,针对心理心态进行了调整和培训,并完善了预案等内容。在上线时出现的问题,团队保持很好的心态处理线上的问题,而整个系统也非常给力地稳定运行。

总结

最后,总结历次的大促所面临的技术难点,最重要的还是服务治理,因为我们要打造的不是一个系统,也不是一堆系统,而是一个平台生态,要能够持续地提高系统的运营能力。这里还是以“精打细算,大道至简”这句话结束此次京东服务市场的总结。

作者介绍

张松然,京东商城商家研发部架构师,丰富的构建高性能、高可用大规模分布式系统的研发、架构经验。2013 年加入京东,目前负责京东服务市场的系统研发工作。

感谢雨多田光对本文的审校。

2018 年 1 月 09 日 17:142072

评论

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

如何设置融云用户信息

融云 RongCloud

自定义融云会话列表 cell 选中背景

融云 RongCloud

30 分钟集成融云 IM 即时通讯

融云 RongCloud

集成融云 IMLib 时,如何实现一套类似于 IMKit 的用户信息管理机制

融云 RongCloud

融云聊天页面长按消息后“翻译”功能的实现方法

融云 RongCloud

融云 IM SDK 如何插入消息

融云 RongCloud

使用融云 IM 点击最近聊天记录时跳转到 @ 自己的消息

融云 RongCloud

如何隐藏融云输入框语音按钮

融云 RongCloud

融云 SDK 如何实现群组操作

融云 RongCloud

重磅 | 国内首款研发OKR管理工具PingCode Goals正式发布

PingCode

OKR 研发管理 研发效能 研发管理工具

大作业 1

简简单单

MMMDeFi智能合约(MDF互助)系统开发方案

薇電13242772558

智能合约 数字货币

如何利用融云 IMLib 来实现一个阅后即焚功能

融云 RongCloud

Hystrix技术专题-基础配置说明

李浩宇/Alex

Hystrix

LeetCode题解:221. 最大正方形,动态规划,JavaScript,详细注释

Lee Chen

算法 LeetCode 前端进阶训练营

跳槽涨薪必备秘籍!阿里内部面试突击手册,满满的全是精华

互联网架构师小马

Java 面试 软件开发 求职 找工作

干货分享——使用融云通讯能力库 IMLib 实现单群聊的阅读回执

融云 RongCloud

唠一唠融云的消息补偿机制

融云 RongCloud

大作业

eva

文档 用例

为融云聊天页面的输入框添加 Placeholder

融云 RongCloud

应对“角色爆炸”,PBAC 真香!

龙归科技

权限控制 管理系统 权限管理

飞桨框架2.0正式版重磅发布,一次端到端的“基础设施”革新

百度大脑

AI 分布式 框架 #百度#

还在计划转Go么,聊聊程序员的成长

架构精进之路

go 3月日更

融云的聊天页面在 iOS14 出现崩溃的解决办法

融云 RongCloud

桶排序,计数排序,基数排序

一个大红包

3月日更

浅谈自动化测试

行者AI

自动化测试

融云 IMKit 音频录制参数

融云 RongCloud

给融云的输入框上方加个功能按钮,怎么整?

融云 RongCloud

Worktile CTO :研发团队落地OKR管理经验分享

PingCode

团队管理 互联网 OKR 研发管理 研发管理工具

Java-技术专题-挖掘陷阱系列(1-10)

李浩宇/Alex

Java

使用融云 SDK 避坑指南之 iOS13 推送失败

融云 RongCloud

演讲经验交流会|ArchSummit 上海站

演讲经验交流会|ArchSummit 上海站

全流程重构京东服务市场系统-InfoQ