从0到1构建一个电商平台 – 开发篇(转)

文章来自 在外企和互联网碰撞的猴子 的微信公共帐号。是咱们跨境电商项目的架构师写的。咱们项目的每一个点读提到了,方便记录查找,我转载一下。php

背景

针对如火如荼的跨境电商行业,催生了提供一个SaaS电商平台为一些传统企业和机构开展跨境电商业务的市场机会,因此咱们有机会来作这件事情。今天重点讨论销售和运营系统,不涉及跨境贸易价值链中的采购、通关、购汇结汇以及支撑电商业务的CRM,OMS,ERP等系统。前端

团队成员-最初由3名具备比较丰富企业级电商平台开发经验(>6年)的资深工程师组成,通过1年发展,团队规模成长到30+工程师。node

架构准则

1. 领域驱动 – 基于本身对企业级电商平台设计和开发的丰富经验,进行领域建模,并将领域映射到业务子系统,划分高阶系统边界linux

2. 分布式、服务化 - 吸收企业级电商平台的经验教训,各业务子系统独立开发、测试和部署,子系统以服务暴露本身的能力,子系统与子系统之间以服务进行通讯和交互。这一点可能会有些争议,在业务发展初期,整个系统的容量不须要那么大,分布式是否是设计过分,特别是在部署上对一个小团队来说也很难驾驭。在项目中途我也曾经深深的质疑过这一点,可是慢慢也体会到它带来的好处,放开分布式架构带来的将来系统更容易更灵活的优点暂且不谈,至少每一个业务子系统能比较独立高效的开发,测试和部署,在一个高速发展的团队,不可避免的沟通会成为一个巨大的挑战,这种独立性能够很大程度减小一些艰难和低效的沟通。web

3. 持续集成 – 从第一天开始创建持续集成的体系和流程,保持开发、测试、部署的高效性docker

第一阶段(2个月)

在人员有限的状况下,以一个业务领域切入,构建业务子系统,肯定基础技术选型,提供业务子系统的样本项目工程和框架供后续业务子系统开发参考。由于交易是电商平台最重要的一个业务领域,而且涉及到跟几乎全部其余业务领域的交互,咱们选择交易子系统为切入点。数据库

基础技术选型:Java+Spring做为服务开发的基础框架MySQL存储交易数据RestEasy做为REST服务框架子系统间的服务调用使用Hessian。由于团队成员曾经在淘宝交易平台的工做经历,整个服务框架基础技术栈的的选型深受阿里的影响。后端

1. 一个业务子系统由-app, -client, -server三个项目组成。其中app是controller层,经过RestEasy把服务以REST接口发布出去client暴露了业务服务接口及Domain对象server提供业务服务的实现。这里app不会直接调用server,app须要调用业务服务也是以client为入口。这里主要的考虑是基础服务和应用服务这两级服务的概念。这个概念相信了解淘系系统的人不会陌生,基础服务只提供对核心领域对象的基本操做(典型的增删改查及基于此的一些基本操做),应用服务通常对应一个业务场景,须要调用一个或多个基础服务,进行串联、聚合或其它计算处理。例如:购物车的增删改查是典型的基础服务,而计算购物车时一个典型的应用服务。这样设计,随着业务量的增加,系统将来更容易向微服务架构演进。缓存

2. 业务子系统打包成WAR部署到Tomcat须要一个web项目,-web,Web项目里,包含datasource,hessian服务发布,声明包含哪些业务子系统,以及配置文件。由于在项目初期咱们须要支撑的业务量还没达到必定的规模,出于成本控制的考虑咱们须要将多个业务子系统打包发布到一个WAR中,因此这里出现了须要在Web项目中声明包含哪些业务子系统。将来若是须要从一个WAR中将一些业务子系统拆出来单独发布,也比较灵活。php框架

3. 服务调用框架,说到这里,你们第一反应确定是Dubbo,在项目初期,咱们并未使用Dubbo的方案,主要是考虑到团队成员并无Dubbo使用的经验,并且在咱们并未产生大规模分布式服务和服务治理的需求时,Dubbo的使用在项目初期反而可能成为咱们的不可承受之重,把Dubbo用起来并不难,难的是如何驾驭它。因此咱们选择使用DNS+Load Balance来作服务发现,基于动态代理实现了Hessian/Local/Mock的服务调用机制(Local主要用于不一样业务子系统部署到一个WAR中,Mock主要用于UT),每一个Web项目提供一个服务调用的配置文件,配置这个Web项目要调用的服务的调用机制和服务endpoint,由于咱们使用DNS+Load Balance来作服务发现,因此服务endpoint都是Load Balance的地址。

4. 分布式日志,分布式服务调用如何跟踪一个请求的整个服务调用链条,是咱们必需要解决的问题,基于Google的Dapper论文,淘宝实现了EagleEye。其实Dapper的原理并不难理解,咱们选择本身实现请求的global ID并在分布式服务调用间进行透传,与ELK(ElasticSearch+Logstash+Kibana)相结合,能够比较清晰的呈现一个外部API请求跨系统服务调用的全部日志。

5. 缓存的使用,对于内容型数据(例如:价格,商品税率),使用Redis缓存,这里特别要注意防缓存穿透(当数据库记录自己不存在每次都穿透缓存去读数据库);对于配置型数据,使用Local JVM缓存(未使用EhCahce,本身实现MRU和缓存空间设定,主要缘由是之前项目有相应的积累,可重用);对于操做型数据(例如:购物车,库存),使用Redis缓存,操做型数据里,特别是购物车读写一样频繁,在高并发环境下容易由于缓存更新失败而致使缓存和数据库数据不一致,须要使用MQ记录不一致情形并经过Listener应用执行缓存失效。

6. 图片服务器的使用,团队小伙伴之前有大规模应用TFS的经验,给我反馈是TFS算是淘宝开源做品里为数很少的精品,刹那间我是有点心动的。现实立刻打醒我,小伙伴须要去作其它更重要的事情,TFS这玩意,等咱们图片规模大到必定程度必须本身来作图片服务器和存储系统再考虑吧。因而咱们选择了七牛,很完整的图片管理和CDN服务,算下来一年的价格也不贵。固然第三方服务始终是不可靠的,咱们也专门引入了assets的业务子系统管理图片及图片在服务器上的元信息,这样在未来比较容易的迁移到其它图片云服务或者本身使用Nginx+TFS。

第二个阶段(4个月)

随着人员的逐步到位,各个业务领域的业务子系统开始铺开进行开发,这个时候如何协调开发人员以统一的风格来实现各个业务子系统就显得格外重要了。并且,在基础框架和工具层面,也要尽可能统一,减小维护的成本,因此,咱们在开发团队里一直有一个由精兵强将组成的小组负责这一部分。

1. 数据库规范:包括每一个业务对象对应的数据库表如何设定主键,惟一ID,惟一性索引,每张数据库的预留字段(建立时间,更新时间,建立人,更新人,乐观锁计数等)。

2. REST API规范:增删改查的URI pattern,特殊POST操做的URI pattern,API版本。

3. 通用参数规范:对语言,货币,请求ID,调用者等的统一命名和处理。

4. 领域边界划分:在划分基础领域边界的基础上,为了提升性能,减小没必要要的远程服务调用,在业务容许的状况下,进行必定的冗余。例如:在购物车中不只仅存储商品SKU ID,还包括商品的名称,商品缩略图连接,店铺名称等信息,避免在查询购物车时再去调用商品API获取这些信息。

5. 异常处理:提供异常基础类,基于异常基础类提供异常处理框架在业务服务层和REST服务层对异常进行统一处理。各业务子系统仅需扩展异常基础类,在业务代码中抛出这些异常便可。异常处理框架帮助处理剩下的事情。这样对于API调用者来讲也是极其友好的。

6. 配置管理:在系统尚未发展到必定规模时,可能还不须要一个集中的配置中心。可是,每一个业务子系统切忌分散管理配置信息,由于开发,测试,预发布,线上各类环境须要的配置极可能是不相同的,分散管理的配置信息在每次部署一个新的环境简直是噩梦。目前,由于咱们已经的系统规模已经发展到10+个业务子系统,即便每一个子系统都集中管理配置信息,如何高效的管理各个不一样环境的配置信息已经成为一个很是突出的问题,咱们基于disconf已经作了POC,计划在下一个阶段把disconf用起来。我本人是Netflix OSS的粉丝,原本Netflix Archiaus是个人首选,可是考虑最近接触的国内开源的工具愈来愈好,社区也愈来愈活跃,咱们果断的选择了disconf。

7. Session:在这个方面咱们是走了一些弯路的,已开始咱们有规划专门的session service,而且与shiro作整合,后面的session信息存到Redis中,这个已经基本开发完毕。可是在项目实际演进的过程当中,这个阶段咱们的前端应用里PC端和移动端商城都是基于PHP开发(基于Yii Framework),在PHP里已经有比较完整的session处理,咱们后端的业务服务彻底是无状态的,在这里再去使用这个统一的session service反到在项目时间特别紧张的状况下给PHP开发形成不少困扰,因此,咱们决定短时间内放弃session service,全权交由PHP来处理。在将来系统演进到前段多应用,甚至不一样技术栈时在从新把它找回来。

8. Scheduler和任务处理:对淘宝了解的同窗必定据说过他们早期开源的TBScheduler和如今正在使用的TTD,还有最近当当开源的Elastic-Job,但是,对于一个刚刚起步的业务系统,对于这种任务处理还没到须要分布式的地步,因此,咱们选择了从Quartz和Spring Batch切入,可是Quartz自己只负责调度自己,咱们仍是须要存储一些任务的详细信息和状态,要支持异步任务的回调通知状态,排他性任务,已经简单的任务流程编排,咱们按照本身的需求,基于Quartz作了扩展支持这些功能。将来若是业务发展须要咱们引入分布式任务处理的机制,我想Elastic-Job会是个人第一选择。

9. 统一登陆服务:今天我相信在Java的世界里你们要实现统一登陆服务大部分人会第一选择会是CAS。CAS对各类认证协议的支持何尝完备,后面也很容易挂本身实现的credential数据库。不过比较讨厌的是,CAS自带前台登陆页面,这部分是基于JSP的,而咱们的前端工程师全是PHP的,改造起来那叫一个费劲。虽然咱们能够放弃它自带的JSP页面直接调用CAS的API,可是处理起来过于复杂,对于咱们这个规模有限的初创团队有点不可承受之重。另外,目前咱们团队在注册这个环节是直接绕过了CAS调用用户子系统的注册API,对于PHP同窗们来说也是很高兴的。

10. 非Java业务子系统:在电商平台中,从业务需求来说,促销是很是复杂的一个业务子系统,各类不一样的促销条件,促销次数限制,促销之间的互斥和组合,促销金额计算,和优惠券或优惠码结合使用,其实这须要一个高效的规则引擎,一方面可以比较清晰的定义这些规则,另外一方面在执行期也可以在毫秒级可以完成对一个订单的促销计算,传统的Java+关系数据库建模是很难搞定的。已开始咱们想基于一个开源的规则引擎来作,因此咱们队Drools作了必定的研究,做为一个通用的规则引擎,Drools功能比较强大,可是也比较复杂,在咱们只有1个开发工程师能铺到这个领域的状况下,也很难搞定;另外,Drools在实时规则计算的效率上也是一个很大的问号,并且促销管理业务工具对咱们是很是重要的,基于Drools的规则定义语言来开发这个工具难度过高。咱们最终选择了DSL,定义一套促销这个领域的专有语言,用JRuby来实现促销DSL的语义模型和语法解析器,一来Ruby的一些声明式语法特性是很是适合来定义DSL的语义模型和进行语法解析,另外一方面JRuby运行在JVM中能够和各类Java库无缝整合。促销引擎分为4个子系统:DSL引擎-负责管理促销DSL定义,解析DSL转化为语义模式;预计算引擎-负责对促销规则进行预计算,将商品和其可能应用的促销进行关联(提升runtime计算的效率);runtime计算引擎-在线为一个订单计算促销结果。优惠券引擎 – 处理优惠券和优惠码的生成,管理和使用。目前,咱们的促销规则DSL定义,预计算结果和优惠券信息均使用MongoDB进行存储。其实即便这样,业务管理工具来管理DSL来表达的促销规则仍是一个挑战,咱们在二者直接加入了一层meta data层来作映射缓解这个问题,这就意味着业务管理工具只是DSL表达能力的一个子集,对于一些高级的促销,咱们直接在工具上开放了DSL编辑器直接对DSL进行编辑。另外,促销引擎的4个子系统都已经利用Docker进行部署,也为咱们系统向容器化部署演进打下了基础。

11. 前台商城系统和CMS:自己我本身在CMS上有一些研究,对WordPress, Joomla和Drupal都有必定的了解,加上电商运营的特色,对CMS的要求特别强,我一开始是打算在Drupal上往前走的。并且咱们在Drupal上也作了投资,研究学习,POC,前先后后也有小三个月,可是慢慢我也发现一个问题:Drupal自己也是很完整的一个系统,加上生态系统,和电商的领域已经有不少交集,这给个人小伙伴形成了不少困扰,他一直在质疑为何产品目录树,购物车这些Drupal都有现成的插件咱们还要再去实现一套后端服务。虽然从架构上很容易解释,可是现实是当你天天面对这样一个系统时是不免会很纠结的。后来,咱们找到了一个既有丰富的Drupal经验,也经历了去Drupal,自主开发CMS的小伙伴,一番沟通和探讨,咱们决定放弃Druple,本身干。

我这里贴一下个人小伙伴给个人建议吧:drupal善于作cms,模块当然成熟,可是牺牲了性能为代价。咱们的业务场景更加灵活丰富,结合了传统pc,线下收银,以及移动,o2o等各类场景。单纯的依靠定制模块开发将会是事情变得更加复杂且不可控。 采用一个流行且易用的开源框架,使得咱们在迅速迭代与稳定架构之间得到了更好的平衡,同时也能更好的结合中国特点的市场需求。经过框架的crud功能,咱们迅速完成了传统的cms模块。而经过mvc最流行的业务分层模式,咱们更好的经过m层同后台进行了松耦合式的开发体验。使得整个系统既能够脱离应用层纵向拓展,也能够经过其余各类应用层架构(node.js agular.js)实现横向的拓展。drupal的缺点还有一些,好比社区陈旧,模块数量虽多可是优质模块不多。性能上由于其自身机制,当系统复杂以后容易产生性能瓶颈,且不利于开发人员trace。 若是采用drupal,咱们将被迫用5-10年的senior phper去学习一个在中国并不流行的技术架构。基本是很难找到人的。换开源php框架以后,那么,只须要有几个senior的开发人员进行组织总体架构并review,就能利用中国大量的码农(coder)。

12. 业务管理工具:AngularJS+Bootstrap,这个应该没有太多争议,虽然现现在React.js如火如荼,但是在咱们那个时候Angular仍是更靠谱的选择,另外咱们的工具应用选用了Spring的AppFuse框架,让他们可以更快速的开发前台工具应用,并方便的处理访问控制,多语言,先后台交互等问题。

单独的话题

1. 单元测试 – 节奏再快,资源再短缺,我仍是坚守底线,你们作好UT,并且是基于mock的UT,前期在UT的投资虽然对于快速迭代的节奏来说每每都会有冲突,但是它带来的后期维护的便捷和高效每每是值得咱们付出的。

2. 持续集成 – 从第一天,咱们就投入一个专职工程师进行来负责持续集成,Jenkins, Maven repostiory, Git的搭建,持续集成脚本的开发,持续集成流程的创建。这些在不少小团队看来没必要要的投资其实随着项目的执行其带来的好处是会不断被放大的。咱们采用chef来生成操做系统和中间件级别的标准image(例如:Tomcat, Nginx, MySQL, Redis, MQ, ElasticSearch等等的标准image),可是咱们的持续发布脚本并未使用chef来编写,而是直接用ruby来实现的,这个主要缘由是直接用ruby提供了更大的灵活性,咱们在之前的项目中也有必定的积累。

3. 测试 – 很遗憾,咱们的测试资源是在有限,在保系统功能的基本准则下,咱们牺牲了API级别的功能测试,前期只能靠UT来保障了。这也有不少无奈,若是有一天测试资源跟上,须要第一时间把API自动化测试,和基于场景的自动化测试不上。

在路上

1. 配置中心,目前很痛的一个环节,前文已经提到,咱们已完成disconf的POC,接下来就会使用。

2. 服务发现和调用框架:用不用dubbo?其实有其它更轻量的选择(例如:consul作服务注册和发现,hystrix/turbine作故障隔离和服务metris),咱们如今的实现已经很小清新。另外一个角度dubbo在国内的生态的确不错,基本上主流电商公司都在使用。这个留着慢慢纠结吧。

3. 错误数据校订平台:在高并发的场景老是不可避免出现一些异常状况形成错误数据,创建一套错误数据校订平台是很重要的,要知道客户投诉起来,运营抱怨起来,每每技术团队是最惨的。

4. 全docker化:目前除了促销引擎的4个业务子系统,其余仍是使用的青云上的linux虚机,考虑到初期业务不大要节省服务器成本,咱们不得不将一些业务子系统合并到一个WAR里面发布,若是每一个业务子系统都可以docker化,就不须要这么费劲了。

5. API网关:目前咱们的业务服务已经有需求要被外部系统调用(例如:联盟营销系统),如何将内部的业务服务暴露出去变成一个课题,咱们须要引入API网关来解决这个问题,目前咱们已经开始调研zuul。

6. 多租户模式下不一样客户的个性化定制:目前咱们只能作到前端商城的个性化定制和外部系统集成的个性化定制,核心业务层如何作个性化定制是须要解决的,咱们之前有作传统企业级电商平台的定制框架的经验,可是在SaaS多租户的场景下这个将会是个巨大的挑战。

7. 压测体系:SaaS和传统企业电商平台的压测是很不同的,若是评估线上环境的容量,如何持续的对线上环境作压测,如何模拟真实流量,如今这些一二线互联网电商平台已经积累了足够的经验,咱们要作的,就是一步一步,吸收人家的精华,结合自身的状况,执行起来。

8. 运维体系:这是一个比较大的话题,咱们能够单独再讨论。

相关文章
相关标签/搜索