背景
有赞是 SaaS 公司,向商家提供了全方位的软件服务,支撑商家进行采购、店铺、商品、营销、订单、物流等等管理服务。
在这个软件服务里,能够满足大部分的商家,为商家保驾护航。
但是很多大商家往往会有自己的特殊需求,如使用自己的优惠券、分期支付、定制的商品详情页、定制下单流程等等,为了能够满足这样的需求,让商家有更多的可能性,我们提供了很强的扩展能力,这是电商云的核心。
概述
前面提到,为了满足商家的定制需求,提供商家更多可能性的能力,我们需要提供很强的扩展能力,扩展能力简单来说就是用代码开发扩展服务,然后通过界面配置来编排扩展服务。
用代码开发扩展服务,我们需要有一个应用框架来支持扩展服务代码的开发。
应用框架
应用框架包含代码部署、应用开发框架、应用运行框架。
看到这里应该会提出几个问题,代码部署在哪里?扩展服务或者扩展点是什么?
扩展点是什么
扩展点是由有赞核心系统定义的一组扩展 API ,分为业务扩展点、消息扩展点和前端扩展点。
业务扩展点
从技术上可以理解为 Java 的 SPI ,举个例子,有赞核心系统价格中心提供了附加费用计算扩展点,对于 Java 程序来说这就是一个接口类,开发者自己写一个实现类去实现该接口并在我们的控制台上进行相应的配置生效后,那么在进行价格计算时会调用开发者编写的 附加费用计算扩展点实现类,形成的结果就是在标准价格计算之外再会加上开发者实现类返回的附加费用(比如额外的关税、境外的运输费等等)。
消息扩展点
一样的,消息扩展点从技术上可以理解为 Java 的 SPI。从命名上可以看出它是用来传递消息的,也就是说这个扩展点不会影响业务主流程。比如,有消费者进入店铺了,商家希望获得这个消息可以做一些事情去尽可能的挽留消费者;比如有消费者下单成功了,通过对应的消息扩展点发送一个消息,商家可以自己去做些更有价值的事情。
前端扩展点
业务扩展点和消息扩展点对应的都是后台逻辑,我对某个页面(如下单页)不满意或者想扩展里面的组件或者想自己添加一个组件怎么办?前端扩展点给了开发者进行完全前端定制的能力,这里举两个例子来说明:
拓展官方组件:在下单页面上有下单按钮,下单按钮原有的逻辑是下单,但是下单按钮的事件它是一个前端扩展点,这个时候开发者就可以实现这个扩展点,在下单的过程中做点好玩的事情,比如蹦出个好玩的动画、请求后台的额外接口做些事情等等;
添加自定义组件:如在下单页添加个组件,添加一个商品推荐的组件。
代码部署
如图中所示,横线(粗黑线)上方是电商云部分的服务和操作区,下方是开发者的操作区。
这里的代码提交和正常的 Git 管理代码方式不太一样,所以这个图也复杂一点:
电商云的 Git 仓库不做代码管理,这个仓库的用途主要是用来初始化工程代码和进行代码部署。
代码从电商云 Git 仓库拉下来之后,开发者可以使用自己公司内部的 Git 去管理该项目,开发完成后再提交到电商云的 Git 仓库。
在提交电商云 Git 仓库时只能提交 master 分支。
提交到电商云 Git 仓库后,可以去电商云控制台进行代码发布,部署到服务器上。
所以在这个工程中会发现,电商云 Git 不承担团队管理、分支管理等等功能,他的作用主要是用来发布。
代码开发框架
整体的 App 是一个 youzan-boot 项目,整体框架分两部分,内部核心模块和外部定制模块,两个模块单独维护单独开发,互相隔离。
内部核心模块
这是一个可以单独运行的模块,在该模块里有很多子模块,定义了接口、工具类、实现类等等,除此之外还有应用启动器,这个模块的开发由有赞内部的开发人员负责开发,实现内容外部不可见,那么我们会在这个模块里做哪些事情呢?
定义标准,编写接口,供外部开发者依赖实现;
编写监控实现,来监控应用框架以及外部定制模块;
编写 dubbo 服务注册发现组件;
编写自动化数据源和 orm 框架支持;
基础配置信息;
各类工具;
其他。
这么做的好处是什么?
开发者只关注如何使用,不需要关注实现;
内部核心模块有些部分会和我们核心域通信,隔离保证安全;
我们内部开发人员更懂我们自己的环境,我们来进行一些通用配置,更加简化开发者的学习成本;
必要时候可以限制开发者的行为;
我们可以改变底层实现,如缓存模块,从 redis 换成 zankv 等等;
可以在未来定制一些符合有赞体系的组件如用户 session 等,而用户不需要去感知不需要去改代码就能直接用。
外部定制模块
这个模块是通常意义上的 App ,也就是开发者可见并进行开发的 App,该模块同样有很多子模块,可以理解为正常的一个业务项目,只不过没有启动器没有启动框架,内部核心模块启动时会加载本模块。
通过上图可以看到,这个外部定制模块跟一般的 Maven 没什么区别:
电商云 App 是一个 maven 项目,根 pom 的 parent 是 cloud-parent ,cloud-parent 管理了 spring-boot、spring、jdk、内部对外的 API 等框架和 API 的版本。所以在 App 开发过程中开发者依赖这些体系的 jar 包时不需要指定版本。
扩展点开发
扩展点开发是基于 Maven 框架去做的,有赞内部通过电商云把扩展点接口发布到有赞云 Maven 仓库,开发人员在本地配置有赞云 Maven 仓库后就可以拉取扩展点接口包,进行扩展点实现的开发。
代码运行框架
上图中可以看到,开发者将外部定制模块开发完后通过电商云控制台进行发布,此时会将 App 打成一个 Jar 包发到服务器上,发布系统会启动内部核心模块,在启动时内部核心模块会启动 Aladdin (有赞 Jar 包加载容器),Aladdin 会将外部定制模块的 Jar 包加载进来,启动里面的业务逻辑,完成发布。
为什么这么做
其实前面讲了很多 App 的总体框架,也讲了一些这样做的好处,这里主要列举三点来阐述一下:组件开放、组件升级和安全性。
组件开放
在设计电商云应用框架时,考虑到的一点就是如何更便捷更快速的把我们有赞的能力开放出去,如何做更新的迭代,如今天开放了数据库组件、明天开放队列服务?如何做到先准备后开放?如何让开发者的接入成本最低?
有赞内部其实有很多很好的组件,有些可以开放在开发者,有些可能无法开放。同时开源也有很多组件,如何把这些能力集成到电商云平台上?
经过讨论和思考,决定以接口声明的模式(对于 Java 来说就是 Interface Jar 包)对外提供服务。
这种方式对于开发者来说是非常便捷的:
结合电商云控制台来完成组件申请和配置初始化,开发者只需要在代码里写 Bean 注入代码就行,比如 Redis:
如上所示,开发者只需要写这样的代码就可以使用,无需关注配置,也不需要做太多的依赖。
组件开放也很简单:
编写 SDK;
按一定规范编写电商云 Interface;
将 SDK 实现放在内部核心模块,并使用电商云组件配置加载逻辑初始化 SDK;
提供简单文档供开发者使用;
在这个过程中,由于电商云组件的使用方式是统一的,都通过一定规范的 Interface 开放,所以不需要做太多的 SDK 兼容去兼容开发者各种各样的使用场景。
开发者也只需要一种学习姿势,不会遇到不同的组件用不同的方式的困惑。除此之外也建立了电商云组件开放的规范。
组件升级
有赞通过 Interface 的方式对外开放组件服务,那么该组件的实现类就会落到内部核心模块里,也就是说我们的服务实现对于开发者来说是个黑盒,有赞来负责对这个实现的开发、监控、管理,在未来的发展过程中,由于一些原因可能需要对实现进行修改和升级,这是很正常的情况,通过这种模式,开发者无感,开发者只关注业务扩展、方案实现。
组件的实现方式也分两种,一种是组件的实现源码直接写在内部核心模块工程里,另一种是单独一个 Jar 包(内部核心模块依赖这个 Jar 包)。
对于第二种模式,有赞有对应的 Jar 包管理工具 Aladdin,在应用启动时会匹配当前版本是否为最新版本,不是的话就会升级。
同时在这个开发过程中,假如核心模块和外部模块都依赖了同一个 Jar 包,但是不同的版本怎么办? Aladdin 也会去识别出来,只加载一个版本。
安全性
组件安全
对于开发者可见的只有自己的业务代码,组件对于开发者来说是个黑盒,开发者无法去对组件实现植入代码,这样保证了组件实现的安全可控运行。
如果不是这种模式,而是将组件作为一个 client 开放给开发者,首先开发者能够看到我们组件的逻辑,第二个能够对组件实现做一些入侵(如修改字节码)。
数据安全
如上图所示,app-biz 即开发者部分的 App,和有赞进行实线连接的只有我们提供的接口层(Java 的 Interface),接口层的实现由 App 基础框架来负责,来访问配置、访问数据库、访问缓存。
虽然每个 App 的数据库、缓存等等相互之间是隔离的,但是他们可能都在一个机房或者一个网络环境,如果不做这种封装,让 App 直接访问这些资源,很难保证不会对其他 App 的数据库、缓存等等造成影响或者攻击。
目前这种模式天然的保证了每个 App 的私有资产的安全性。
展望
电商云目前处于开荒后的创世阶段,我们希望能够让开发者和商家有更多的可能性,那么落到应用框架上,就需要有更多的灵活性和更多的能力来支持。
首先、完善组件,开放更多的组件;
第二、支持多语言,目前不管是组件还是应用框架都只支持 Java;
第三、支持本地运行,前面讲到 App 无法自己运行,也就说明 App 无法在开发者本地运行,所以未来要支持本地运行;
第四、支持远程调试;
第五、完善应用框架,如监控、热加载等等;
其他……
创世阶段需要更多人才,也希望各路神仙加入展现绝技。
评论