出口电商+跨境物流技术挑战:MongoDB应用实例

·  阅读 2573
出口电商+跨境物流技术挑战:MongoDB应用实例



内容来源:2017年4月22日,贝法易集团技术部总监黄亮在“2017年MongoDB中文社区深圳用户组大会”进行《MongoDB在跨境电商物流供应链系统中的实践》演讲分享。IT 大咖说作为独家视频合作方,经主办方和讲者审阅授权发布。

阅读字数:2896 | 4分钟阅读

嘉宾演讲视频地址:suo.im/5pkBJh

摘要

本次介绍下出口易跨境电商物流供应链系统从单体应用过渡到面向服务的分布式系统架构的过程中,遇到的一些挑战和实现。其中包括了基于MongoDB建模和数据持久化方面上具体实践。

关于出口易物流

出口易物流是广州市贝法易商贸有限公司(简称贝法易)旗下,以全球仓储为核心,整合全球物流网络系统,为跨境电商卖家提供海外仓储、国际专线、国际小包、国际快递、FBA头程等物流服务以及本地化售前售后服务,解决订单管理、金融融资难题。我们不是物流的供应商,我们是跨境电商全程物流解决方案提供商。

我们公司的重资产是人员,我们了解跨境电商物流,包括跨境电商通关的环节、关于物流方面的国际法律以及离境品的相关信息,这些都是我们公司最宝贵的资源。

我们公司底下有一大群长期合作的供应商,这是我们最大的优势。我们的难点也是在于这些供应商是不可控的,因为我们是在使用别人家的服务。

所以除了订单系统还有一个很重要的资产就是我们自营的海外仓储,这也是我们最核心的价值。

覆盖欧美澳主要市场的服务网络

上图是我们全球布局的物流网络。这些仓储有大有小,英国仓库是我们最核心的仓库。截至2017年,在国内我们一共有八个仓储中心,重点的是在深圳、广州和上海。

全球主流电商平台重点推荐物流服务提供商

我们合作的平台推荐我们的物流服务提供商有Amazon、ebay、wish、阿里国际、shopee、AliExpress还有LAZADA。

出口易新老架构演变过程

我们之前的系统是上图左边的架构,针对商家第三方的ERP和一些商家自己研发的一套系统,还有一些平台跟我们的系统都是有直接交互。有的是通过出口易提供了一套UI来进行访问,还有就是大量的线上发货,我们会采用API来进行接入。我们后台有admin管理后台,还有单独的一块WMS系统。

我们认为这个系统有些过于庞大,想做一些调整。新的架构大部分还是没有改动,只是在后端针对admin的系统想要往面向服务架构方向落地。基于业务场景的切分有两块,一块是基于通用服务,比如说用户的认证和授权,还有就是日志。

支付有一些支付网关,有和paypal、alipay、payoneer还有银行的接口。

下面是我们业务最主要模块,包括产品报价、客户关系管理系统,还有订单、物流网络和运输,包括WMS、支付、物流轨迹跟踪、供应商管理系统,还有结算报表等等诸如此类。

出口易老业务系统特点

单体应用:前后端系统共用一套WEB App Solution。

单一数据库:采用MS SQLServer 数据库,核心业务功能共用一个数据库。

业务功能完整:IT系统随业务的发展不断扩展新功能。满足开展跨境电商物流业务最基本的功能性需求。

容易测试和部署:单独一个Solution,系统依赖少,一旦部署,全部功能即可测试。

出口易老业务系统不足

不够灵活:对应用程序做任何细微的修改都需要将整个应用程序重新构建、重新部署。

妨碍持续交付:系统规模大,构建和部署时间也相应地比较长,不利于频繁部署,阻碍持续交付。

受技术栈限制:包括开发语言,开发工具,数据库一旦选定,无法根据实际需要作其他选择。

技术负债:系统逻辑异常复杂,随着时间推移,人员更迭,技术负债不断累积。

出口易新业务系统特点

面向服务:根据业务模块切分不同的系统模块,系统模块采用面向服务架构。服务与服务通过明确的接口定义进行通讯。

领域驱动设计:每个业务模块团队负责一个领域或业务功能相关的全部开发。核心领域根据DDD中明确定义的规则实现。

独立部署、升级、扩展和替换:每个服务可以单独部署,透明升级,不影响整个系统。

异构/采用多种语言:每个服务开发团队,可以选择自己熟悉开发语言,数据库,开发工具和开发架构。

新架构落地的切入点

身份认证:每个服务都需要统一的登录认证。

鉴权:不同的用户使用相同的服务模块都需要鉴权。

由单点登录的页面包括基于OAuth2 API这样的方式来接入。内部采用的是DDD这样的一个逻辑架构,包括应用层、领域层。领域层里面又包括了领域模型、实体子对象、领域服务、领域事件和查询的规格。

基于仓储,要存一个订单,必须连接实体和子对象一起存储刷新到数据库。

我们做应用的时候更偏向于完成业务,所以选用了mangoDB。我们有一套自己的架构,在封装的过程中就会把mangoDB做一层封装。

上图中面向切面的架构包括了exertion、loading和cache等切面。

上图是TMS系统调拨单聚合根示意图,它包括了物流轨迹的集合、预计到货时间等信息,还有这些调拨单历经的节点信息。

为什么选择MongoDB?

1、非事务紧密型。错误数据容忍性相对比较高。

2、团队成员有使用MongoDB开发经验。对基于MongoDB方面的建模需要考虑的必要冗余有一定的了解。

3、Portal 模块数据库读大于写,基于MongoDB读写方面的高性能,解决了高并发下系统卡顿问题。

4、TMS 系统模型之间关系复杂,采用传统关系数据库,势必增加一堆表。采用MongoDB,可以把复杂的模型,通过一个Doucment存储到一起。

基于MongoDB开发需要注意的问题

集合之间不能Join,建模方面要特别注意。建议增加必要的冗余,减少二次查询。

仅仅支持单个Document级别事务。数据一致性错误时,要考虑增加必要数据监控和数据修复功能。

聚合查询,需要通过MongoDB 聚合管道方式查询,MongoDB C# 驱动提供了良好支持,但是相对Linq查询还是比较繁琐。

基于MongoDB的持久化实现

一、仓储Repository

仓储限定在对整个聚合根的操作上,提供聚合根的持久化和重建或查询。

二、仓储上下文Repository Context

负责事务处理。每个聚合根的仓储都会关联到同一个仓库上下文。但是MongoDB 不支持事务,我们提供了虚拟实现。仓储上下文应用了工作单元模式。

一些关注点

一、领域模型采用POCO(POJO)

简单的CLR对象(简单的Java对象),不继承任何持久化框架中的基类,或实现任何持久化框架中的接口。领域层不引用MongoDB类库。MongoDB仓库层使用lambda expression 实现类的Map。

二、ID 生成器

有多种ID生成器可供选择。GuidGenerator,OjbectIdGenerator,String OjbectIdGenerator,etc。我们ID一律使用String类型。所以直接使用MongoDB的StringObjectIdGenerator。

三、多态类的Map

如果把多态类(继承)映射到MongoDB,需要指定已知类型。

四、一些需要了解的约定

NamedIdMemberConvention可以指定类的哪些属性可以作为ID。

IgnoreExtraElementsConvention可以忽略Document中不存在于类中的字段,否则会抛出异常。

EnumRepresentationConvention可以指定枚举序列化的方式,我们都指定为BsonType.String。

MongoDB聚合框架(C#)

一、聚合框架

MongoDB2.2版本引入了此功能,是数据聚合的一个新框架。

这个框架一是对文档进行“过滤”,也就是筛选出符合条件的文档;二是对文档进行“变换”,也就是改变文档的输出形式。其他的也包括按照某个指定字段分组和排序等。

它其实是MapReduce的替代方案,但比MapReduce简单。

该框架使用声明性管道符号来支持类似SQL 中的Group by 操作的功能。不需要自己编写自定义的JavaScript。

二、管道操作符

$project:数据投影,主要用于重命名、增加和删除字段。

$match:过滤操作,筛选符合条件文档,作为下一阶段的输入。

$limit:限制经过管道的文档数量。

$skip:从待操作集合开始的位置跳过文档的数目。

$unwind:将数组元素拆分为独立字段。

$group:对数据进行分组。

$sort:对文档按照指定字段排序。

$geoNear:会返回一些坐标值,这些值以按照距离指定点距离由近到远进行排序。这个在地理信息系统中比较常用。

总结

对于大多数的聚合操作,聚合管道可以提供很好的性能和一致的接口。

使用起来比较简单,和MapReduce一样,它也可以作用于分片集合。

输出的结果只能保留在一个文档中,要遵守BSON Document大小限制(当前是16M)。

管道对数据的类型和结果的大小会有一些限制,对于一些简单的固定的。

聚集操作可以使用管道,但是对于一些复杂的、大量数据集的聚合任务还是使用MapReduce。

今天的分享就到这里,谢谢大家!