今天来跟大家聊聊商详。商详是展示商品详情信息的一个页面,整个购物流程比较重要的一个部分,承载着网站的绝大部分流量。为了提高转化率构成商详的元素非常丰富,有大量的图片、部分商品还有视频介绍、有相对静态的商详模板,有实时变化的价格、促销、库存。
下面我们先来看下商详上一共有哪些元素组成?
可以看到商详上的元素非常多,总结下来分为这么几个维度:商品维度
(标题、主图、规格参数、商品文描)
、分类维度、商家维度、店铺维度。另外还有一些实时性比较高的:价格、实时促销、配送地址、库存、广告等。
主要面临的挑战有:
-
高性能,商详页聚合服务比较多,要保证商详页在1-2秒内可以打开。
-
灵活性较好,可以快速响应页面变更需求。
-
具有较好的扩展性,当访问量增加的时候可以随时进行水平扩展。
-
要能够做到柔性降级,自带开关。某些底层服务出问题时可以通过开关进行相应降级处理。
商品详情页实现的三种方式
针对商详可以有几种不同的实现方式,用户看到的是同一个商详页,但背后实现的方式却多种多样,下面给大家介绍几种常见的实现。
第一种实现方式:单机版
整个网站放在一台机器上,通过几条SQL分别拿到商详需要展示的各种信息,聚合成一个大的接口吐出给前端展示。
优点:
逻辑简单。灵活性较好,可以快速响应页面变更需求。
缺点:
性能比较差,没有扩展性。
第二种实现方式:缓存银弹
在第一版的基础上增加各种维度的缓存。可以将主图、商详的HTML模板放到CDN上,每个商品的聚合信息可以放到cache中,不需要每次请求都通过DB获取商品数据。
优点:
可以一定程度上提高性能。
缺点:
需要解决缓存与DB的数据一致性问题,单纯增加缓存面临数据实时性不高,底层数据已经修改,缓存中还不是最新数据。若任何底层数据变更实时更新缓存则修改工作量较大。另外仍然没有解决扩展性问题。当请求量过大,或者商品数据过多将导致性能变差。
第三种实现方式:分布式服务化
按照领域进行切分,不同业务领域独立实现分别部署,将商详依赖的底层业务领域分别拆分出来。例如,商品、库存、促销、地址等分别进行服务化。每个子领域的服务自己保证各自的性能。
优点:
具有很好的灵活性。当有业务需求变化的时候,每个子领域内部自行修改,对外部提供的接口协议不变,对外部无感知。并且具有良好的扩展性,当请求量或者数据量比较大的时候,每个子领域都可以分别进行横向扩展。
缺点:
开发难度变大,由于按照领域进行服务划分,往往原来一行SQL可以搞定的事情。现在要涉及到多个领域一起配合来修改,需要协商接口协议,各个领域内部仍有不少开发量。
商品详情页的几个基本概念
下面我们来说几个基本的概念,上面提到的服务到底是什么?
服务:
自己自足的、无状态的业务功能,通过定义良好的标准接口,它接受一个或多个请求,返回一个或多个应答。
接口和契约
(技术层面)
影响商品详情页的不可控因素
上面介绍了分布式服务化的方式来实现商详的技术架构,那么这样是不是就完美了?在实际情况下即使这种架构还是有很多不可控的因素会影响整个商详的性能。
最重要的一个就是
对外部服务的依赖。
如果外部服务出现抖动那么整个商详页也会随之出现不稳定。尤其商详是个比较大的聚合服务,底层依赖的服务比较多,任何一个出问题都会受到影响,那么商详出错的概率就会比较大。如何解决这个问题呢?
一种方案是要求所有底层服务各自保证自己的稳定性,这需要有比较完善的监控体系,能够快速找到出问题的点,然后进行修正。这种方案属于比较理想状态,对外部有强依赖。这需要有一个比较靠谱的团队,团队中每个人负责的领域稳定性、健壮性都比较高,那么这种方式是比较好的,领域的划分比较清晰,各自的职责也比较清晰。大家各自把自己的领域做好,那么整个网站的效率都比较高。
这对整个团队要求比较高,这种团队是存在的,但是当公司业务发展比较快,团队人数增长也比较快的时候,这时候团队的质量就比较难保证了,就会出现某一个领域或者某几个领域质量不是很高,就会导致整个服务调用链都不稳定,对整体网站影响较大。那么出现这种问题时如何解决呢?
互不信任原则
对外部不信任的原则是服务自我保护最重要的方式,尽量降低外部服务出问题时对本服务的影响。对于一个写服务来说,一定要校验外部服务调用的所有参数是否合法,对每一个调用方分配场景ID记录调用来源,并且自己要做幂等去重处理。设计系统时首先想到对于这个外部调用一旦失败该如何处理?是否有相应的降级策略?对于不同调用失败的场景一定要清晰记录错误信息方便后面调试跟踪解决问题。
数据闭环
数据闭环即数据的自我管理,或者说是数据都在自己系统里维护,不依赖于任何其他系统,去依赖化;这样得到的好处就是别人抖动跟我没关系。
-
数据异构:
是数据闭环的第一步,将各个依赖系统的数据拿过来,按照自己的要求存储起来;
-
数据原子化:
数据异构的数据是原子化数据,这样未来我们可以对这些数据再加工再处理而响应变化的需求;
-
数据聚合:
将多个原子数据聚合为一个大JSON数据,这样前端展示只需要一次get,当然要考虑系统架构,比如使用Redis,Redis又是单线程系统,我们需要部署更多的Redis来支持更高的并发,另外存储的值要尽可能的小;
-
数据维度化:
对于数据应该按照维度和作用进行维度化,这样可以分离存储,进行更有效的存储和使用。
下面是一种相对比较简单的维度的划分: