移植 express.js 应用到函数计算

本文涉及的产品
简介: 背景 目前有很多 web 应用是基于 express 框架写的,这样的 web 应用按照传统的部署方式可能部署在云主机上,用户可能不想购买云主机,也不想在运维上投入太多成本,函数计算是一个不错的选择。

背景

目前有很多 web 应用是基于 express 框架写的,这样的 web 应用按照传统的部署方式可能部署在云主机上,用户可能不想购买云主机,也不想在运维上投入太多成本,函数计算是一个不错的选择。函数计算的入口方法如何适配 express 是一个相当复杂的问题,我们需要适配 http 触发器和 API 网关这两种类型,因为,这两种类型的函数方法签名是不一样的。比如 API 网关方式触发函数,需要把 event 映射到 express 的 request 对象上,而 express 的 response 对象需要映射到 callback 的数据参数上。

现在,我们提供了一个 npm 包,基于该 npm 包,可以将函数计算的请求转发给 express 应用,几行代码可以实现。

如果你需要快速开始,可以参考另一篇文章:开发函数计算的正确姿势——移植 Express

使用说明

安装相关 npm 包

npm install @webserverless/fc-express express

http 触发器类型函数

const proxy = require('@webserverless/fc-express')
const express = require('express');

const app = express();
app.all('*', (req, res) => {
  res.send('hello world!');
});

const server = new proxy.Server(app);

module.exports.handler = function(req, res, context) {
  server.httpProxy(req, res, context);
};

API 网关类型函数

const proxy = require('@webserverless/fc-express')
const express = require('express');

const app = express();
app.all('*', (req, res) => {
  res.send('hello world!');
});

const server = new proxy.Server(app);

module.exports.handler = function(event, context, callback) {
  server.proxy(event, context, callback);
};

http 触发器类型自定义 body

http 触发器触发函数,会通过流的方式传输 body 信息,我们可以通过 npm 包 raw-body 来获取,获取流中 body 信息需要特别注意一点:在 node8 版本以下(包括 nodejs8),获取 body 信息的代码逻辑一定要在其他 await 或者 promise.then 等方法的前面,在某些特殊场景下,可能需要让 server.httpProxy 方法需要在一个 await 代码后面执行,再这种情况下,我们就需要自己手动获取 body,然后通过一种特殊的方式传递给代理服务。本质原因与 nodejs 的 Eevent Loop 机制有关。代码如下

const proxy = require('@webserverless/fc-express')
const express = require('express');
const getRawBody = require('raw-body');

const app = express();
app.all('*', (req, res) => {
  res.send('hello world!');
});

const server = new proxy.Server(app);

const init = async () => {
    .....
}

module.exports.handler = async (req, res, context) => {
  req.body = await getRawBody(req); // 本行代码一定要放到其他 await 代码之前
  await init();
  server.httpProxy(req, res, context);
};

获取请求头

我们在浏览器端设置好请求头,@webserverless/fc-express 会将我们的请求头透传给 express 应用的 request 对象,通过 express 的 request 对象直接获取我们设置的请求头

设置响应头

我们只需要按照 express 方式设置好 response 的响应头,@webserverless/fc-express 会把该响应头透传出来,在浏览器可以获取透传出来的响应头。

Server 说明

@webserverless/fc-express 包导出了一个 Server 类,Server 负责构建代理服务,转发请求到 express 应用。

构造函数定义:

Server(
  requestListener: (request: http.IncomingMessage, response: http.ServerResponse) => void,
  serverListenCallback?: () => void,
  binaryTypes?: string[]
  )

构造函数参数说明:

参数 类型 必填 说明
requestListener (request: http.IncomingMessage, response: http.ServerResponse) => void 被代理的 express 应用
serverListenCallback () => void http 代理服务开始监听的回调函数
binaryTypes string[] API 网关触发方式才有效,当 express 应用的响应头 content-type 符合 binaryTypes 中定义的任意规则,则返回给 API 网关的 isBase64Encoded 属性为 true

成员方法:

方法 参数 返回值 说明
proxy (event, context, callback) void 当你的函数通过 API 网关触发,就需要使用 proxy 方法将函数计算的处理代理给 express 应用,参数对应着 API 网关类型的入口函数的参数
httpProxy (request, response, context) void 当你的函数通过 http 触发器触发,就需要使用 httpProxy 方法将函数计算的处理代理给 express 应用,参数对应着 http 触发器类型的入口函数的参数

成员属性:

属性 类型 说明
rawServer http.Server 负责将请求转发 express 应用的底层代理服务对象

API 网关中的 isBase64Encoded 参数

有两个地方会有 isBase64Encoded 参数:

  1. 函数 event 参数中包含的 isBase64Encoded 参数
  2. 函数返回值中包含的 isBase64Encoded 参数

当函数的 event.isBase64Encoded 是 true 时,我们会按照 base64 编码来解析 event.body,并透传给 express 应用,否则就按照默认的编码方式来解析,默认是 utf8。

当 express 应用响应的 content-type 符合 Server 构造函数参数 binaryTypes 中定义的任意规则时,则函数的返回值的 isBase64Encoded 为 true,从而告诉 API 网关如何解析函数返回值的 body 参数。

业务代码中获取函数 context 和 event 方法

我们提供了一个 express 中间件,用来获取函数的 event 和 context 对象,其中 event 对象,只有在 API 网关触发函数的时候才会有,且 event 是 JSON.parse 后的对象。代码如下:

const proxy = require('@webserverless/fc-express')
const express = require('express');
const app = express();
app.use(proxy.eventContext())
app.all(/.*/, (req, res) => {
  console.log(req.eventContext.event); // http 触发器方式,没有 event 对象
  console.log(req.eventContext.context);
  res.send('hello world!');
});

const server = new proxy.Server(app);

module.exports.handler = function(event, context, callback) {
  server.proxy(event, context, callback);
};

eventContext 中间件之所以能解析到 event 和 context 两个参数,是因为我们会将这两个参数序列化后,通过请求头透传给了 express 应用的 reques 对象。
eventContext 中间件提供了一个配置参数 options,options 参数是选填的,其中包含了两个属性 reqPropKey 和 deleteHeaders:

参数 类型 默认值 必填 说明
reqPropKey string 'eventContext' 控制从请求头解析出 event 和 context 对象放到 request 对象的属性名称,默认是 eventContext,则获取方式为 request.eventContext.event
deleteHeaders boolean true 控制从请求头解析出 event 和 context 后,是否需要删除与 event 和 context 相关的请求头,默认会删除

需要考虑的问题

  • 无状态的。所以移植后的 express 也需要是无状态的,像 express session 就没法简单的用起来了,可以考虑使用 jwt 或者将状态持久化到相关存储中
  • 冷启动。第一次访问有冷启动时间,一段时间没有请求,函数计算会释放掉实例,下次再有请求过来,也会有冷启动时间,可以通过预热来解决,另外,打包压缩代码,也可以减少冷启动时间
  • 部分浏览器请求对象属性没有从函数计算中透传出来,比如:protocol、hostname 等,所以在 express 应用中无法获取
  • 函数计算的最大超时时间 600 秒,API 网关最大超时时间是 30 秒,如果你使用了 API 网关,请确保你的请求能在 30 秒内处理完,如果你使用了 http 触发器,请确保你的请求能在 600 秒内处理完
  • 无法使用本地库(像 Addons

小结

使用 @webserverless/fc-express 包,我们可以几行代码让 express 接入函数计算,@webserverless/fc-express 会帮我们做很多适配的事情,让我们尽可能接近原生的方式使用 express 框架,适配的逻辑对用户是透明的。另外,我们还提供了一个 fun 模板,帮助我们更快地搭建一个基于函数计算的 express 项目,预置了编译、打包、调试和发布等开箱即用的功能,可以参考另一篇文章:开发函数计算的正确姿势——移植 Express

相关实践学习
基于函数计算一键部署掌上游戏机
本场景介绍如何使用阿里云计算服务命令快速搭建一个掌上游戏机。
建立 Serverless 思维
本课程包括: Serverless 应用引擎的概念, 为开发者带来的实际价值, 以及让您了解常见的 Serverless 架构模式
目录
相关文章
|
4天前
|
设计模式 前端开发 JavaScript
【JavaScript 技术专栏】JavaScript 设计模式与实战应用
【4月更文挑战第30天】本文探讨JavaScript设计模式在提升开发效率和代码质量中的关键作用。涵盖单例、工厂、观察者、装饰器和策略模式,并通过实例阐述其在全局状态管理、复杂对象创建、实时数据更新、功能扩展和算法切换的应用。理解并运用这些模式能帮助开发者应对复杂项目,提升前端开发能力。
|
4天前
|
开发框架 JavaScript 前端开发
【JavaScript 与 TypeScript 技术专栏】TypeScript 在 Web 开发中的前沿应用
【4月更文挑战第30天】TypeScript在Web开发中日益重要,以其强大的类型系统提升代码质量,支持组件化开发,与React、Vue、Angular等框架良好集成。在大型项目管理中,TypeScript助于代码组织和优化,提高团队协作效率。此外,它提升开发体验,提供智能提示和错误检测。众多成功案例证明其前沿应用,未来将在Web开发领域持续发挥关键作用。
|
4天前
|
前端开发 JavaScript 网络协议
【JavaScript技术专栏】WebSockets在JavaScript中的应用
【4月更文挑战第30天】WebSocket是为解决HTTP协议在实时通信上的局限而诞生的技术,提供全双工、持久连接的通信方式,适合在线聊天、实时游戏等场景。JavaScript中的WebSocket API使浏览器与服务器能建立持久连接,通过事件处理连接、发送/接收数据及错误。相较于AJAX轮询和长轮询,WebSockets更高效、实时,是现代Web实时通信的优选。
|
4天前
|
移动开发 JavaScript 前端开发
【JavaScript技术专栏】Web Worker在JavaScript中的应用
【4月更文挑战第30天】HTML5的Web Worker API解决了JavaScript单线程性能瓶颈问题,允许在后台线程运行JS代码。本文介绍了Web Worker的基本概念、类型、用法和应用场景,如复杂计算、图像处理和数据同步。通过实例展示了搜索建议、游戏开发和实时数据分析等应用,并提醒注意其无法直接访问DOM、需消息传递通信以及移动端资源管理。Web Worker为前端开发提供了多线程能力,提升了Web应用性能和用户体验。
|
5天前
|
JSON 前端开发 JavaScript
使用JavaScript制作一个简单的天气应用
使用JavaScript制作一个简单的天气应用
|
5天前
|
运维 Serverless 应用服务中间件
Serverless 应用引擎产品使用之在阿里云Serverless中函数计算FC nginx 部署上去之后放置静态页面如何解决
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
35 0
|
5天前
|
弹性计算 运维 Serverless
Serverless 应用引擎产品使用之在阿里函数计算中,使数据库和阿里云函数计算位于同一个内网中如何解决
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
17 0
Serverless 应用引擎产品使用之在阿里函数计算中,使数据库和阿里云函数计算位于同一个内网中如何解决
|
5天前
|
运维 监控 Serverless
Serverless 应用引擎产品使用之在阿里函数计算中,在response.send()之后继续执行其它代码如何解决
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
23 0
|
5天前
|
运维 负载均衡 Java
Serverless 应用引擎产品使用之在阿里函数计算中,创建了个关于 stable diffusion 的函数服务,总是很卡如何解决
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
21 0
|
5天前
|
人工智能 运维 Serverless
Serverless 应用引擎产品使用之在阿里函数计算中使用自定义字体如何解决
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
18 0

热门文章

最新文章

相关产品

  • 函数计算