Serverless 一般翻译为「无服务架构」,是一种软件系统设计架构思想和方法,并非一个开发框架或者工具。他的出现是为了让开发者更加关注业务的开发,而将繁杂的运维和部署交给云厂商。Serverless 由 Faas 和 Baas 组成,Faas 为开发者提供业务运算环境,而后与 Baas 提供的数据和存储服务,进行交互,从而提供与传统服务一致的体验。可是因为 Faas 是无状态的,而且其运行环境是有读写限制的,最重要的是它是基于事件触发的。所以若是传统 Web 服务想迁移到 Serverless 上,是须要进行相关改造和特殊处理的,为此迁移成本是必不可少的。本文将具体帮助你们剖析下,如何 Serverless 化传统的 Web 服务。html
读完本文将了解到:node
Web 服务定义:git
Web 服务是一种
面向服务的架构
(SOA) 的技术,经过标准的 Web 协议提供服务,目的是保证不一样平台的应用服务能够互操做。github
平常生活中,接触最多的就是基于 HTTP 协议的服务,客户端发起请求,服务端接受请求,进行计算处理,而后返回响应,简单示意图以下:web
传统 Web 服务部署流程:一般须要将项目代码部署到服务器上,启动服务进程,监听服务器的相关端口,而后等待客户端请求,从而响应返回处理结果。而这个服务进程是常驻的,就算没有客户端请求,也会占用相应服务器资源。express
通常咱们的服务是由高流量和低流量场景交替组成的,可是为了考虑高流量场景,咱们须要提供较高的服务器配置和多台服务进行负载均衡。这就致使服务处在低流量场景时,会多出不少额外的闲置资源,可是购买的资源却须要按照高流量场景进行付费,这是很是不划算的。npm
若是咱们的服务能在高流量场景自动扩容,低流量场景自动缩容,而且只在进行计算处理响应时,才进行收费,而空闲时间不占用任何资源,就不须要收费呢?api
答案就是 Serverless
。promise
上面已经提到了 Serverless 的两个核心特色:按需使用和收费
和 自动扩缩容
。并且近几年 Serverless 的应用也愈来愈普遍,可是它并非银弹,任何技术都是有它的适合场景和不适合场景。咱们不能由于一项技术的火热,而盲目的追捧。Serverless 是有它的局限性的,通常 Serverless 适合以下几种场景:浏览器
若是你的服务不知足以上条件,笔者是不推荐迁移到 Serverless。
若是你的服务是以上提到的任何话一个场景,那么就能够尝试迁移到 Serverless 上。
常见的 Serverless HTTP 服务结构图以下:
那么咱们如何将 Web 服务进行迁移呢?
咱们知道 Faas (云函数)是基于事件触发的,也就是云函数被触发运行时,接收到的是一个 JSON 结构体
,它跟传统 Web 请求时有区别的,这就是为何须要额外的改造工做。而改造的工做就是围绕如何将事件 JSON 结构体转化成标准的 Web 请求
。
因此 Serverless 化 Web 服务的核心就是须要开发一个 适配层
,来帮咱们将触发事件转化为标准的 Web 请求。
整个处理流程图以下:
接下来将介绍如何为 Express 框架开发一个适配层。
首先咱们先来看看一个标准的云函数结构:
module.exports.handler = (event, context) => { // do some culculation return res; };
在介绍如何开发一个 Express 的适配层前,咱们先来熟悉下 Express 框架。
一个简单的 Node.js Web 服务以下:
const http = require("http"); const server = http.createServer(function (req, res) { res.end("helloword"); }); server.listen(3000);
Express 就是基于 Node.js 的 Web 框架,而 Express 核心就是 经过中间件的方式,生成一个回调函数
,而后提供给 http.createServer()
方法使用。
Express 核心架构图以下:
由此可知,咱们能够将 Express 框架生成的回调函数,做为 http.createServer()
的参数,来建立可控的 HTTP Server,而后将云函数的 event
对象转化成一个 request
对象,经过 http.request()
方法发起 HTTP 请求,获取请求响应,返回给用户,就能够实现咱们想要的结果。
对于 Node.js 的 HTTP Server,能够经过调用 server.listen()
方法来启动服务,listen()
方法支持多种参数类型,主要有两种监听方式 从一个TCP端口启动监听
和 从一个UNIX Socket套接字启动监听
。
server.listen(port[, hostname][, backlog][, callback])
:从一个TCP端口启动监听server.listen(path, [callback])
:从一个UNIX Domain Socket启动监听
服务器建立后,咱们能够像下面这样启动服务器:
// 从'127.0.0.1'和3000端口开始接收链接 server.listen(3000, '127.0.0.1', () => {}); // 从 UNIX 套接字所在路径 path 上监听链接 server.listen('path/to/socket', () => {})
不管是 TCP Socket
仍是 Unix Domain Socket
,每一个 Socket 都是惟一的。TCP Socket
经过 IP和端口
描述,而 Unix Domain Socket
经过 文件路径
描述。
TCP 属于传输层的协议,使用 TCP Socket 进行通信时,须要通过传输层 TCP/IP 协议的解析。
而 Unix Domain Socket
可用于不一样进程间的通信和传递,使用 Unix Domain Socket
进行通信时不须要通过传输层,也不须要使用 TCP/IP 协议
。因此,理论上讲 Unix Domain Socket
具备更好的传输效率。
所以这里在设计启动服务时,采用了 Unix Domain Socket
方式,以便减小函数执行时间,节约成本。
关于 Node.js 如何实现 IPC 通讯,这里就不详细介绍的,感兴趣的小伙伴能够深刻研究下,这里有个简单的示例,nodejs-ipc
原理大概介绍清楚了,咱们的核心实现代码须要如下三步:
function createServer(requestListener, serverListenCallback) { const server = http.createServer(requestListener); server._socketPathSuffix = getRandomString(); server.on("listening", () => { server._isListening = true; if (serverListenCallback) serverListenCallback(); }); server .on("close", () => { server._isListening = false; }) .on("error", (error) => { // ... }); server.listen(`/tmp/server-${server._socketPathSuffix}.sock`) return server; }
function forwardRequestToNodeServer(server, event, context, resolver) { try { const requestOptions = mapApiGatewayEventToHttpRequest( event, context, getSocketPath(server._socketPathSuffix), ); // make http request to node server const req = http.request(requestOptions, (response) => forwardResponseToApiGateway(server, response, resolver), ); if (event.body) { const body = getEventBody(event); req.write(body); } req .on('error', (error) => // ... ) .end(); } catch (error) { // ... return server; } }
function forwardResponseToApiGateway(server, response, resolver) { response .on("data", (chunk) => buf.push(chunk)) .on("end", () => { // ... resolver.succeed({ statusCode, body, headers, isBase64Encoded, }); }); }
最后函数的 handler 将异步请求返回就能够了。
若是不想手写这些适配层代码,能够直接使用 tencent-serverless-http 模块。
它使用起来很简单,建立咱们的 Express 应用入口文件 sls.js
:
const express = require("express"); const app = express(); // Routes app.get(`/`, (req, res) => { res.send({ msg: `Hello Express`, }); }); module.exports = app;
而后建立云函数 sl_handler.js
文件:
const { createServer, proxy } = require("tencent-serverless-http"); const app = require("./app"); exports.handler = async (event, context) => { const server = createServer(app); const result = await proxy(server, event, context, "PROMISE").promise; };
接下来,将业务代码和依赖模块一块儿打包部署到云函数就能够了(记得指定 执行方法
为 sl_handler.handler
)。
除了 Express
框架,其余的 Node.js 框架也基本相似,只须要按照要求,exports
一个 HTTP Server
的回调函数就能够。
好比 Koa
,咱们拿到初始化的 Koa
应用后,只须要将 app.callback()
做为 createServer()
方法的参数就能够了,以下:
const { createServer, proxy } = require("tencent-serverless-http"); const app = require("./app"); exports.handler = async (event, context) => { // 这里和 Express 略有区别 const server = createServer(app.callback()); const result = await proxy(server, event, context, "PROMISE").promise; };
对于非 Node.js 框架,好比 Python
的 Flask
框架,原理都是同样的,核心只须要作到 将 Serverless Event 对象转化为 Http 请求
,就能够了。因为笔者对其余语言不太熟悉,这里就不作深刻介绍了,感兴趣的小伙伴,能够到 Github 社区搜索下,已经有不少对应的解决方案了,或者本身尝试手撸也是能够的。
读到这里,相信你已经清楚,如何将本身的 Node.js 框架迁移到 Serverless 了。可是在这以前,咱们都是手动处理的,并且每次都须要本身建立 handler.js
文件,仍是不够方便。
为此开源社区提供了一套优秀的解决方案 Serverless Component,经过组件,咱们进行简单的 yaml
文件配置后,就能够方便的将咱们的框架代码部署到云端。
好比上面提到的 Express
框架,就有对应的组件,咱们只须要在项目根目录下建立 serverless.yml
配置文件:
component: express name: expressDemo inputs: src: ./ region: ap-guangzhou runtime: Nodejs10.15 apigatewayConf: protocols: - https environment: release
而后全局安装 serverless
命令 npm install serverless -g
以后,执行部署命令便可:
$ serverless deploy
耐心等待几秒,咱们的 Express 应用就成功部署到云端了。更多详细信息,请参考 Express 官方文档
注意:本文 Serverless 服务均基于
腾讯云
部署。
Serverless Express 组件不只能帮咱们快速部署 Express 应用,并且它还提供了 实时日志
和 云端调试
的能力。
只须要在项目目录下执行 serverless dev
命令,serverless 命令行工具就会自动监听项目业务代码的更改,而且实时部署,同时咱们能够经过打开 Chrome Devtools 来调试 Express 应用。
关于云端调试,腾讯云 Serverless Framework 正式发布公告 中有详细的介绍,而且有视频演示。
并且除了 Express 组件,还支持: Koa.js,Egg.js,Next.js,Nuxt.js.....
固然 Serverless 化 Web 服务并无本文介绍的那么简单,好比文件读写,服务日志存储,Cookie/Session
存储等......实际开发中,咱们还会面临各类未知的坑,可是比起困难,Serverless 带给咱们的收益是值得去尝试的。固然传统 Web 服务真的适合迁移到 Serverless 架构上,也是值得咱们去思考的问题,毕竟现有的 Web 框架都是面向传统 Web 服务开发实现的 (推荐阅读 利与弊-传统框架要不要部署在 Serverless 架构上)。可是笔者相信,很快就会出现一个专门为 Serverless 而生的 Web 框架,能够帮助咱们更好地基于 Serverless 开发应用 ~
3 秒你能作什么?喝一口水,看一封邮件,仍是 —— 部署一个完整的 Serverless 应用?
复制连接至 PC 浏览器访问:https://serverless.cloud.tencent.com/deploy/express
3 秒极速部署,当即体验史上最快的 Serverless HTTP 实战开发!
传送门:
- GitHub: github.com/serverless
- 官网:serverless.com
欢迎访问:Serverless 中文网,您能够在 最佳实践 里体验更多关于 Serverless 应用的开发!