2016 年 Google I/O 大会上,Google 彻底改造了 Firebase 这个 realtime database,将文件存储、推送、统计等众多开发者服务打包到一起,使之成为了下一代的云计算平台。在此之前,云计算领域的标杆企业 Amazon,也于去年推出了 MobileHub 产品,将 Amazon AWS 原来的单项服务统合起来,提供应用开发的一站式解决方案,并且在今年年初,又推出了一个名为 Lambda 的新服务。
说起云计算,大家比较熟悉的有 IaaS、PaaS、SaaS,对于上面的新形态,业界给它取了一个新名字:BaaS(Backend as a Service),是说将产品开发中所有后端的需求,通过统一的云平台来解决,这样产品开发团队只需要关注客户端的开发,重点打磨用户体验,快速、高效做出更好的产品。
理想是美好的,但是现实中这都行得通吗?我们知道,业务的需求千变万化,一个统一的云平台,一套代码,真能解决所有问题吗?如果能,又是怎么做到的呢?笔者这里就这些技术细节来跟大家做一个探讨。
后端服务其实也是高度模式化的
提起后端开发,我们最先想到的可能就是 LAMP(Linux+Apache+MySQL+PHP) 技术框架,从系统架构上来看,一个典型的业务系统至少包含如下三部分:
在业务发展的过程中,我们工程团队花费了大量的精力,解决的都是「扩展」和「容错」这两个问题,对于核心架构却绝少改变。
同时,因为每个产品具体业务不一样,所以客户端和 App Server 的交互内容千差万别,但有两点是基本一致的:
- 交互协议,基本上都是基于 HTTP(S)。
- 尽管数据格式千差万别,但是「数据」本身是一样。
不论 App Server 内又细分了多少个模块与子服务,它总是将客户端传来的数据,结合业务规则进行处理,并最终持久化到 Database。所以这就给了我们一个启示:如果我们的云平台能支持任意数据的增删改查,而数据本身由应用自解析,这样就能解决大部分产品的后端问题了。所以我们选择了「JSON Object」这一相当宽松的数据模型,作为客户端和云端交互的唯一格式要求,我们并不关心里面有多少属性、取值如何,而且我们也不理解具体的数据含义。现在我们通过这样的方式来提供通用的后端结构化数据存储服务:
这样,应用开发者可以将任意的数据存储到云端,例如我们要实现一个类似于微博的社交 App,主要有三类数据:账户、帖子、评论,一条微博帖子可能包含下面几个属性:
{
"content": "每个 Java 程序员必备的 8 个开发工具",
"pubUser": "LeanCloud官方客服",
"pubTimestamp": 1435541999
}
无需提前创建表格、指定 schema,这样的数据可以直接存入 BaaS 云端(JavaScript 的示例代码如下)。
var Post = AV.Object.extend('Post');// 声明类型
var post = new Post();// 新建对象
post.set('content','每个 Java 程序员必备的 8 个开发工具');// 设置发布内容
post.set('pubUser','LeanCloud官方客服');// 设置发布者信息
post.set('pubTimestamp',1435541999);// 设置时间戳
post.save().then(function (post) {
console.log('objectId is ' + post.id);
}, function (error) {
console.log(error);
});// 保存到云端
客户端和云端交互的问题解决了,而要构建一个能支持大量应用的统一平台,在 App Server 端,我们还有大量的开发工作要做。就 LeanCloud 来讲,我们后端系统的核心架构如下:
这里重点要解决如下几个问题:
- 如何支持任意对象的存储。因为每个应用的 domain class 都各不相同,我们需要能统一存储到一个集群里面去,这就需要后端存储系统支持 schema free 的特性,能够随时应对新结构、属性的追加,而同时我们又要保证现有数据的 schema 不能遭到破坏。我们的存储集群综合使用了关系型数据库(MySQL)、NoSQL 数据库(MongoDB)以及 Hadoop/Hbase 等开源系统来完成这一需求。
- 如何保证应用之间的数据隔离。在我们的存储系统里,我们为每一个应用建立了单独的「Database」,以保证应用之间数据是物理隔离的,同时我们所有的数据请求都需要带上应用标识,在开发者没有明确设置共享的情况下,跨应用的数据访问是绝对禁止的。
- 如何保证数据安全。我们做了多方面的努力,来保证各应用的数据安全:
- SSL 加密传输。我们所有的 API 请求都通过 SSL加密传输,保证传输过程中的数据安全性和可靠性。并且,我们的 iOS/Android SDK 还加入了防范中间人攻击的措施,彻底排除网络抓包和嗅探的威胁。
- 身份鉴别(Authentication)。我们云端对客户端发过来的每一个请求,都进行了身份鉴别(Authentication)和访问授权(Authorization)的严格检查。BaaS 平台为每一个应用准备了标识信息(应用 id 和应用 key),在每一次网络请求中,我们都要求 SDK 带上 app 标识信息(鉴权签名),以此来对请求发起方进行身份鉴别。
- 访问授权(Authorization)。Authentication 解决的是冒名访问的问题,Authorization 则对一个合法用户的操作进行权限检查,以保证登录者不能看到或者破坏 ta 本没有权限操作的数据。BaaS 平台对每一条数据都提供「ACL」(Access Control List,访问控制列表),以供开发者进行更细粒度的控制。
- Web 应用安全域名。在通用的两层鉴权机制之外,我们额外增加了 Web 应用的安全域名设置,以此来对请求来源进行限制,防御住 Web 应用的服务器资源盗取问题。
- 其它安全措施。我们还有更多的安全选项和数据备份设置,具体可以登录我们的 BaaS 平台了解更多。
- 如何应对大流量、保证高性能。压力和性能看起来是一对矛盾,但其实它们是一个硬币的两面,我们讨论性能的时候都需要明确是在多大的访问压力下得出的。为了保证在流量增长的情况下,还有出色的性能,我们在架构设计上对 scale up/scale out 做了很多努力,譬如,内部系统都尽量采用微服务的架构,使用 Apache mesos/marathon/Docker 和自动化运维技术来管理资源池并实现自动扩展,等等。
业务逻辑千差万别,通用平台如何解决特定需求
大家可能会觉得,上面的方案只解决了基本的数据增删改查的问题,并不能覆盖复杂的业务逻辑。例如我是一个网商产品,消费者下单购买了某个商品,后台除了需要生成订单、更新库存之外,还需要能通知到物流等其他环节,这时候怎么办?
我们提供了代码扩展的功能——云引擎。这是一个给后端 Web 开发者的零管理计算平台,使用方式上与 AWS 的 Lambda 一样,开发者自己用 Node.js / Python / PHP 等语言实现自己的核心业务逻辑,然后部署到 BaaS 平台的独立容器中,就可以完成一些特定的业务需求了。开发者无需关心具体的代码部署、负载均衡以及平行扩展,都由 BaaS 平台来保证的,所以说这是一个「零管理」的计算平台。
开发者可以使用云引擎来完成如下功能:
- 定义新的函数,供客户端直接调用,完成特定的业务逻辑,以此来自由扩展数据存储的标准 API 接口;
- 定义 Hook 函数,响应特定事件(譬如某类数据被修改、被删除等),完成各种关联操作,其作用类似于数据库的存储过程或 trigger。
- 甚至,还可以用来进行网站托管,这就和大家熟知的 App Engine 差不多了。
为了保障性能,在云引擎中我们也提供云缓存(类似于 memcached、redis)等服务,这样方便开发者轻松实现各种个性化场景功能。
结论
使用第三方云服务已经是大势所趋,在新的产品开发流程里,我们希望让更多的团队忘掉「infrastructure」,能够集中精力在解决用户的问题,而不要浪费时间在打造「又」一个复杂的后端系统。