蘑菇街 11·11:在微信小程序做大促,技术如何支撑?

阅读数:2659 2017 年 11 月 10 日

话题:语言 & 开发架构双十一

据腾讯旗下的企鹅智酷发布的数据以及腾讯财报公布的数据,微信生态内月活跃用户数据已经超过 9 亿,在获取用户成本日益高涨的情况下,微信内的用户数据无疑是一个巨大的宝库,因此蘑菇街也一直在关注着如何更好地获取微信内的用户流量。

早在 2015 年的时候,蘑菇街其实已经在微信体系内有了对应的玩法,比如微信商城、App 分享、早期的拼团业务等等,受制于 H5 在微信生态内的体验,以及微信本身对营销类业务的管理态度,导致效果不太明显。2017 年以来,随着微信对小程序的持续投入及扶持,小程序也已经有了类似于 native 的体验,很多新的玩法结合着小程序不断推出。在这种情况之下,蘑菇街和微信共建了“蘑菇街女装”小程序(在微信 -> 钱包 -> 第三方服务内),希望在微信生态内给用户提供更好的体验。

另外,由 InfoQ 举办的ArchSummit 全球架构师峰会即将于 12 月 8-11 日北京举行,大会与阿里巴巴合作策划了双 11 架构专场,并邀请了顶级技术专家担任出品人,设置了“新一代 DevOps”、“人工智能与业务应用”、“架构升级与优化”等 17 个热门话题,目前大会日程已出,欢迎前来讨论交流。

早期蘑菇街在微信生态内的系统结构

如上图所示,从客户端过来的请求直接打在代理层,经过 Nginx 中转下发到各业务层,和 PC 架构并没有太大区别。在蘑菇街体系里由于是标准服务化拆分加前后端分离的结构,因此各个业务很容易进行业务扩展,这个技术体系延续了很长一段时间,一直持续到迁移到小程序才移除掉。在这段时间里,由于缺乏统一管理,越来越多的问题被暴露出来。

  • 难以形成统一规范

    如前面提到的,因为接入业务的团队比较多,那么势必各团队的标准不同。比如前端接入的方式不一样,有些达不到安全标准,有可能一些边缘业务会存在安全漏洞。

    各业务统计的时候,去推动系统以及业务埋点比较难,有的业务是前端埋点,有的是服务端埋点,有的埋点格式不同,有的可能少了一些埋点事件。蘑菇街内很多运营策略包括营销活动对数据依赖还是很高的,在数据缺失或者不准的情况下,会影响业务的正常开展。

  • 整体性能差,导致用户体验差

    系统接口以及页面是在微信容器内打开,用户交互需要有额外的渲染过程,这个过程会受到网络的影响,导致很多时候用户在没有等到页面完全加载完成就流失掉了。

    H5 和 native 的体验相比差了很多,特别是微信生态内涉及到免登流程,这块会和微信多次公网交互。电商体系内很多功能又必须登录以后才能使用,比如领券、下单等,直接让用户输入蘑菇街的账号密码,流失率非常高,走免登流程在弱网环境下超过 5 秒以上的情况很多,如果是在双 11 这样的大流量高并发情况之下,结果会更加糟糕。

  • 质量以及管理成本高

    没有统一的发布流程及版本控制,在开发过程中难以把控。一段需求集中期可以按照项目制的方式来管理,等项目阶段结束之后,需求管理就松散了,随着时间的发展没有人能够了解业务发生的变化。

  • 资源浪费

    各业务需要独自承接网络流量,有些业务负载很低,但为了支持业务正常运行,也提供了服务器使用。

微信小程序系统结构

上图所示为进入微信小程序后的系统结构,这是早期和微信官方合作时初步定下来的方式,因为当初定的工期(技术调研评估到测试上线 28 天)比较紧,我们在考虑如何避免原来架构的问题之后,定下来的一个方案。核心点在于,前端按照小程序的一套体系来实现,服务端基于统一的网关透出。既需要在短期内快速迁移已有业务,又需要考虑到后期能够复用蘑菇街已有的技术工具。如下为微信小程序做的一些变更。

  • 开发规范统一

    蘑菇街基于微信小程序本身的体系,同时结合蘑菇街现有的开发方式,统一开发规范,以提升复用性并降低出现问题的几率。小程序内初步形成了一些组件规范,新开发的功能都会以组件的形式落地,避免重复出现问题。

  • 流程规范化

    用来改善原本分散的开发方式,以班车流程化的方法来管理,统一收集评估需求,按照固定的发布计划发布项目,避免计划外的功能上线导致问题。发布权限收拢起来到固定人手里,由这类接口人去保证代码规范和质量。

  • 服务端基于统一网关(MWP)透出

    蘑菇街为无线端开发了一个基础平台 MWP(Mobile Wireless Platform),在此平台隔离无线请求和具体的应用服务,后续对网络层的优化都可以在统一的网络层上进行,降低实施成本。

    此外在这个网关内还提供 dsl 的功能,可以在客户端包装底层逻辑,提高客户端的灵活性。有关 MWP 的更多知识可以了解另外一篇内容

  • 全链路业务区分平台处理

    以往微信容器内的业务,和蘑菇街的主业务相互耦合,但由于是运营独立,定制化的需求又比较多,因此针对性的逻辑开发也越来越多,导致系统难以维护。

    在业务开发过程中,蘑菇街对系统进行梳理,对于全链路的系统进行逻辑拆分,以分平台的方式来开发,从商家入驻到商品招商到导购业务,再到详情、交易、促销、支付以及资金结算等等,在整体业务上定义了统一的分平台标识。不光从业务上解决了以前相互耦合的情况,后续蘑菇街继续推出的新小程序也能够低成本地接入,并且各自独立不会互相影响。系统架构上也可以做低成本的扩展,后续会详细介绍。

前端技术演进

第一版蘑菇街微信小程序上线之后,体验相比以前有了很大的改善,业务数据也持续增长。随着业务的发展,微信小程序本身的一些限制,制约了我们的业务发展。

  • 小程序包大小限制。
  • 页面层级限制。
  • 组件对于蘑菇街业务而言,复用度不高。

针对上述问题,前端在技术体系上做了一些调整,并且积累出一些工具,更加方便于和蘑菇街已有的一些系统接合。

VX

针对 vue 内的 sfc 结构组件,将 template 中的模板部分转化到小程序的 wxml,style 里的 css 部分转化为小程序的 wxss,script 里的 js 部分转化为小程序的 js。

  • 模板层比较复杂,需要对 vue 进行语法分析得到抽象语法树,然后再一一映射到小程序的语法,就是一次把字符串拆成 ast,再转化成另一种语法的字符串的过程。
  • CSS 部分主要是单位转化,把 px、rem 单位转成小程序里的 rpx。
  • js 部分是核心,虽然不需要语法上的转化,但是需要实现一套统一标准,保证在 vue 和小程序端都能跑起来,其实就是要实现一个框架。但是为了方便,我们并没有重新构造一套框架,而是将 vue 的核心部分(除了模板渲染)搬到了小程序里,模板渲染依旧使用小程序内置的。

目前 vx 已经能够实现微信小程序和 H5 代码生成,正在对接 Xcore 中。

Min

Min 是蘑菇街基于实践摸索沉淀出来的一套面向小程序组件化的解决方案。通过 Min,小程序开发者可以优雅高效地进行自定义组件的开发和使用,为开发者赋能。Min 目前提供 Min Dev(开发环境)和 MinUI(UI 组件库)。

Min Dev 一期提供了 CLI 服务(后续将提供 GUI 服务,进一步降低普及成本),包括开发范式、一键式搭建开发环境、Demo 预览小程序、内置化文档、自动编译打包、组件一键发布、安装、更新等能力。

MinUI 是一套小程序 UI 基础组件库,基于统一的 UI 规范,是一套基于 Min 的小程序组件化开发最佳实践。一期提供了 18 个基础 UI 组件,并提供了一个独立的演示版小程序供开发者体验。

目前 Min 已经开源。有关 Min 的更多介绍请戳这里

动态模块加载

微信小程序的包有 2MB 的大小限制,蘑菇街作为一家电商公司,本身功能就特别复杂,一些基础功能譬如图墙、商品详情、下单、支付、IM、退货退款还有维权等等功能,占用的空间就比较多了,电商促销也很多,经常会有各类运营活动促销页推出来,因此 2MB 的包大小极大地限制了我们的业务发展。

动态模块的机制是结合蘑菇街现有的技术体系——蘑方构建出来的,蘑方本身是一个工具,里面维护了公司内部的各类业务组件库以及基于组件搭建页面的功能。在微信小程序中,此机制就是尽量把业务需要的组件库以及页面框给打包到小程序包内,在搭建页面后台里其实维护的是组件组合的配置,以.json 存储在 ats 上。小程序中展现形式多样化的页面,如活动页面,其实是在页面渲染的过程中动态加载配置,然后进行组合渲染。这种机制的出现可以很好地解决小程序包越来越大的问题。

其实在业务上我们还做了很多其他的尝试,比如业务分包、代码控制等,也可以改善包大小的限制问题。

其实小程序内打开 H5 页面也可以很好地解决这类问题,在我写这篇文章的时候微信已经支持这个功能。不过我们上述方案在一定程度上比小程序原生的方式灵活,体验又会比 H5 更好。

服务端技术演进

业务服务分组

当业务增长达到一定规模之后,提供业务功能调用的服务端集群对应的调用源可能会比较多,这时候我们希望针对性地对一些服务做分组管理,比较简单的方式是以部署机器的方式来做,也可以按照调用来源的方式来做。

举个例子,如上图所示,当一个服务集群 C 对外提供服务 S1 和 S2,有两个外部调用集群 A 和 B 同时订阅了 S1 和 S2 两个服务,当 A 和 B 的调用量远远达不到 C 的服务能力的时候,这个架构本身问题不大。

但是当 A 或者 B 一方甚至两方的调用量激增起来,这个时候调用集群就有可能互相影响。我们在架构上做了一些调整,让消费集群可以互不影响。我们把服务集群 C 拆分为 C1 和 C2(两个集群都可以提供 S1 和 S2 服务),消费集群在订阅的时候,可以按照规则选择订阅哪个服务集群。此时 A 和 B 调用的集群分开,服务集群 C1 和 C2 页互不影响,业务相互隔离,从稳定性和容量规划上来看更加合理。

分组可以基于多种规则来定制,比较灵活,在实际使用过程中更多还是以服务器分组的方式进行,维护成本更低。

面向小程序(数据)的容灾策略

这里以导购类业务来举例说明数据容灾的一些变化。这类业务的特别之处在于业务变化快,表现上以数据展示(数据加工)为主,交互逻辑简单,相对于详情和交易这类需要实时交互的业务而言,可以以低成本的方式做一层数据容灾。当上层业务出现问题的时候,本身应用容灾的逻辑都失效了,那么这个时候数据就是最后一层容灾了。

如上图所示,前端请求到服务端,正常返回进行渲染,展示给用户看。当这个过程中出现异常,譬如服务端逻辑错误、网络中断等,这种情况对于电商而言商品类的曝光量会收到很大影响,相对应的广告收入也会降低。为了解决此类问题、提升系统的可靠性,我们提供了两种方式去做数据容灾,核心思路都是以空间换稳定性。

  • 第一种是可以穷举的条件组合情况,我们在服务端计算出对应的数据场景,将之定期同步到 CDN 上。
  • 第二种是依赖于用户操作(以用户搜索商品为主)的场景,那么会进行打点并且去离线统计,覆盖约 90% 左右的用户访问数据,定期同步到 CDN 上。

当前端请求失败的情况发生时,会直接请求 CDN 上的容灾数据,数据更新在分钟级。

补充说明下,这只是我们已有的多级容灾策略的一种补充,仅仅用来保证最上层业务数据的正常透出。

个性化推荐

蘑菇街的推荐系统始于 2016 年,经过一年的发展,现在蘑菇街全站很多产品都已经接入,包括商品、店铺、搜索、导购以及社会化等。

推荐系统的整体架构可以分为三个部分:

  1. 推荐数据存储层:使用的是蘑菇街自研的一套 KV 内存数据库,用来存储推荐数据、特征数据的底层存储介质。
  2. 推荐数据计算层:针对推荐结果、用户特征、排序模型进行离线计算,基于用户行为、推荐内容的特征数据进行实时计算。
  3. 推荐投放服务层:按照特定的召回策略,组织推荐结果投放展示。

在推荐投放服务层,算法工程师面向场景开发推荐结果召回策略;系统工程师为了策略脚本的执行,提供数据结构和运行时环境,保证系统稳定性,二者协同提供个性化投放服务。

蘑菇街的推荐投放服务,有两个重要特征:

  1. 推荐内容类型多。作为一家社会化电商公司,提供的除了传统电商平台的商品、店铺、搜索、广告等电商平台的推荐数据,我们还提供了社交内容的推荐,并且范围扩大到了红人、直播等业务。推荐的场景是完全不同的。
  2. 跨团队协作。蘑菇街内部有多个算法团队,所在地也分北京和杭州,需要在一套系统上开发多种需求,沟通对接存在成本。

以上两点决定,蘑菇街开发出来的一套通用的推荐投放系统,向上可以统一透出各类结构化数据,完成数据补全、格式化工作,向下可以快速接入多种数据源,满足不同业务线的推荐需求。

展望

微信本身的活跃用户群体十分庞大,随着微信在小程序内的投入日益增加,蘑菇街对这块也是越发看重。

现阶段蘑菇街的用户群体还偏年轻化,性别也是女性用户占绝大多数,进入小程序以后用户群体就完全不一样了,因此对于蘑菇街的基础工具系统带来了更大的挑战,比如如何管理商品、店铺,做一些营销活动等;对于推荐系统也带来了更大的机会,我们可以有更多的策略运用在不同的场景上。

蘑菇街目前面向小程序的积累还远远比不上在 PC、native、Xcore 的积累多,因此接下来组件和工具的积累就显得格外重要。对于数据的积累,特别是用户层面行为和偏好数据的积累,后续我们会结合着微信进行,应该可以极大地完善蘑菇街的数据积累。