移植 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 架构模式
目录
相关文章
|
7天前
|
运维 Serverless 应用服务中间件
Serverless 应用引擎产品使用合集之关于OSS映射目录的大小限制,如何可以跳过
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
Serverless 应用引擎产品使用合集之关于OSS映射目录的大小限制,如何可以跳过
|
5天前
|
分布式计算 Hadoop Serverless
数据处理的艺术:EMR Serverless Spark实践及应用体验
阿里云EMR Serverless Spark是基于Spark的全托管大数据处理平台,融合云原生弹性与自动化,提供任务全生命周期管理,让数据工程师专注数据分析。它内置高性能Fusion Engine,性能比开源Spark提升200%,并有成本优化的Celeborn服务。支持计算存储分离、OSS-HDFS兼容、DLF元数据管理,实现一站式的开发体验和Serverless资源管理。适用于数据报表、科学项目等场景,简化开发与运维流程。用户可通过阿里云控制台快速配置和体验EMR Serverless Spark服务。
|
7天前
|
运维 Serverless API
Serverless 应用引擎产品使用合集之通过 API 调用 /tagger/v1/interrogate 时,出现unsupported protocol scheme "" 错误,如何处理
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
|
7天前
|
缓存 运维 监控
Serverless 应用引擎产品使用合集之在使用函数计算 FC 部署 stable-diffusion 应用时,选了 tagger 扩展插件却拿不到提示词,还报错“Error”,是什么原因
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
|
7天前
|
运维 Kubernetes Java
Serverless 应用引擎产品使用合集之如何设置能让应用定时启停
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
|
7天前
|
域名解析 运维 网络协议
Serverless 应用引擎产品使用合集之一般情况下在SAE中如何关联域名
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
|
7天前
|
运维 Serverless 测试技术
Serverless 应用引擎产品使用合集之在SAE 2.0中,如何区分生产环境和测试环境
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
|
3天前
|
监控 JavaScript 前端开发
JavaScript与Nest.js:打造高性能的服务器端应用
Nest.js是Node.js的渐进式框架,融合OOP、FP和FRP,提供模块化、装饰器和依赖注入,助建高性能服务器应用。选择Nest.js的原因包括模块化设计、简洁的装饰器API和高性能基础(如Express或Fastify)。开始使用需安装Node.js和`@nestjs/cli`,创建项目、编写控制器。深入学习涉及模块化、服务的依赖注入及中间件。安全性优化涵盖HTTPS、CORS策略、限流和性能监控。
|
6天前
|
负载均衡 安全 Java
Java中的无服务(Serverless)应用最佳实践
Java中的无服务(Serverless)应用最佳实践
|
6天前
|
JavaScript 前端开发 大数据
Vue.js 中的 `v-if` 和 `v-show`:理解与应用
Vue.js 中的 `v-if` 和 `v-show`:理解与应用
15 0

热门文章

最新文章

相关产品

  • 函数计算