☰
API网关+函数计算+云数据库:Serverless内网链路与鉴权实践
2026/10/21 9:35:07 网站建设 项目流程

很多朋友在第一次接触云上接口时,都会遇到一个经典选择:函数计算(FC)已经能挂 HTTP 触发器了,为什么还要在前面再加一层 API 网关?等你在生产环境真正跑过一轮接口,就会明白——入口统一、签名校验、流量控制、调用方管理,这些事如果全写在业务函数里,函数会越来越肿,而且每个接口都要重复实现一遍。把 API 网关放在 FC 前面,由它接管公网入口,业务函数只专注处理请求,数据再通过 VPC 内网落到 RDS,这是目前云原生应用里非常典型的一条链路:HTTP 请求 → 函数 → RDS。

这篇记录就把这条链路从零到尾完整走一遍。我会按照实际动手的顺序来写:先讲清楚为什么要这样分层,再逐步完成 RDS、VPC、FC 和 API 网关的配置,把重点放在鉴权设计上,最后给出联调时的常见问题和排错思路。适合正在做 Serverless 接口改造、或者刚开始接触函数计算和网关的同学参考。

1. 链路拆解:三层架构里每一层到底在干什么

1.1 三层的职责边界

先看完整链路的各个角色。前端发起一次 HTTP 请求,请求先打到 API 网关,网关做完鉴权、限流和协议转换后,把请求转发给 FC,FC 执行业务逻辑,再通过 VPC 内网访问 RDS,拿到数据后原路返回。

API 网关在最前面,本质上是"流量入口和安检门"。它不写业务代码,但管着所有请求的进与出:谁可以调用、每秒能调多少次、请求参数是否符合规范、响应格式是否统一,都在这一层处理。

FC 是业务逻辑的执行者。它不关心请求从哪里来,只关心怎么把数据查出来、算出来、组装好。因为 FC 是按调用次数和资源用量计费,没有请求时实例缩到零,有请求时秒级拉起,所以很适合处理波动大的接口负载。

RDS 是最终的数据落点。它负责持久化存储、索引查询、事务一致性。这里有个关键点:RDS 不直接暴露公网地址,而是通过内网 IP 供 VPC 内的资源访问,FC 要连数据库就必须和 RDS 处于同一个 VPC 内,或者通过互通方式访问。

1.2 为什么不直接用 FC 的 HTTP 触发器

可能有人会问:FC 自带的 HTTP 触发器已经能处理公网请求,再加一层网关不是多此一举吗?

直接使用 HTTP 触发器和加上 API 网关的对比,我整理了一张表:

能力FC 自带 HTTP 触发器API 网关 + FC
签名鉴权需自行实现内置多套方案,配置即可用
流量控制需自行实现按 API、按调用方做限流
调用方管理无通过应用/凭证管理调用方
参数校验需自行实现可定义请求模型,自动校验
公网域名的 HTTPS 证书需自行配置支持托管证书、自定义域名
监控告警只有函数日志网关侧有调用量、错误率、延迟等维度

网关这些能力如果全在函数里手工实现,工作量不小,签名验签、限流算法、参数校验逻辑,每个函数都要写一遍,而且很难做到统一维护。API 网关把这些通用能力下沉到基础设施层,函数代码保持纯粹,这才是 Serverless 该有的分工。

1.3 这条架构更匹配哪种业务

基于我自己的经验,这套链路最适合两类场景。一是小而独立的接口服务,比如用户信息查询、订单状态查询、配置拉取、表单提交这类读写逻辑不复杂、但需要稳定对外提供服务的场景。二是 BFF 层,也就是后端对前端,前端需要聚合多个数据源时,用 FC 做聚合逻辑,网关做统一入口和安全控制。

不过它也有不擅长的场景。如果业务本身就是长连接,比如 WebSocket 实时通信,或者一个请求要跑几分钟甚至更久的重型计算任务,就不适合放在这种架构里。网关超时时间再长也有限度,函数计算对单次执行时长也有上限。做技术选型时先想清楚业务形态,别把架构硬套上去。

2. 前置准备:把 RDS、VPC 和 FC 的网打通

2.1 需要提前开通的云资源清单

动手之前先列一个清单,避免配置到一半发现少了资源。这里假设你要做的是一个简单的用户信息查询接口,底层是一张 MySQL 用户表。

资源用途注意事项
RDS MySQL 实例存储业务数据建议选与 FC 相同的地域和可用区
数据库账号和库表业务数据读写单独建账号,不要用高权限账号跑业务
专有网络 VPC内网互通基础RDS、FC、交换机必须在同一 VPC 体系内
FC 服务和函数执行业务逻辑配置 VPC 访问,绑定交换机和安全组
API 网关分组与 API对外发布接口配置后端指向 FC 函数

需要先开通 RDS、FC、API 网关这三个核心服务,然后再开始配置。

2.2 创建 RDS 与配置访问白名单

创建 RDS 实例时,数据库引擎选择 MySQL 或者符合业务需求的引擎,规格不用一味追求高配。接口查询量不大时,最小规格就够用,后续可以升降配。

创建完成后有两个步骤不能跳过。

首先是创建独立的数据库账号。给业务函数专用的账号,只授予业务需要的库表权限,比如 SELECT、INSERT、UPDATE。这个账号的密码会写进 FC 的环境变量里,所以要单独管理,别和运维管理账号混用。

然后就是大家最常踩的坑:白名单。RDS 默认只允许白名单内的 IP 或安全组访问。要让 FC 能连上数据库,最省事的做法是选择安全组访问方式,把 FC 绑定的安全组加进 RDS 白名单的安全组列表里。如果当时创建 FC 服务时没有把安全组记录下来,也可以直接把 FC 所在 VPC 的交换机网段加入白名单。但安全组的方式更精确,建议优先用安全组。

2.3 为 FC 开启 VPC 访问

FC 默认运行在云厂商托管的公共网络环境里,要访问 VPC 内的 RDS,必须给 FC 服务开启 VPC 配置。

创建服务时,开启"允许访问 VPC"的选项,然后选择与 RDS 相同的 VPC、交换机和安全组。这里有个关键认知:交换机决定了函数实例落在哪个网段,安全组决定了函数对外访问哪些端口。你选择的安全组出方向规则里,要允许访问 RDS 的内网 IP 和 3306 端口。

我一开始就吃过这个亏:VPC 和交换机都选对了,但安全组出方向全是默认规则,函数连数据库时一直超时。后来才发现是安全组规则把访问 RDS 的内网端口挡住了。配置安全组时,除了默认的放行规则,最好显式加一条指向 RDS 内网 IP 的入方向规则,协议选择 TCP,端口填 3306。

2.4 网络配置里最常翻车的三个点

除了白名单和安全组,还有三个细节特别容易出问题,提前给你打了预防针。

第一,地域必须一致。RDS、FC、API 网关建议都选同一个地域,跨地域访问内网是走不通的,就算打通了,延迟也会高得离谱。

第二,交换机别选错。一个 VPC 下可以有很多交换机,RDS 和 FC 所在交换机如果分属不同网段,跨网段访问在没有路由配置的情况下是连不通的。最简单的办法:创建 RDS 时和 FC 服务配置 VPC 时,都选同一个可用区下的同一个交换机。

第三,函数执行超时时间要预留。网络链路多了一层 VPC 访问,如果数据库连接没走连接池,冷启动后第一次建连可能需要几百毫秒甚至更久。函数超时时间不要只设 3 秒,建议根据业务复杂度设置 10 到 30 秒,联调通过后再逐步调小。

3. API 网关:把 FC 函数发布成公网 API

3.1 API 分组:确定 API 的对外域名空间

API 网关里最重要的概念有两个:分组和 API。

分组类似于域名空间的概念。创建一个分组后,网关会分配一个公网域名,这个域名下的所有 API 都属于同一个业务空间。你可以为分组绑定自定义域名,比如api.yourdomain.com,然后把 HTTPS 证书托管到网关,客户端通过自定义域名访问。

一个分组下可以创建很多个 API。每个 API 对应一个具体的路由,比如GET /user/info。分组的作用是把一组相关的接口聚合在一起,同时可以使用同一套域名和网关策略。

3.2 创建 API 并指定后端为 FC

在 API 网关控制台创建 API 时,第二步会让你选择后端服务类型。这里选择函数计算,然后指定对应的服务名、函数名和版本。

第一次配置时可能会遇到授权提示,也就是让 API 网关在转发请求时,拥有调用 FC 函数的权限。控制台一般会引导你完成一键授权,本质是创建一个服务关联角色。这一步不要跳过,否则网关调用 FC 时会被拒绝。

配置后端为 FC,意味着网关和函数之间的调用走的是腾讯云内部的协议转换链路,网关会把标准的 HTTP 请求事件转换成 FC 能识别的结构再传给函数。所以函数里解析 event 时,并不需要关心原始 HTTP 报文细节,直接按云函数的标准事件格式取参数就行。 (注明:这里只是个人最佳实践,具体权限和授权引导以实际控制台为准。)

3.3 配置前端请求定义与参数映射

创建 API 时,核心工作是定义前端请求和后端参数映射。

前端请求定义比较容易理解:填写请求的 Path、请求方法、入参定义。比如我做GET /user/info,入参是userId,位置在 Query 参数里,类型为 Long,必填。如果接口接收 JSON Body,还需要定义 Body 的字段结构。

参数映射的要点在于"透传"和"转换"的选择。最简单的方式是后端参数直接透传,也就是网关收到的参数原封不动传给 FC。更灵活的方式是自定义映射,比如前端参数叫uid,统一在网关层映射成后端的userId,这样前端无需感知后端参数名,后端也可以独立演进。

我建议第一版先做透传,把链路跑通后再考虑参数映射规则。映射配置一旦复杂,排查问题时会增加一层理解成本。

3.4 发布到环境并用控制台快速调试

API 创建完成后,默认是不可被公网调用的,必须发布到指定环境。API 网关一般提供线上、预发、测试三个环境。发布到测试环境后,可以通过控制台自带的调试工具发起模拟请求,先验证网关到 FC 这条链路是否通畅。

调试时有个小技巧:在网关的调试工具里发一次请求,同时在 FC 控制台观察函数日志,看函数有没有被正确触发,以及函数的输入事件里是否包含预期参数。如果函数日志有输出,说明网关到 FC 链路正常;如果函数没日志,排查重心要放在网关的转发配置或者授权上。

4. 鉴权配置:从网关到数据库的防护设计

4.1 防护模型:哪些入口需要做鉴权

鉴权设计前先理清一个问题:这条链路里到底有几个入口?

第一个入口是公网客户端访问 API 网关,这是主要的攻击面,任何知道 API 域名的人都能发请求进来,所以这里是鉴权的核心。第二个入口是网关调用 FC,这个链路在云内部进行,有云平台的安全隔离,但依然需要确保只有网关有权限调用函数。第三个入口是 FC 访问 RDS,这层靠 VPC 和安全组隔离,不是常规鉴权手段,但属于基础设施层的防护。

三层防护叠下来,实际设计中最重要的判断是:网关层做哪些鉴权,函数层做哪些兜底,数据库层如何配合。下面我给出三种网关层鉴权方案,以及 FC 侧的兜底建议。

4.2 方案 A:APP 签名鉴权

API 网关自带一套基于阿里云 AccessKey/Secret 的签名鉴权机制,在控制台创建应用后,每个应用会得到一对 AppKey 和 AppSecret。

开启方式很简单:在 API 的鉴权设置里选择阿里云 APP 鉴权,同时将该 API 授权给对应应用。客户端发起请求时,需要使用 AppSecret 对请求参数进行签名,生成的签名放在 Header 里,网关收到请求后会验签,校验通过才放行。

这套方案的优势是云平台托管,不需要自己在函数里写验签代码。缺点是签名逻辑比较繁琐,客户端需要依赖官方 SDK 或自行实现签名算法。如果调用方是自己公司的服务端程序,用 SDK 还可以接受;如果是外部第三方开发者,让他们按签名规范接入,沟通成本会高一些。

4.3 方案 B:JWT 插件鉴权

JWT 鉴权是我个人最推荐的方案,尤其是需要对接外部调用方时。

JWT 的全称是 JSON Web Token,本质是一个自包含的令牌,服务端签发、客户端携带、网关验签。令牌里可以包含用户标识、过期时间、权限范围等信息,网关收到请求后解析令牌,校验签名和有效期。校验通过后,网关还会把令牌里的 claims 信息作为 Header 传给 FC,这样函数不用再解析一遍 JWT,直接读 Header 就能拿到用户上下文。

API 网关的 JWT 插件配置核心是设置公钥。签发 JWT 的服务端持有私钥,网关持有公钥,双方使用 RS256 非对称签名算法。配置项大致类似这样:

{ "payload": { "tokenLocation": "HEADER", "tokenName": "Authorization", "type": "jwt", "jwks": { "keys": [ { "kty": "RSA", "alg": "RS256", "kid": "your-key-id", "n": "你的公钥模数", "e": "AQAB" } ] }, "claims": [ {"name": "iss", "value": "your-issuer"}, {"name": "aud", "value": "your-audience"} ] } }

配置时公钥要转换成 JWKS 格式,也就是 JSON Web Key Set。很多在线工具都能将 PEM 格式公钥转成 JWKS,转换时注意 kid 要对应。

这套方案的优点很明显:对调用方相对友好,只要会生成 JWT 就能接入;网关侧的验签和 claim 校验完全托管,函数代码不用关心验签细节;JWT 本身无状态,方便水平扩展。缺点是需要自己搭建签发服务,JWT 的密钥管理要做好,私钥一旦泄露,所有签发的令牌都不可信。

4.4 方案 C:自定义鉴权函数

如果已有自己的账号体系,比如内部实现了 OAuth2 或自定义 Token 机制,API 网关也支持自定义鉴权插件。可以将鉴权逻辑写成一个独立的 FC 函数,网关在转发请求之前,先调用这个鉴权函数,函数返回通过或拒绝的结果。

自定义鉴权的优势是灵活,可以对接任何已有的认证服务。比如请求里带一个自定义 Token,鉴权函数去 Redis 里查这个 Token 是否有效、用户 ID 是什么,然后把结果传递给后端函数。

需要注意性能问题。每个请求都会先经过鉴权函数,相当于多了一次函数调用,延迟会增加。鉴权函数里建议加简单缓存,比如把 Token 的校验结果缓存几秒到几分钟,避免每次都重复查询。另外鉴权函数的超时时间要设得短一些,比如 2 到 3 秒,一旦鉴权服务本身卡住,不能拖垮主请求。

4.5 FC 侧的兜底校验:建议至少留一手

网关层的鉴权做得再完善,FC 侧也不能完全不设防。原因很现实:网关配置可能被误改,或者某个内部接口被其他服务直接绕过网关调用,如果函数不校验,等于内部数据裸奔。

FC 侧兜底校验不需要重复实现完整的 JWT 验签,但可以做轻量校验。比如在函数入口检查事件头里是否包含x-ca-openid或由网关注入的请求头,如果缺少这些头,说明请求可能不是从网关转发的,直接拒绝。对于 JWT 方案,网关可以把解析后的用户 ID 放在自定义 Header 中传给 FC,函数检查这个 Header 是否存在且格式正确,就能提高安全性。

我的建议是:网关负责完整鉴权,FC 负责最小可信校验。函数里加一段十行以内的检查代码,给数据访问多一层保障,性价比很高。

5. 函数代码:从 HTTP 请求到 SQL 查询

5.1 事件结构和入参解析

配置完成后,重点来看函数内部怎么写。FC 通过 API 网关触发时,函数收到的 event 是网关格式化后的标准结构,里面包含请求方法、路径、查询参数、请求头、Body 等信息。

我以一个用户信息查询接口为例。API 定义是GET /user/info?userId=123,那么 FC 函数里解析 userId 的方式大致是:

import json def handler(event, context): # 不同运行时解析 event 的 API 略有差异 # 以 Python 运行时为例,event 里通常包含 queryParameters 等字段 req = event.get("requestContext", {}) query = event.get("queryParameters", {}) or {} headers = event.get("headers", {}) or {} user_id = query.get("userId") if not user_id: return { "statusCode": 400, "headers": {"Content-Type": "application/json"}, "body": json.dumps({"code": 400, "message": "userId is required"}) } # 后续业务处理

不同云厂商的函数运行时,event 字段名会有差异。联调前用控制台调试工具发一次真实请求,打印整个 event 看看结构,比对着文档猜字段要快得多。

5.2 数据库连接池代码示例

数据库连接是函数里最容易写出问题的部分。如果每个请求都新建数据库连接,冷启动场景下函数实例第一次调用会非常慢,而且高并发时数据库连接数会被打满。

正确做法是使用连接池,并让连接池在函数实例的全局作用域中复用。Python 里可以借助dbutils的PooledDB:

import os import json import pymysql from dbutils.pooled_db import PooledDB _pool = None def get_pool(): global _pool if _pool is None: _pool = PooledDB( creator=pymysql, maxconnections=10, mincached=2, host=os.environ.get("DB_HOST"), port=int(os.environ.get("DB_PORT", "3306")), user=os.environ.get("DB_USER"), password=os.environ.get("DB_PASSWORD"), database=os.environ.get("DB_NAME"), charset="utf8mb4", cursorclass=pymysql.cursors.DictCursor, blocking=True ) return _pool def handler(event, context): query = event.get("queryParameters", {}) or {} user_id = query.get("userId") if not user_id: return { "statusCode": 400, "headers": {"Content-Type": "application/json"}, "body": json.dumps({"code": 400, "message": "userId is required"}) } pool = get_pool() conn = pool.connection() try: with conn.cursor() as cursor: cursor.execute("SELECT user_id, nickname, email FROM user_info WHERE user_id = %s", (user_id,)) row = cursor.fetchone() conn.commit() finally: conn.close() if row is None: return { "statusCode": 404, "headers": {"Content-Type": "application/json"}, "body": json.dumps({"code": 404, "message": "user not found"}) } return { "statusCode": 200, "headers": {"Content-Type": "application/json"}, "body": json.dumps({"code": 0, "data": row}, ensure_ascii=False) }

注意部署时要把pymysql、dbutils等依赖一起打包上传,或者使用函数计算的层来管理依赖。

blocking=True的含义是当连接池满时,新请求会等待而不是直接报错,能保护数据库不被瞬间击穿。这个参数在联调阶段很重要,否则并发稍高就会出现大量连接获取异常。

5.3 响应格式与 API 网关的约定

函数返回给 API 网关的响应必须是特定结构的 JSON,核心字段是statusCode、headers和body。body必须是字符串,不能是 Python 字典,否则网关无法正确解析。

最稳妥的做法:

return { "statusCode": 200, "headers": {"Content-Type": "application/json; charset=utf-8"}, "body": json.dumps({"code": 0, "data": row}, ensure_ascii=False) }

如果body里返回的是 JSON 字符串但中文出现乱码,记得加上ensure_ascii=False。返回二进制内容时,有些引导式网关还要求额外指定是否进行 Base64 编码,具体看运行时和产品文档。普通 JSON 接口用上面的格式就够了。

5.4 敏感信息放进环境变量

数据库连接信息这类敏感配置,千万不要硬编码在函数代码里。FC 服务支持环境变量配置,把数据库主机、端口、用户名、密码都放到环境变量里,代码里通过os.environ.get()读取。

这样做还有个额外好处:不同环境(测试、预发、生产)可以复用同一套函数代码,只通过环境变量切换连接配置。代码仓库里不会出现数据库密码,安全审计也更好通过。

6. 踩坑实录:联调测试与高频问题速查

6.1 一次标准的端到端联调流程

配置完成后,建议按下面的顺序做一次完整的联调验证,每步通过后再进入下一步,排查问题时不用四处猜。

第一步,用 API 网关控制台的调试工具,直接发一个GET /user/info?userId=1的请求。如果返回预期的 JSON,说明从网关到 FC 再到 RDS 的链路已经打通。这一步是验证网络链路的关键,先把网络问题排除。

第二步,修改请求参数,比如 userId 传空或者传不存在值,验证参数校验和错误分支是否正常工作。

第三步,在 FC 控制台查看函数日志,确认函数确实被调用,且日志中打印的参数与网关转发的一致。

第四步,开启鉴权后再发请求,不带认证信息的请求应该返回 401,带认证信息的请求应该正常返回。这就是验证鉴权配置是否正确。

6.2 高频问题速查表

现象可能原因处理方式
客户端请求返回 503 或 504函数执行超时或函数实例拉不起来检查函数超时时间设置,看函数日志有无报错
函数能连数据库但查询超时RDS 白名单未加安全组或交换机网段把 FC 所在安全组加入 RDS 白名单
函数报错connect timeoutVPC 配置不正确或安全组出方向未放行 3306确认 VPC、交换机、安全组选择与 RDS 一致
网关返回 401鉴权配置未开启或签名校验失败检查 API 鉴权设置,验证 JWT 或 APP 签名的时效和签名头
网关返回 403未发布 API 到对应环境,或 API 未授权给应用发布 API,检查 API 授权关系
函数返回但客户端报 500函数响应格式不符合网关约定检查返回值是否包含statusCode、headers、body,body是否为字符串
调用方报签名错误客户端系统时间不准检查签名算法,确认客户端时间与服务器时间偏差在许可范围内

6.3 排错思路:先看网关、再看函数、最后看数据库

联调排查一定要分层定位,切忌从头到尾一把抓。

请求失败时,先看 API 网关的调用日志。网关日志会清晰告诉你:请求有没有到网关、网关有没有转发、转发后返回状态码是多少。如果网关日志显示请求根本没进来,问题在前置,比如域名解析、API 发布状态、访问控制;如果网关日志显示转发后报错,问题在函数。

然后再看 FC 日志。函数每次调用的开始和结束都有记录,返回值是什么、有没有抛异常,都在日志里。如果函数日志显示查询时间特别长,问题大概率在数据库侧。

最后才排查 RDS。确认数据库连接数、慢查询日志、白名单和安全组。按照这个顺序定位,大部分问题都能在几分钟内圈定范围。

再说一个我自己的习惯:联调阶段在 FC 函数入口处加一行日志,打印整个 event 结构和关键参数。这个日志看起来简单,但在排查"参数为什么对不上""Header 为什么没有"这类问题时特别有效。上线前把详细日志降级或删掉就行,避免敏感信息落日志。

如果你现在正在搭这条链路,我个人的建议是:第一次跑通时先不用一上来就三套鉴权一起上,先把链路打通,用 APP 签名鉴权或者最基础的 JWT 方案让请求能通过验证,确认数据能正常返回,再逐步加固。最后再分享一个小技巧:在 API 网关调试阶段,可以一边开着网关的调试工具,一边在 FC 控制台实时看日志。网关发请求,函数日志同步打印,两边对比着看,很多让人头疼的连通性问题都能顺藤摸瓜快速解决。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询