任何一个App都须要一个Server,咱们认为,移动开发者(或组织)不该该把精力放在这些事情上面:java
搭建后端Server服务。git
编写后端Server代码。github
设计Server底层数据存储架构。web
关注Server的高可用、可扩展、负载均衡、高性能等诸多繁琐问题。redis
这些事情可能会耗掉你80%以上的时间和精力,结果服务可用性、稳定性、可扩展性等均可能不尽人意。spring
咱们还认为,你应该把更多的精力聚焦在业务和App开发上面,这样不只节约了时间和金钱,更重要的是你能把全部精力都放在业务自己上面,让你的应用在竞争中脱颖而出,领先竞争对手。数据库
简单来说就是针对App开发者提供的统一的后端对象数据存储服务,让用户再也不关心Server端数据存储的问题,CloudData具备关系型、半结构化、json格式及单个对象具备事务性等特色,采用MongoDB 3做为存储系统,至于CloudData更多细节请参考MaxLeap CloudData相关文档。编程
CloudData架构很是的简单,从上往下看,包括API Server、数据访问逻辑处理层和底层数据存储层,下面,咱们分别介绍三个模块的具体实现原理。json
在Api Server层面,咱们的语言采用的是java,针对java,当前比较流行的方式多是:选择一个web 框架,多是spring mvc,使用java servlet,而后选择一款 java web 容器,多是tomcat或者jetty等,但咱们并无采用这样方式,由于,在咱们看来,这种方式有几个缺点:架构显得有些笨拙,每次启动都须要部署到java web 容器中,不够轻量、同步通讯,并发上不去,性能较低。所以,经过综合考量,咱们选择了vert.x web框架,vert.x 基于事件驱动、非阻塞通讯机制,这意味着,只须要不多的线程就能应付大量的并发,同时它也更加的轻量,拥有更高的性能,启动vert.x web服务只须要几十毫秒到几百毫秒之间,使用也是异常的简单,只要你稍稍懂一点http协议及网络编程几乎能够说是零学习成本:后端
HttpServer server = vertx.createHttpServer(); Router router = Router.router(vertx); router.route("/index").handler(routingContext -> { HttpServerResponse response = routingContext.response(); response.putHeader("content-type", "text/plain"); // Write to the response and end it response.end("Hello World Maxleap!"); }); server.requestHandler(router::accept).listen(10086);
须要注意的是,vert.x web基于EventLoop单线程模式,因此,在写web服务的时候,不能写同步处理逻辑,不然会阻塞主线程,致使其余请求不能获得响应;另外,有异步编程经验的同窗必定感觉到这样的痛苦:callback方法泛滥成灾,大量的flatMap方法调用致使也致使代码的可读性差,除此以外,咱们实在是找不到vert.x web框架的任何缺点。固然,这些缺点也并非不能够避免,基于此,咱们对vert.x web,稍稍加了一点东西,改变了一些实现策略:咱们在逻辑处理阶段将异步变成同步,在框架后面,咱们在变成异步,而且增长了对JAX-RS 部分规范支持,若是以前你以为,基于vert.x web写rest接口还有一点点的学习成本,但如今基于咱们改变以后的vert.x web写rest接口,你会发现跟之前并无两样:
@GET @Path(":className/:objectId") public void get(RoutingContext context) { func(context, (ctx, cloudCode, appId, className, principal) -> { LASObject doc = lasDataEntityManager.get(appId, className, principal, new ObjectId(context.request().getParam("objectId"))); if (doc == null) { ctx.response().end("{}"); } else { ctx.response().end(MongoJsons.serialize(doc)); } }); }
更多的Api Server相关细节请参考Maxleap的开源BaaS系统实现 https://github.com/MaxLeap/MyBaaS
这一层包含了CloudData的核心逻辑实现,相对来说,是CloudData设计中最复杂的一层,固然,仅仅只是相对,全部的逻辑处理都在这一层,而Pandora是其具体实现,因此,咱们重点会讲一讲Pandora的设计思路
Pandora是古希腊神话中最美的女人,她充满诱惑,携带危险来到人间,固然,在咱们看来,危险与诱惑跟美丽的女人无关,若是说美丽的人会让你身处危险之中,那就请让我万劫不复吧。
扯远了,Pandora是Maxleap统一数据访问层的实现,主要是针对mongo数据的操做,因此,不只仅是用在CloudData中,在Maxleap中全部应用中,须要访问mongo数据库的都是经过Pandora来进行的,而且提供了异步和同步两种接口
Pandora主要实现了两种模式的数据访问,CloudData和NativeData。
Clouddata是Maxleap App数据的访问,凡是访问应用的数据,都是经过这个模块,CloudData数据的访问稍显复杂一些,包含Pointer、Relation等自定义数据结构的实现;数据ACL的实现;自定义操做指令如$relationTo等
NativeData是对mongo指令友好的封装,与mongo原生指令并无两样。
Pandora最为核心的功能是实现了资源限制和数据库访问的路由策略,这对数据库进行平滑的动态扩展及迁移提供了可能性,固然,目前Maxleap也实现了这一点,当用户的应用由于数据量、访问量上升须要升级后台mongo数据库的时候,这一切都是无感知的,不会影响你的线上服务照常运行,固然,某些特殊状况下,可能须要短暂的停顿服务几分钟,而且仅仅是写停顿,读不受影响。
在Maxleap项目中,使用Pandora最大的好处是,开发人员无需关心后台mongo的架构,数据一致性、mongo服务升级降级等诸多繁琐问题,固然,你甚至连数据存储在那个mongo集群都不要知道,由于,使用Pandora我实在想不到你还须要了解数据存储在哪里的理由。
若是你对Baas有些许,若是你去Maxleap提供的服务有所了解,我想,你必定会关心Cloudapp后端数据存储架构是怎么设计的,成千过万的应用数据咱们是怎么存储的、你的应用可用性问题、服务是否有足够的保障,接下来,咱们正要介绍这一点。
先看一下业界某BaaS服务的Clouddata的设计:
(图片来自网络)
在咱们看来,这是一种糟糕的设计,若是咱们没有理解错的话,全部的用户数据都在一个大集群里面,后端是一个大的sharded集群,全部的用户数据的访问都会经由一组mongos路由服务(毫无疑问,这绝对是一个风险,试想,若是mongos出了问题,整改后端数据存储就会down掉),固然,这并非主要问题,主要问题在于:
一、数据在一个庞大的集群中,出现问题的几率也会大不少,好比有10000个应用,那么出现问题的几率也会大10000倍,1个应用出现一次问题,那么整个服务都会由于某一个应用出现问题而受到影响。
二、数据规模大了,运维困难,又如,若是mongo异常down掉,恢复时间绝对的是一个煎熬的过程,而这个时候,整个服务都不可用。
三、稳定性较差,每个应用都有可能发送使人恶心的指令,可能会致使资源抢占(好比最多见的状况是没有索引的查询,会引发热数据污染,结果集返回大量数据,抢占了网络带宽等),这个时候,整个集群都会受到影响,可能致使整个集群的吞吐量降低,反应到应用层面,可能就是大量应用请求短期超时,读写失败
这是咱们Maxleap其中的一台产品Mongo Server所表现的性能,固然,你可能会惊讶于数据在磁盘也可以拥有4000/s的吞吐量,固然,这一切得益于咱们使用SSD硬盘所带来的效果。这张图也告诉咱们,尽量的当心你的查询,当服务器趋于吞吐平稳的时候,不要形成内存数据污染,但若是是全部的应用在一个大集群,这种状况将不可避免,由于,你永远都不会知道用户会写怎样的查询指令,而一旦有这样的指令,影响将会扩散到整个大集群其余的应用。
四、效率低下,须要更多的硬件成本,好比,当你的读写压力变大的时候,你不得不加新的机器去copy集群的全量数据,形成磁盘和内存的浪费,由于你应用的热数据在每个mongo节点都有(固然,为何我列在第四点,可能你以为这并不重要,硬件历来都不是问题,更重要的是,你以为,公司历来都不缺这点钱)
好了,是时候看看咱们的CloudData的设计了,固然,也是极其的简单,遵循3个原则:
一、用户资源隔离(毫无疑问,这点是最重要的)
二、动态可无限扩展(无论你有多少个库,1万仍是100万)
三、分而治之
基于以上3点,咱们的设计是为每个应用建立一个独立mongo集群,是否是太简单了,有木有,可是,咱们认为这是最科学的,将大集群按照应用分解成小集群,大问题分解成小问题,以大化小,分而治之,无论在哪里,都是靠谱的,而且还特别有效,最近几年,火的不要不要的被普遍传播的软件架构方式‘微服务’不也是一样的道理么?固然,你能够说,微服务更倾向于‘单一职责’原理,固然,只要你高兴,随你怎么想。
也许,你可能会质疑这是否会浪费资源,由于,每一个人都会有这样直观上的错觉,可是,事实上,不只没有浪费资源,还会大大的节约资源,这一点,在上面已经讨论到,数据在一个大集群中,副本集越多,浪费的内存越多,磁盘越多。第二点,起一个mongo集群,并不会浪费多少内存。
固然更重要的是,咱们并非真正为每个应用建立一个mongo集群,毕竟,这样作,不少时候并无意义,由于,90%的应用可能并无较高的访问量,50%的以上的应用,可能历来都不会被频繁访问。
因此,咱们的策略是,当用户第一次注册Maxleap建立一个应用的时候,用户的数据放在一个默认的集群中,在Maxleap中,这个集群叫User Default Cluster,当你的用户有必定的访问量的时候(没有访问量也没有关系,只要你是咱们的付费用户)咱们会把你公司全部的应用部署在一个单独的mongo集群中,再日后,你应用访问量继续上升,咱们就把你的应用在单独升级到一个mongo集群,固然,这一切的升级过程,都是平滑无缝的进行的,没有人可以感知的到,包括咱们本身。
So,咱们Maxleap CloudData是这样子的:
好了,关于Pandora及CloudData咱们先简单介绍到这里,更多的细节咱们到时候会专门拧一个细节出来讨论。
上面咱们早已经知道,咱们使用的Mongo做为Clouddata做为主要存储系统,更早以前,在咱们mongo还在2.6以前,咱们还使用了redis做为热数据缓存,但后来我mongo升级到3.0以后,变去掉了redis,因此,整个底层数据存储,就只剩下mongo,整个系统设计又进一步变得更加简单,之因此去掉redis,这不是咱们所要讨论的重点,咱们会在相关的文章中进行详细说明。
这里,咱们重点介绍mongo的集群架构设计方案
集群特色:
每个mongo集群都是一个复制集:1主1从1投票节点,还有一个备份节点(Norns-backup是咱们自主实现的一个增量实时备份系统)
每个mongo集群都在一个VPC网络里面
每个mongo集群3个节点都在不一样的可用区里面(不一样的机房,机房是光纤直连,网络延迟大概咋500us)
部分mongo集群的数据存储在LVM上面
每个mongo集群都使用SSD硬盘
每个mongo集群咱们都提供瞬时10倍以上的请求峰值
原文做者来自 MaxLeap 团队 基础服务及架构组成员:赵静
关于MaxLeap
MaxLeap移动云服务平台为企业提供一站式的移动研发和运营云服务,帮助企业快速研发和上线移动应用,平台提供数据云存储,云引擎,支付管理,IM,数据分析和营销自动化等服务。
MaxLeap官网连接: https://maxleap.cn
若是您正在学习移动研发和云服务等方面的讯息,不妨关注咱们的微信服务号MaxLeapSvc,咱们将不按期推送相关干货。敬请期待!