灰度环境
首先说下为什么需要灰度环境。随着业务的复杂度以及技术复杂度的上升,导致在测试环境可能有些问题无法全面复现,以及复杂度上升可能带来的某些配置的不同步等等原因,会导致测试环境看起来是没有问题的需求,到了线上反而出现了的问题,为了尽早发现这些问题,以及降低这些问题带来的影响,就需要一个和线上环境几乎一样的环境来做最后的质量把控。
为什么不是预发环境?其实我之前的项目中很多都是使用预发环境的,而且业界使用预发环境的企业也不在少数,那我们为什么使用实现难度更大的灰度环境呢?肯定是有好处的啊!第一,从开发的角度,使用预发环境要么使用预发域名,要么使用预发机器IP 绑定,操作起来比较麻烦,对非开发同学不友好。第二,灰度环境可以让真正的用户进行使用,也可以作为A/B Test 使用。总结下就是预发环境能做的灰度环境基本都能做,预发环境不能做的灰度环境也能做。所以我们使用灰度环境。
既然要用灰度环境,首先要确定的是通过什么方式来区分用户进入灰度或正式环境。网易互客作为一个toB 的产品,当然希望是同一个公司的所有员工能够得到同样的使用体验和功能。所以我们通过企业 ID 来控制用户进入灰度或正式环境。
【示意图】
确定方案之后就是技术实现了,我们之前的网关只能通过请求的path 来区分请求,来控制流量转发到 Java 或 Node 应用。现在我们需要网关能够区分请求中的企业 ID,所以需要对网关进行升级,于是我们使用新的技术方案 Eureka(Netflix开发的服务发现框架),这里不对 Eureka 做过多介绍,主要说下 Node 端要实现的 **Eureka Client**,用来完成服务注册及维持。
npm 中已经有了 eureka-node-client 来实现 Node 端的服务注册功能,但是要结合 Egg.js,以及平滑发布的理念,还是需要进一步的开发,以实现一个更完善的工具,所以我们实现了 `pp-eureka` 这个 Egg.js 插件。
考虑到Egg.js 的多进程模型,为了防止一台机器多次注册,我们通过扩展 agent 来实现这个插件的功能。主要代码如下:
```javascript
const Eureka = require('eureka-node-client');
module.exports = agent => {
const eureka_client = new Eureka(agent.config.eureka);
eureka_client.start(function (error) {
agent.logger.info(error || '启动成功!');
});
}
```
如果仅仅是这样倒也能实现灰度的需求,但是损失了上文提到的平滑发布,所以又增加了一个middleware 来监听发布系统的上下线请求,然后通过 `app.messenger.sendToAgent(OFFLINE, '');` 来通知 agent ,agent 代码升级后如下:
```javascript
const Eureka = require('eureka-node-client');
module.exports = agent => {
const eureka_client = new Eureka(agent.config.eureka);
eureka_client.start(function (error) {
agent.logger.info(error || '启动成功!');
});
agent.messenger.on(OFFLINE, data => {
eureka_client.stop();
});
}
```
Agent 收到下线通知后会让 Eureka 停止注册服务的心跳,这样 Eureka 注册中心就会把这台 Node 机器踢掉,流量就不会转发进来,以免在发布过程中导致请求失败。插件开发好之后怎么用呢?首先是在 config 目录下增加 config.gray.js 灰度环境配置文件,里面添加 pp-eureka 的配置,如下:
```json
eureka: {
eureka: {
serviceUrls: {
default: [
'url'
]
}
},
instance: {
app: 'huke',
port: { '$': 7001, '@enabled': true },
metadata: {
ysf_app: 'huke',
ysf_env: 'gray'
}
}
}
```
同时线上环境也增加对应的配置,只是`eureka.instance.metadata.ysf_env = 'prod'` ,通过这个配置来区分是灰度环境还是线上环境。这样就完成灰度环境工程相关的部分,剩下就是把所有的请求都默认带上当前企业的 ID 就完成了。
自此应用发布相关的问题都已基本解决,剩下的就是要随时了解自己的应用的运行状态了。
应用监控
应用上线之后,为了了解应用的运行状态、服务是否稳定、有没有潜在问题,我们需要应用监控,有了应用监控以后能帮我们解决以下问题:
1. 能够让业务流转调用链可追踪,能够知道一个请求在哪里出了问题,方便解决
2. 能够了解应用的系统指标,比如:Load、CPU、内存、磁盘、网络、TCP 等
3. 能够在应用状态异常的时候及时发送通知给开发者,把影响降到最小
关于网易内部的工具我就不多介绍了,主要介绍下可以使用的第三方平台或工具。
首先说下Sentry,它是一个实时事件日志记录和聚合平台。我们之前使用它来做前端代码的错误监控以及关键数据的统计。因为它也能支持 Node 端,所以我们就顺便接入进来了,同时实现了 `pp-sentry` Egg.js 插件。使用的时候只需要在 config 中配置 sentry project 的 dsn 即可捕获 Node 中代码错误。
还有阿里开源的Pandora.js,是一个 Node.js 应用监控管理器。它集成了多种类型的能力诸如:监控、链路追踪、调试、进程管理等等。
另外就是阿里云的Node.js 性能平台,如果使用 Egg.js 框架的话,接入非常方便,使用官方提供的 egg-alinode,参考 [Egg 集成部署_部署 runtime 与 agenthub_用户指南_Node.js 性能平台-阿里云](https://help.aliyun.com/document_detail/60907.html?spm=a2c4g.11186623.6.555.41d676bfwLNpaH) 这个文档即可,功能全面,关键是免费。如果还有对日志更高的要求,可以使用阿里云的日志服务。
利用好上面的工具能对解决应用中出现的问题提供很大的帮助,另外在代码需要的地方打上日志也是非常必要的,Egg.js 提供了非常完善的日志功能,使用好它对了解应用的运行状态以及排查问题都有很好的帮助。
总结
至此我要分享的关于智慧企业Node.js 接入实践已经结束,但是要开发好 Node 应用要考虑的还有很多。首先在开发思路上要和写前端代码有个区分,要具备服务端开发的思考能力,对性能、稳定、健壮等的考虑要更多。另外要养成良好的打日志习惯,这个非常重要。还有单元测试也是非常重要的,写单元测试是服务端开发的基本要求。Node 端开发对运维能力也有一定的要求,不像前端代码,发上 CDN 之后基本就不需要关注了,但是做 Node 开发,代码上线之后也要时刻关注应用的状态,以及会不会有报错等,要具备快速定位、解决问题的能力,将可能出现的问题导致的损失降到最小。Node的接入整体上对开发效率的提升还是很显著的,而且通过 Node 前端可以做的更多,让前端发挥更大的价值。