按照嘉宾的要求,本文不对外公开。如果你是意外看到这篇文章,请勿将链接发送给其他人。
大家好,我是来自京东商城的邵阳,我架构的《京东全球售》“购物车系统”和“结算页系统”两个系统已经于上周五9月21号正式上线,也是我的第二件架构作品。正好借这个机会回顾总结一下,今天我主要讲一些“购物车系统”的架构内容,咱们相互探讨下。
一、网站背景
《京东全球售》是京东海外跨境电商网站,是把祖国的商品卖给全世界,类似于阿里速卖通。目前我们有三个站点分别是英文站 www.joybuy.com/ 俄语站站 www.jd.ru/ 西班牙站 www.joybuy.es 。我们的业务涉及全球上百个国家,涉及多种语言,十几个币种,所以我们的网站最基本的要求和特点就是同一套代码要支持多站点、多语言、多币种,大家可以登录 www.joybuy.com/www.jd.ru/www.joybuy.es 先体验下。
二、购物车系统特点
购物车和结算页为用户做出购物车决策必经的两个界面,也是黄金流程中的核心界面,上游承接商品详情页,下游承接收银台,承担着提升用户购买转化率指标的重要角色:
- 购物车系统承担了用户90%以上的常用操作,用户未登录和登录时,加入购物车、一键购、选中不选中、批量选中、修改数量、更改促销活动、删除、批量删除清空购物车等。
- 购物车系统展示的内容非常多,有商品信息、商家信息、价格、促销价格、总价、套装、总价促销、跨店铺总价促销、赠品、优惠券、库存、承运商、以及商品和赠品的各种展示状态下架、无库存、不可以送达等等(购物车价格一定要计算准确);同时展示的以上内容,要按照某种排序后进行分组展示。
业务上购物车系统是一个非常复杂的系统,同时又是一个直接面向全球用户的电商最核心的前端系统,所以这个系统要求就非常高:
- 稳定,就是所谓的高可用,代码层面不能出bug,及时除bug了,也要内部消化不能吐给用户,对代码质量要求高。
- 快,就是所谓的性能高,用户看到的界面展示越快越好。目前我们的核心接口一台服务器20个商品20个并发压测持续10分钟平均时间90ms。
看下目前线上的购物车界面:
购物车列表主要的展示规则
三、名词解释
- 普通商品:spu和sku概念,晨光圆珠笔有三种颜色分别是红色、白色、蓝色,该描述中有三个sku属于一组spu。购物车是sku级别,你买白色的圆珠笔和红色的圆珠笔会展示两个记录
- 套装:大于等于两个的普通商品可以组成套装,套装比单买要便宜,有节省价格。普通商品A和B和C可以组成四种套装分别是ABC、AB、AC、BC,那么购物车中就会存在普通商品A和套装ABC同时存在的场景。套装概念的引入导致购物车系统复杂度增加了一个量级。
- 总价促销:一件或者多件普通商品价格之和超过某个金额,则会有优惠。在购物车页面要把参加同一个促销活动的放到一组,一个商品可能会满足多个促销活动,用户在在购物车页可以选择其他活动,也可以不参加,更改会导致界面框架结构更改。总价促销概念的引入导致购物车系统复杂度又增加了一个量级
- 跨店铺总价促销:顾名思义多个店铺的商品参加总价促销,属于特殊的总价促销,系统复杂度再度升级
- 直降促销:一个商品满足某个价格或者买到某个件数会有优惠 优惠券:不解释,都懂
- 赠品:不解释,都懂,套装无赠品
四、总体架构 架构思想
- 用户最关心的商品名称、属性、图片和价格、促销价、总价同步输出,输出这些内容已经可以保证用户可以下单了,同时调用的jsf接口越少,可用性和性能越高。
- 大型互联网系统都有降级和熔断机制,所以对于其他其他如商家名称、优惠券、赠品库存、是否可达等接口,在大促期间是可以降级熔断的,所以把这些作为一个异步接口,异步渲染,及时报错了,用户也无感知,不影响下单。
- 购物车会因为删除、批量删除、更改促销活动或者不参加促销导致页面分组结构更改,前端js是无法进行分组的(因为太复杂),为了最好的客户体验,异步吐出源码html,然后进行渲染
具体细节如下:
五、单台服务器性能提升明显的优化点
- 购物车基础数据全部存储Redis内存数据库,使用到了redis两种数据结构,sorted set和Hash,无需序列化和反序列化开销,批量操作使用管道流pipeline,减少socke链接和传输时间
- 购物车分组很多、嵌套层级很多,如果一层层循环设置对象值,时间复杂度就是几何级增加,可以通过java8新特性stream流,把list转成外部map,内部循环直接获取赋值,两种循环结果对比10x10x10X10x10x10和10+10+10+10+10+10是1000000和60的区别List.stream().collect(Collectors.toMap())。(Java8的lamda表达式循环也很方便,但是性能有待考证,而且有个非常不好的地方是不能对局部成员变量进行赋值)
- 使用java8新特性CompletableFuture进行线程编排,多个后端jsf接口(京东自研的微服务框架,类似Dubbo)多线程编排输出,自定义线程池ThreadPoolExecutor,线程池大小压测后得到最优值。如果没有线程编排的经验,也可以使用闭锁CountDownLatch,也可达到相同的效果,但是没有CompletableFuture强大。
- Nginx页面gzip压缩级别调整
- Tomcat服务器JVM虚拟机参数调整-Xms2048m -Xmx2048m -XX:MaxPermSize=512m
- (技巧)使用ThreadLocal保存每次请求客户端信息,通过get方法获取副本
六、其他
关于影响架构外非技术性的一个很重要因素人,表达一个观点技术本身并不是最重要的,一定是工作态度。
分享结束,谢谢大家,大家先消化一下。
本文档来自支付产品技术交流群的聊天记录整理,由志愿者整理并发布到本网站。如需要及时收到来自支付产品技术交流群的最新消息,请扫码关注“凤凰牌老熊”的微信公众号。 本群面向支付行业的有经验(2年以上)的产品经理、软件工程师、架构师等,提供交流平台。如想加入本群,请在本文评论中留言(不公开),说明所在的公司、负责的工作、入群分享的主题和时间。