大部分人认同开发微信小程序或SPA(WEB单页应用)是实现“前后端分离”的。所有业务数据都是“后端应用”通过HTTP接口提供给“前端应用”。一个“完整的应用”被物理隔离为两个独立的应用,并由擅长的开发者负责,从而实现职责分离:
前端开发者:负责 View 和 Controller 层。
后端开发者:只负责 Model 层,业务处理/数据等。
“前后端分离”是强调“职责分离”,上面应用分离的例子是为了方便理解接受,可以不应用分离。通常应用分离能大量减少不小心的越界行为。
分离前:前端开发者提供静态页面,后端开发者完成 Controller 和 View 层代码。但前端开发者经常会插一脚修改 Controller 和 View 层代码。
分离后:面向用户的 Controller 和 View 层都由前端开发者做;后端开发者只需复杂 Model 层,提供业务数据接口。
在自己服务器测试两种场景。
服务器配置:
腾讯云服务器(Ubuntu 16.04.1 LTS)
CPU: 2核
内存:4GB
带宽:2M
Node.js环境:
Node.js版本:v8.12.0(Alinode v3.12.0)
框架:Egg.js
worker进程数:2
PHP环境:
PHP版本:7.2.10
框架:Lumen
php-fpm最大子进程数:5
两种场景测试代码:
场景一,直接输出字符串,不做任何IO和计算。
PHP效率比Node.js高
Node.js:
// egg-4asyncfunctiontest(ctx){ ctx.body ='<h1>goddess.daifee.com</h1><p>Egg程序</p>';}
egg-4.png
PHP:
// php-4$router->get('/test',function()use($router){return'<h1>goddess-php.daifee.com</h1><p>这是PHP服务</p>';});
php-4.png
场景二,模拟200ms的网络请求。
Node.js效率比PHP高。相对“场景一”PHP效率下降明显,Node.js不明显。
Node.js:
// egg-4—test2asyncfunctiontest2(ctx){conststart =Date.now();awaitnewPromise(resolve=>{ setTimeout(()=>{ resolve(true); },200); });constoffset =Date.now() - start; ctx.body =`
goddess.daifee.com
Egg程序${offset}
`;}
egg-4-test2.png
PHP:
// ./WeTest_[php-4—test2]_20181014172129.pdf$router->get('/test2',function()use($router){ $start = microtime(true); usleep(200000); $offset = microtime(true) - $start;return"<h1>goddess-php.daifee.com</h1><p>这是PHP服务 {$offset}</p>";});
php-4-test2.png
3.2 基于Egg.js框架开发(编码)
下面用Egg.js框架开发一个页面为例(浏览器端开发模式不需要改变)。
开发一个新页面(用户主页页),只需要下面4个步骤:
模板:创建模板文件。
<!-- app/views/user.ejs -->这里是 ejs 模板
数据接口:几行代码封装数据接口。
// app/service/user.jsconst{ Service } =require('egg');module.exports =classUserServiceextendsService{asyncget(userId) {consturl =`https://api.gateway.com/xxx/${userId}`;// http请求/ajaxconstresponse =awaitthis.app.curl(url);returnresponse; }}
控制器:过滤/验证用户输入、验证权限、调用数据接口、渲染模板。
// app/controller/user.jsconstBaseController =require('./base-controller');module.exports =classUserControllerextendsBaseController{asyncprofile() {const{ params, service } =this.ctx;// 只有自己才能访问自己主页。不是自己就抛出 403 异常this.assertUser(params.userId); user =awaitservice.user.get(params.userId);awaitthis.render('user', {user: user}); }}
路由:一行代码声明路由。
// app/router.jsmodule.exports =app=>{const{ router, controller, middleware } = app;const{ authorize } = middleware;const{ user } = controller;// 已登录用户才能访问router.get('/users/:userId', authorize.user, user.profile);}
只负责 Controller 和 View 层的Node.js服务端开发非常简单。一个企业应用除了开发,还有其他环节需要做好。
3.3 我们还缺什么?
前端开发者可以在服务端写 Controller 和 View 层代码,但整个生产环节还缺什么?
3.3.1 前端开发者
既然前端开发者需要做更多,能做更多,所以工作量增加。所以缺前端开发者。
同一个项目,编码的工作量增加了。
前端同事需要关注、分析、协助维护Node.js服务器。
肯定会有更多的xxx小程序需求。
3.3.2 适合我们的框架和项目脚手架
确定了web网站应用使用Egg框架,还需为不同业务场景的项目创建拿来即用的脚手架。
未确定React Isomorphic应用的框架。我体验过next.js,是一个不错的选择。
3.3.3 构建/部署服务
目前还没有Node.js项目的构建/部署服务。
基本需求:
构建:开发者只需关注源码,“构建服务”自动构建项目。
发布:支持选择git tag构建,部署到“生产环境”。
发布:支持选择git branch构建,部署到“测试环境”。
回滚:支持指定版本回滚。
重启:支持重启Node.js服务器。
发布:允许测试人员发布“测试版本”。
3.3.4 严谨的git工作流
需要依据“构建/部署服务”定制严谨的git工作流。
3.3.5 风险监控和日志采集
风险监控可以考虑Alinode。
完全免费
支持性能监控、安全提醒、故障排查、性能优化等
支持手动下载性能数据
日志采集接入大数据团队的服务?
关于React Isomrophic
当时用React全家桶做了一个SPA Demo项目,然后用next.js框架再做一个React Isomorphic版。发现可以复用(copy)98%的代码,然后花一点时间将 React Router 替换为 next.js 自带的 Router 即可
基于 next.js 框架 React Isomorphic 应用与 SPA 的开发体验差不多。
毕竟一套代码运行于两个不同环境,React Isomorphic 还是带来了一些坑:
需要意识到哪些代码在2个环境都执行,哪些代码只在其中一个环境执行。
立即执行的代码在2个环境都执行。
React组件生命周期的代码只在Browser环境执行。
Component.getInitialProps()方法在2个环境都执行。
小心使用运行环境的“接口”
next.js自带Router,与“其他Router”肯定存在差异。
某些组件需要用唯一自增ID或随机数,这种做法会使服务端于客户端存在差异。
“服务端”与“客户端”基本只能用cookie共享“状态”,要珍惜cookie资源。
作者:daifee
链接:https://www.jianshu.com/p/1f3f3f70be33
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
个人倒是觉得。如果有条件的话,架构上可以考虑底层接口采用java来提供基层的共性接口,可以保证高并发和稳定性。然后在业务逻辑处理上,采用php进行处理,为前端提供接口服务,而在前端则采用vue来模块化界面。
所以我们就需要有发布系统,发布系统定义了统一的回退操作,所有服务必须遵循发布系统的定义回退操作。 在饿了么对接发布系统是对所有人的强制要求,所有的系统必须全部接入发布系统。发布系统的框架很重要,这个东西其实对于公司是很重要的一件事情,需要放到第一优先级的队列里面去考虑。
四、服务框架
紧接着就是饿了么的服务框架,把一个大的Repo拆分成一个小的Repo,把一个大的服务拆成一个小的服务,让我们的服务尽量独立出去,这需要一套分布式服务框架来支撑。 分布式服务框架包含的服务注册、发现、负载均衡、路由、流控、熔断、降级等功能,这里就不一一展开了。前面已经提及,饿了么是多语言的生态,有 Python的,也有Java的,我们的服务化框架对应也是多语言的。这对我们后来一些中间件的选型是有影响的,比如说DAL层。
五、DAL数据访问层
当业务量越来越大的时候,数据库会变成一个瓶颈。 前期可以通过提升硬件的方式来提升数据库的性能。比如:
升级到一个有更多CPU的机器;
把硬盘改成 SSD 的或者更高级一点的。
但硬件提升终归是有一个容量限制的。而且很多做业务的小伙伴,写代码的时候都直接操作数据库,发生过很多次服务一上线数据库就被打爆的情形。数据库被打爆掉了之后,除非等待数据库恢复,没有任何其它机会可以恢复业务。 如果数据库里面数据是正常的,业务其实都可以补偿出来。所以我们做DAL服务层的时候,第一件事情是限流,其它的东西可以放一放。然后做连接复用,我们Python框架用的多进程单线程加协程的模型。 多进程之间其实是不可以共享一个连接的。比如:一台机器上部署了10个 Python进程,每个进程10个数据库连接。再扩展到10台机器上,就有1000个数据库连接。对数据库来说,连接是一个很昂贵的东西,我们DAL层要做一个连接复用。 这个连接复用讲的不是服务本身的连接复用,而是说DAL层上的连接复用,就是服务有1000个连接到DAL层,经过连接复用后对数据库可能只是保持着十几个连接。一旦发现某个数据库请求是一个事务的话,那么DAL就帮你保留这个连接的对应关系。当这个事务结束之后,就把数据库的连接,放回到共用池里面去,供其他人使用。 然后做冒烟和熔断。数据库也可以熔断的。当数据库发生冒烟时,我们会杀掉一些数据库的请求,保证数据库不至于崩溃。六、服务治理服务框架之后,涉及服务治理的问题。服务治理其实是一个很大的概念。首先是埋点,你要埋很多的监控点。 比如有一个请求,请求成功了或者失败了,请求的响应时间是多少,把所有的监控指标放到监控系统上面去。我们有一个很大的监控屏幕,上面有很多的监控指标。有专门小组72小时去盯着这个屏幕,如果有任何曲线波动了,就找人去解决。另外是报警系统,一个监控屏幕展示的东西总是有限的,只能放那些很重要的关键指标。这个时候就需要有报警系统。 罗马不是一天建成的,基础架构更是一个演进的过程。我们的资源和时间总是有限的,作为架构师和 CTO 来说,如何在这种有限的资源下,产出更重要的东西? 我们做了很多系统,觉得自己做得很不错了,但实则不是,我感觉我们又回到了石器时代,因为问题越来越多,需求也越来越多,总感觉你的系统里还缺点什么东西,想做的功能也一大堆。 比如对于流控系统,现在我们还是需要用户去配一个并发数,那么这个并发数,是不是根本不需要用户去配?是不是可以基于我们服务本身的一个状态自动去控制并发数? 然后是升级方式,SDK升级是个很痛苦的事情。比如说我们服务框架2.0发布的时候是去年12月份,到现在还有人用的是1.0。是不是可以做到SDK的无损感升级,我们自己来控制升级的时间和节奏。 还有,我们现在的监控只支持同一个服务上的汇聚,是不分集群、不分机器的,那是不是以后的指标可以分集群、分机器?举一个最简单的例子,比如一个服务上有10台机器,那么可能只是某一个机器上出了问题,但它所有的指标都会平均分摊到其它的9台机器上去。你只是看到了整个服务延时增加了,但有可能只是某一台机器拖慢了整个服务集群。但我们现在还做不到更多维度的监控。 还有智能化的报警,这个报警,就是要快、全、准,我们现在做到更快了,做到更全了,怎么才能做到更准?每天的报警量高峰时间一分钟一千多个报警发出去。所有的一千报警都是有用的吗?报警多了之后,就相当于没有报警。大家都疲劳了,就不去看了。我怎么能够把这个报警更准确地区分出来?还有更智能化的链路分析?以后是不是我们的监控不要放监控指标,而是放链路分析,这样就能够很清晰地知道,这个问题对应的是哪一个结点上出了问题。 这些问题涉及我们做事的一个原则:东西够用就好,但是要能够未雨绸缪,做一定的超前规划。