随着互联网的发展,电子商务在全社会的深入普及,中国网购用户强大的消费能力已经不止满足于天猫、京东、淘宝等购物平台。据相关报道,目前国内中产阶级人数已过亿。其中一批懂英文或日文的先行者,通过双币或多币种信用卡,直接在国外电商站点上购物,直邮或者通过海外转运公司将商品运输回来,也就是所谓的“海淘”。海淘族最初的发展壮大,还得力于一批海外代购客的大力宣传,通过收取服务费以及国内国外商品的价格差赚的盆满钵满。
从2012年开始,涌现出一大批海淘平台(也属于跨境电商)创业潮,专门抢夺这3千万海淘用户群体,例如:洋码头,蜜淘、麦芽宝贝、辣妈帮、小红书等。2015年天猫、京东、唯品会等各大电商巨头更是纷纷加入海外购频道,一时绿海变红海,好不热闹。
本文旨在从技术的角度,来探讨海淘平台建设中的一些问题和经验。
虽说个人可以直接到国外网站购物,但毕竟有些门槛。一是二三线城市,多币种信用卡申请有限制;二是语言障碍,即使英文略懂,也不敢贸然下单,还有日文、德文、法文等站点;三是大部分海外购物站点并不支持直邮中国,需要借助于转运公司,到转运公司的服务站点申请帐号,进行商品预报及付费等一系列处理。整个流程下来,周期短则半月多则数月,一般人都望而却步了。
海淘平台的出现,正是为了解决上面的痛点。用户可以直接在平台上浏览中文商品介绍,像在国内京东、天猫上一样的购物流程,使用支付宝或银联卡进行付款,无须关注后续转运细节操作。由海淘平台来提供一站式翻译、外币支付、物流转运和售后服务。
1) 接入层用于为PC浏览器,手机浏览器,原生APP应用提供后端Web服务。
2) 业务服务层用于为用户提供注册、登录认证、商品浏览、加入购物车、提交订单、支付、自动下单、商品优惠爆料爬取等业务相关的服务。
3) 基础服务层用于提供短信发送、邮件收发、全文检索、图片存储与读取、消息通讯、日志集中存储与读取、分布式缓存等业务相关度低的服务。
4) 数据存储层用于对结构化数据与文件数据的存储。
1) CDN网络用来发布商品图片、JS、CSS、HTML等静态资源。
2) Nginx一主一从,保障高可用,提供静态资源缓存和反向代理服务。
3) Web应用中的会话信息集中存放到Memcached中,支持Non-sticky会话。
4) 商品信息、评论信息、用户属性等业务信息缓存到Redis中。
5) 消息服务用于内部子系统间事件通知,业务消息通讯。
6) 数据库主从复制,基于LVS实现高可用。
由于项目周期短,产品不停迭代新功能,以及技术人员水平有限,系统依然存在不少弊端,主要体现在以下几点:
商品数据、用户数据、订单数据以及基础数据全部表在一个数据库实例中,核心数据与其它数据没有分开来,无法根据业务系统负载量来将访问压力分摊。运营支撑系统与网站应用共用数据库表,一旦后台子系统程序出现bug,数据库负载加重或锁表,直接影响前端用户的访问。
商品信息的编辑入口太多,没有统一的商品管理接口,各个业务模块直接访问数据库,难以维护商品缓存的一致性。一旦开发新功能,容易顾此失彼,维护成本加大。生成订单的逻辑,分散在网站、H5、APP,计算订单金额的逻辑,由于开发人员各搞各的一套,容易出现漏洞以及不一致的情况。
过度设计,人为地制造问题。为了支持多视图模版,蹩脚的插件式Web集成,非标准化的Maven工程结构,DAO层重复造轮子等等。同样功能的代码存在好几份,难以维护。
数据库表数量膨胀过快,两百张表里面核心库表不足20个。应用程序代码中功能实现过度依赖多表关联查询,有些基础数据完全可以在缓存中读取。数据库表命名不规范,复用性差。有些业务场景,数据完全不需要持久化到数据库中,可以采用Redis、MongoDB等来替代。
为了解决上述问题,同时随着业务的运营发展,日PV量很可能达到千万级,不得不对架构进行重构优化。考虑从以下几个方面着手:
引入分布式服务注册与发现框架,远程调用服务,请求应答参数需要序列化与反序列化,都会增加开发与维护成本。因此先将核心业务服务化,设计成无状态的应用,便于负载加大时动态扩容。
1) 用户注册、登录、用户信息查询封装成用户服务。
2) 商品信息的新增、编辑、上下架、价格更新、重量更新、SKU属性更新封装成商品服务。对商品及SKU信息的缓存维护在该服务内部集中管理起来。
3) 加入购物车、删除购物车、购买数量更新、收货人地址更新、提交订单、计算订单价格、更新订单状态封装成购物车服务和订单服务。
将单个数据库切分成多个数据库:用户库、商品库、订单库、基础数据库、营销活动库、物流库、文章资讯库、综合数据库、日志库。其中用户库、商品库和订单库对高可用要求最高,商品库和日志库的存储容量要求最高。
目前的代码中按照分层理论一刀切的痕迹过重,切分粒度太大,体现不出按照业务来划分组件的设计思想。这样的后果就是封装性差,业务上无法复用代码。同时新增功能时谁都可以改动已有的代码,容易引入新bug造成原有功能的不稳定。接下来需要对代码进行重构,拆分成不同的业务组件,各个组件对外提供访问服务接口,对内管理数据持久化状态和缓存更新。
目前一主一从的方式,虽然保证了高可用性,但是当发生故障时(例如从库宕机时)无法保证读写分离的可用性。实际应用中,读数据的场景远远大于写数据的场景。因此后面考虑数据库一主多从(3个以上)的配置,同时保障高可用和读写分离持续可用性。
数以万计的商品量,数十上百万的SKU,作为一个创业公司钱少人少,是不可能有那么大的运营团队去人工编辑录入的。
没错,我们就是用爬虫去国外网站上下载。技术宅嗖嗖嗖一天功夫爬虫程序写好了,一个站点的商品信息开始下载了。第二天早上过来一看,怎么只有几千件商品这么少?赶快查看日志,诊断定位,原来被对方站点屏蔽了。不能请求太频繁,同一个IP不能访问太长时间,图片下载过慢,国内IP无法访问到墙外的世界,隔三差五原站点改版换样式。什么,图形验证码?OMG,美亚服务端能分析出来你不是真人在访问!你屏蔽,我就换IP,封封封,换换换。
费了九牛二虎之力,商品信息爬下来了;运营说,全都是英文的,日文的,他们现在的人力配置,翻译完需要到猴年马月。翻译慢,找百度翻译,谷歌翻译啊,So easy!结果程序一运行,发现翻译结果真垃圾啊,根本就不是在说人话,比没翻译时的效果还差。终于有一天,放下了自尊,抄同行的啊,直接爬取别人编辑好的中文商品信息。终于,如此这般,总算有了第一批冷启动的一两万件商品。
那么问题来了,原站点商品价格一直在变化啊,SKU一时有一时没有,怎么办?用户下了单,结果原站点涨价50%,要不要给用户下单,还是退钱?呃,我们还需要开发一套商品信息同步系统,每隔5分钟获取最新的价格和SKU信息。即便你5秒更新一次,就是那么巧,用户下单后,商品涨!价!了!运营同学跑过来,怎么办,有没办法解决?思量好久,最后和老板说,用户提交订单的时候我们去原站点再检查比对一次,但是这之后的价格波动,只能由我们平台认栽了(没错,赔钱也要给用户下单。电商行业,用户就是上帝)。
至于每5分钟同步一个商品的价格和SKU信息的成本,呃,这是个秘密。只能说公司预算少,根本做不到!!!后来,我们只将热门推荐商品每半小时更新一次价格和SKU信息,其它大量商品,在用户浏览商品页点击SKU时去触发后台异步更新单个SKU的信息和价格,在预算成本和用户体验之间做了个折衷。当然,这个方案并不高明,但是很有效。
用户不下单,我们愁啊。是不是产品视觉没有冲击力,还是商品不是用户喜欢的,价格不吸引人,还是推广投入不足流量没有做上去?用户下了单,我们也愁啊,是喜极而泣的烦恼。信用卡额度不够了怎么办?什么,美亚又砍单啦?帐号怎么又登录不上去啦?如此这般,愁啊,没有一天安生过。运营说,我们可以找黄牛来解决,没错,就是专业代购。他们有信用卡,有帐号,有固定IP,他们有强大的协同作战扣扣群组织。每天几十上百的订单,他们轻松就能处理完,前提是平台给佣金!到最后等于平台成了个只亏本不盈利,赔钱赚吆喝的主。老板说,这样下去不行,我们得有自己的下单团队。
没错,技术宅又顶上了。噌噌噌两周功夫,一套自动化下单系统开发出来,愉快地运行起来,心想这下该给老板节省不少人力开销。运行了一段时间,感觉良好。且慢,怎么一个订单下了两次!!原来一个用户订单,包含了多个子订单,分别对应几个国外站点,程序居然注册了多个转运公司帐号。思前想后,又设计了一套基于数据库的分布式锁,同一时刻同一用户只有一个子订单能被处理。原以为万事大吉了,结果网络延迟,被美亚屏蔽各种砍单,各种原因分分钟让程序不工作。为此我们开发了一套监控系统,将下单过程中的每个步骤截屏,记录下各个步骤的执行状态,耗费时间,进而优化下单系统。后面,我们又采用人工下单系统与自动下单系统相结合的方式,对于出错订单再进行回滚处理,重试自动下单,大大提高了下单成功率(除了美亚外,其它站点有70%以上成功率)。
物流信息接口主要用于向转运公司预报包裹中的商品信息,包括数量、品牌、名称、金额、类别,方便转运公司进行运输处理以及海关清关,同时能时刻查询到包裹最新的处理情况,以展示给用户物流追踪轨迹。原本以为信息对接后,就无须人工干预了,实际业务运行一段时间后,用户打客服电话来查询包裹物流信息,才知道物流公司接口根本查询不了最新的物流信息。海淘属于最近几年才兴盛起来的业务,转运公司的IT系统也是刚刚建立,漏洞百出。
无奈之下,人工拿着物流跟踪号(TrackingNo)每天去对方站点逐个扫一遍。人工维护更新物流信息的成本实在太高,效率也低。后面改为程序每天去对方站点扫一遍,也常常是包裹五六天拿不到物流更新。还有些物流公司根本没有接口提供,只能人工登录后,肉眼比对更新,效率极差。物流信息更新慢,运输周期长(短则一周,长则两个月都有)一直是海淘用户的痛点。国内物流业发达,一般海关清关完,包裹两三天就能发到用户手中。国内物流跟踪简单,用户只要拿到运单号到网上搜索都可以看到明细。
用户购买的商品数量,乘以单价,再累加起来,不就是订单金额吗?在海淘业务中,计算规则要复杂得多。订单金额除了商品本身的费用,还包含境外运费,转运公司收取的运费(转运费),平台收的服务费,再减去各种营销活动的优惠部分。商品单价受原网站影响,同时汇率波动也会产生影响,需要结算时换算成人民币价格。境外运费和转运费,一般是美元或者日元单位,也有些转运公司直接人民币价格结算。平台收取的服务费,按照商品总金额乘以一定系数来计算。优惠部分计算规则更加复杂,有全场满减优惠,专题活动优惠券,特殊种类商品优惠券,还要考虑各种运营规则。例如:优惠券不能叠加,全场满减与优惠券可叠加,优惠券活动下线等等。订单金额的算法部分,是最为复杂,也是平台安全性要求最高的部分。稍微有个漏洞,就能被黑客以及一大波职业撸bug单的买手利用,造成很大的损失,任重道远。
平台冷启动时,肯定是先有第一批商品信息,才能有内容让用户浏览。用户来了后,发现没有找到自己感兴趣的商品,怎么办?这是很可能发生的事情,特别是商品库品种有限的时候。运营人员根据掌握的推广渠道不同,可能导入过来的用户是数码宅男,也可能是时尚女性,如果呈现的商品与用户的需求不匹配,订单转化就会很低,用户流失率就会很高。
最好是有上百万数量的商品库,各个主要品类的热门商品都覆盖到。用户通过多级分类打开或者直接在搜索框输入商品关键字就能找到自己所需要的商品。但是上百万的商品库,是海量的工作量积累而成,需要投入很多人力成本,不是小创业团队能承受。后来想到两个解决办法,一是从同类导购网站、海淘网站去爬取;二是开发一键购功能,让用户主动输入该商品在原网站上的链接地址后即时爬取。其实可以让运营想想办法,找一些返利平台、转运公司以及导购平台甚至买手团队拿到一定的历史数据,会更有价值。有了一定数量的商品后,运营就可以搞些专题活动,针对不同的品类,不同的目标用户群体,在合适的渠道上去推广。
做这个项目给我最大的启发就是,技术不是唯一解决问题的办法。深入与产品经理和运营人员沟通,以实际业务需求为导向,不同的场景采用不同的设计方案;用最直接有效的技术手段,满足业务功能和质量需求,同时又预留一定的扩展性,这才是架构师该做的事情。架构设计中简单就是美,越是简单的设计越易于维护。