高质量Node.js微服务的编写和部署

  微服务架构是一种构造应用程序的替代性方法。应用程序被分解为更小、完全独立的组件,这使得它们拥有更高的敏捷性、可伸缩性和可用性。

  一个复杂的应用被拆分为若干微服务,微服务更需要一种成熟的交付能力。持续集成、部署和全自动测试都必不可少。编写代码的开发人员必须负责代码的生产部署。构建和部署链需要重大更改,以便为微服务环境提供正确的关注点分离。

  后续我们会聊一下如何在时速云平台上集成 DevOps。

物联网

  Node.js 是构建微服务的利器,为什么这么说呢,我们先看下 Node.js 有哪些优势:

  Node.js 采用事件驱动、异步编程,为网络服务而设计

  Node.js 非阻塞模式的IO处理给 Node.js 带来在相对低系统资源耗用下的高性能与出众的负载能力,非常适合用作依赖其它IO资源的中间层服务

  Node.js轻量高效,可以认为是数据密集型分布式部署环境下的实时应用系统的完美解决方案。

  这些优势正好与微服务的优势:敏捷性、可伸缩性和可用性相契合(捂脸笑),再看下Node.js 的缺点:

  单进程,单线程,只支持单核CPU,不能充分的利用多核CPU服务器。一旦这个进程 down 了,那么整个 web 服务就 down 了

  异步编程,callback 回调地狱

  第一个缺点可以通过启动多个实例来实现CPU充分利用以及负载均衡,话说这不是 K8s 的原生功能吗。

  第二个缺点更不是事儿,现在可以通过 generator 、 promise 等来写同步代码,爽的不要不要的。

  下面我们主要从 Docker 和 Node.js 出发聊一下高质量Node.js微服务的编写和部署:

  Node.js 异步流程控制:generator 与 promise

  Express、Koa 的异常处理

  如何编写 Dockerfile

  微服务部署及 DevOps 集成

  1. Node.js 异步流程控制:Generator 与 Promise

  Node.js 的设计初衷为了性能而异步,现在已经可以写同步的代码了,你造吗?

  目前 Node.js 的 LTS 版本早就支持了 Generator , Promise 这两个特性,也有许多优秀的第三方库bluebird、q 这样的模块支持的也非常好,性能甚至比原生的还好,可以用 bluebird 替换Node.js 原生的 Promise:

  global.Promise = require('bluebird')

  blurbird 的性能是 V8 里内置的 Promise 3 倍左右(bluebird 的优化方式见 https://github.com/petkaantonov/bluebird/wiki/Optimization-killers )。

  1.1 ES2015 Generator

  generator 就像一个取号机,你可以通过取一张票来向机器请求一个号码。你接收了你的号码,但是机器不会自动为你提供下一个。

  换句话说,取票机“暂停”直到有人请求另一个号码( next() ),此时它才会向后运行。下面我们看一个简单的示例:

物联网
物联网

  从上面的代码的输出可以看出:

  generator 函数的定义,是通过 function *(){} 实现的

  对 generator 函数的调用返回的实际是一个遍历器,随后代码通过使用遍历器的 next() 方法来获得函数的输出

  通过使用 yield 语句来中断 generator 函数的运行,并且可以返回一个中间结果

  每次调用 next() 方法,generator 函数将执行到下一个 yield 语句或者是 return 语句。

  下面我们就对上面代码的每次next调用进行一个详细的解释:

  第1次调用 next() 方法的时候,函数执行到第一次循环的 yield index++ 语句停了下来,并且返回了 0 这个 value ,随同 value 返回的 done 属性表明 generator 函数的运行还没有结束

  第2次调用 next() 方法的时候,函数执行到第二循环的 yield index++ 语句停了下来,并且返回了 1 这个 value ,随同 value 返回的 done 属性表明 generator 函数的运行还没有结束

  … …

  第4次调用 next() 方法的时候,由于循环已经结束了,所以函数调用立即返回,done 属性表明 generator 函数已经结束运行, value 是 undefined 的,因为这次调用并没有执行任何语句

  PS: 如果在 generator 函数内部需要调用另外一个 generator 函数,那么对目标函数的调用就需要使用 yield* 。