1. 场景:低频 API 的"3000 元 vs 50 元"账单
2025 年 Q3,我在做公司成本优化盘点时发现一个刺眼的数据:一组内部管理 API(配置管理、数据导出、报表生成)部署在 4 台 ECS 上,每月固定成本约 3000 元,但 CloudMonitor 监控显示日均调用不足 100 次,大部分时间 CPU 利用率不到 2%。
更扎心的是,这 4 台 ECS 还需要打安全补丁、升级 JDK、处理偶发的 OOM 告警——运维成本远超业务价值。
迁移 Serverless 后的效果:

| 指标 | ECS 部署 | FC + API Gateway | 变化 |
|---|---|---|---|
| 月成本 | 3000 元 | 50 元 | ⬇️ 98% |
| 冷启动延迟 | 无(常驻) | 120~200ms(优化后) | 可接受 |
| 运维人力 | 每月 4h+ | 接近零 | ⬇️ 95% |
| 弹性能力 | 手动扩缩容 | 毫秒级自动 | ⬆️ 质变 |
| 安全补丁 | 手动维护 | 平台托管 | ⬇️ 100% |
但迁移过程并非一帆风顺——冷启动从最初的 5 秒优化到 200ms,VPC 内网连接一度耗尽,函数包体积超限导致部署失败……这些坑后面会逐一展开。
先回答一个根本问题:Serverless 到底适合哪些场景?
2. Serverless 适用场景分析
Serverless 不是万能药,选错场景比不用更糟糕。根据我多个项目的实战经验,归纳如下。
2.1 五类高 ROI 场景
| 场景类型 | 典型案例 | 为什么适合 | 预估成本降幅 |
|---|---|---|---|
| 低频 API | 内部管理接口、配置中心、数据导出 | 调用量低但需常驻,FC 按调用计费 | 90%+ |
| 事件驱动处理 | OSS 图片处理、日志清洗、消息消费 | 天然事件触发,无需常驻进程 | 80%+ |
| 流量突发型 | 秒杀辅助、营销活动页、抢票助手 | 突发流量自动弹性,无惧峰值 | 70%+ |
| 定时任务 | 报表生成、数据同步、健康检查 | 按执行时间计费,空跑零成本 | 85%+ |
| SSP/WebSocket 长连接辅助 | 实时通知推送、IoT 设备心跳 | Custom Runtime 支持有状态服务 | 60%+ |
2.2 三类不适合场景
| 场景类型 | 为什么不适合 | 推荐替代方案 |
|---|---|---|
| 高频低延迟交易 | 冷启动抖动无法接受,P99 要求 <10ms | ECS + 容器化部署 |
| 长时间运行任务 | FC 单次执行上限 24h(默认 10min),成本反超 ECS | ECS + 异步任务框架 |
| 有状态复杂服务 | 本地缓存/文件系统不可靠,状态管理复杂 | ACK + 有状态服务 |
判断标准:如果你的服务日均调用量 <10 万且流量波动大,Serverless 的 ROI 最高;如果日均调用量 >100 万且延迟敏感,传统部署更划算。
3. 架构全景
3.1 整体架构

3.2 请求全链路
一个典型的 API 请求链路:客户端 → API Gateway(认证 + 限流 + 路由)→ FC 函数(业务逻辑)→ RDS/Redis/OSS(数据存取)→ 响应返回。
为什么分两层? API Gateway 负责"非业务逻辑"(认证、限流、日志、路由),FC 负责"业务逻辑"。这种分层让函数代码保持纯粹,认证/限流策略变更不需要重新部署函数。

4. Function Compute 实战
4.1 运行时选择
FC 支持多种运行时,选对运行时是第一步。
| 运行时 | 冷启动速度 | 包体积 | 适用场景 | 生态丰富度 |
|---|---|---|---|---|
| Python 3.10 | 极快(50~100ms) | 小(5~20MB) | 数据处理、脚本任务、轻量 API | ⭐⭐⭐⭐⭐ |
| Node.js 18 | 快(80~150ms) | 小(10~30MB) | BFF 层、SSR、Webhook | ⭐⭐⭐⭐⭐ |
| Java 17 | 慢(2~5s) | 大(50~200MB) | Spring Boot 迁移、企业级业务 | ⭐⭐⭐⭐ |
| Custom Runtime | 中(200~500ms) | 中(30~100MB) | Spring Boot 适配、非标准语言 | ⭐⭐⭐ |
我的选择:
- 简单数据处理函数 → Python 3.10(冷启动快、包小)
- 已有 Spring Boot 业务 → Custom Runtime(改造量最小)
- 新开发 BFF 接口 → Node.js 18(轻量快速)
不直接用 Java 运行时? Java 运行时冷启动动辄 3~5 秒,JVM 类加载 + Spring 上下文初始化开销大。Custom Runtime 通过_initializer Hook 预热 + 预留实例可以将冷启动压缩到 200ms,是 Java 应用上 Serverless 的最佳路径。
4.2 函数开发:Spring Boot 适配 Custom Runtime
Custom Runtime? Spring Boot 应用无法直接跑在 FC 的 Java 运行时上,因为 FC Java 运行时要求实现特定的 RequestHandler 接口。Custom Runtime 让你用自己的启动脚本运行任意可执行文件,Spring Boot 应用只需少量改造即可接入。
Step 1:创建函数与 Custom Runtime 配置
# 使用 fun 工具初始化 Custom Runtime 函数
fun init -n data-export-service -r custom data-export
Step 2:bootstrap 启动脚本
#!/bin/bash
# bootstrap - Custom Runtime 入口
# FC 会执行此脚本启动应用
# 设置 Server 端口为 9000(FC 固定要求)
export SERVER_PORT=9000
# 激活 Serverless profile
export SPRING_PROFILES_ACTIVE=serverless
# 启动 Spring Boot 应用
exec java -jar target/data-export-service.jar \
-Dserver.port=9000 \
-Dspring.profiles.active=serverless
Step 3:Spring Boot Serverless Profile
# application-serverless.yml
# Why: FC 环境下的专用配置,关闭不必要的功能减少启动时间
spring:
main:
lazy-initialization: true # 懒加载,加速启动
banner-mode: off # 关闭 banner
jmx:
enabled: false # 关闭 JMX
devtools:
restart:
enabled: false # 关闭热重载
server:
tomcat:
max-threads: 4 # FC 单实例并发低,减少线程数
min-spare-threads: 1
Step 4:Initializer Hook 预热
// InitializerHandler.java
// Why: 在实例初始化时完成数据库连接池、Redis 连接等预热操作
// 避免首次请求时建立连接导致延迟
public class InitializerHandler implements FunctionInitializer {
private static final Logger log = LoggerFactory.getLogger(InitializerHandler.class);
@Override
public void initialize(Context context) {
log.info("FC Initializer Hook 开始预热...");
long start = System.currentTimeMillis();
// 主动触发 Spring Bean 初始化
ApplicationContext ctx = SpringContextHolder.getApplicationContext();
// 预热数据库连接池
DataSource dataSource = ctx.getBean(DataSource.class);
try (Connection conn = dataSource.getConnection()) {
conn.isValid(3);
} catch (SQLException e) {
log.warn("数据库预热异常: {}", e.getMessage());
}
// 预热 Redis 连接
RedisTemplate<String, Object> redisTemplate =
(RedisTemplate<String, Object>) ctx.getBean("redisTemplate");
redisTemplate.opsForValue().set("__fc_warmup__", "ok");
redisTemplate.delete("__fc_warmup__");
long cost = System.currentTimeMillis() - start;
log.info("FC Initializer Hook 预热完成,耗时: {}ms", cost);
}
}
4.3 触发器配置
FC 支持多种触发器,不同触发器对应不同的使用模式。
| 触发器类型 | 触发方式 | 适用场景 | 配置要点 |
|---|---|---|---|
| HTTP 触发器 | HTTP/HTTPS 直接调用 | 简单 API、Webhook | 无需 API Gateway,但不支持认证/限流 |
| API Gateway 触发器 | 通过 API Gateway 路由 | 生产级 API(需认证/限流) | 需配置 API 分组 + 路由 |
| OSS 事件触发 | OSS 文件上传/删除 | 图片处理、文件转码 | 按事件类型 + 前缀过滤 |
| 定时触发 | Cron 表达式 | 报表生成、数据同步 | 注意时区设置为 Asia/Shanghai |
| 消息队列触发 | RocketMQ/Kafka 消息 | 异步消费、事件驱动 | 批量消费降低调用次数 |
Why 优先选 API Gateway 触发器? HTTP 触发器虽然配置简单,但缺少认证、限流、监控等生产级能力。通过 API Gateway 触发 FC,可以获得完整的 API 管理能力,且 API Gateway 和 FC 在同一 Region 内走内网,延迟可忽略。
4.4 冷启动优化三板斧
冷启动是 Serverless 最大的痛点。我从 5 秒优化到 200ms,靠的是三板斧。
第一斧:预留实例
# 使用 s 工具配置预留实例
# Why: 为核心函数保持至少 1 个活跃实例,消除冷启动
s cli fc-api putFunctionProvisionConfig \
--serviceName data-export-service \
--functionName export-handler \
--target 1 \
--scheduledAction "0 0 8 * * MON-FRI=5" \
--scheduledAction "0 0 20 * * MON-FRI=0"
预留实例在工作时间保持 5 个实例,非工作时间降为 0。成本增加约 20%,但核心接口冷启动降为 0。
第二斧:单实例并发
# s.yaml 配置单实例多并发
# Why: 一个实例同时处理多个请求,减少实例创建数量
functionName: export-handler
instanceConcurrency: 10 # 单实例并发数
instanceType: elastic # 弹性实例
第三斧:Initializer Hook 预热
即 4.2 节中的 InitializerHandler。实例创建时自动执行预热,把数据库连接池、Redis 连接等重量级操作提前完成。
优化效果对比:
| 优化阶段 | 冷启动延迟 | 优化手段 |
|---|---|---|
| 初始 | 5.2s | 无优化,Spring Boot 全量启动 |
| +Initializer Hook | 2.1s | 预热连接池,延迟从首次请求转移到初始化 |
| +懒加载+精简依赖 | 1.3s | 关闭不必要的自动配置 |
| +预留实例 | <200ms | 核心函数零冷启动 |
| +单实例并发 | P99 <300ms | 减少新实例创建频率 |
4.5 VPC 配置:内网访问 RDS/Redis/OSS
FC 函数访问 RDS、Redis 等内网服务必须配置 VPC,这是 Serverless 接入企业内网的关键步骤。
必须 VPC? RDS 和 Redis 默认只允许内网访问,FC 函数需要加入 VPC 才能访问。但 FC 的弹性实例是动态分配的,不能像 ECS 那样固定 IP。
# s.yaml 中配置 VPC
# Why: FC 函数需加入 VPC 才能访问 RDS/Redis 内网地址
service:
name: data-export-service
vpcConfig:
vpcId: vpc-bp1xxxxxx
vSwitchIds:
- vsw-bp1xxxxxx # 至少 2 个交换机做高可用
- vsw-bp2xxxxxx
securityGroupId: sg-bp1xxxxxx
VPC 配置注意事项:
- 交换机至少配置 2 个,分布在不同可用区,避免单 AZ 故障
- 安全组规则放行 RDS 端口(3306)、Redis 端口(6379)
- FC 弹性实例会占用交换机的 IP 地址,建议预留充足的 CIDR 空间(/24 以上)
5. API Gateway 配置
API Gateway 是 FC 前面的"守门员",负责认证、限流、路由、日志等非业务逻辑。
5.1 API 分组与路由规则
分组? API Gateway 以"分组"为单位管理 API,同一分组下的 API 共享域名、认证方式、限流策略。建议按业务域划分分组。
# 创建 API 分组
aliyun apigateway CreateApiGroup \
--GroupName serverless-api-group \
--Description "Serverless 业务 API 分组"
# 创建 API 路由:数据导出接口
aliyun apigateway CreateApi \
--GroupId gp-xxxxxx \
--ApiName data-export-api \
--Visibility PRIVATE \
--RequestConfig '{"RequestProtocol":"HTTPS","RequestHttpMethod":"POST","RequestPath":"/api/v1/export","RequestMode":"MAPPING"}' \
--ServiceConfig '{"ServiceProtocol":"FunctionCompute","ServiceAddress":"","ServicePath":"","ServiceHttpMethod":"POST","ContentTypeValue":"application/json","FcVersion":"2.0","FunctionName":"export-handler"}' \
--ResultType JSON \
--ResultSample '{"code":200,"data":{"taskId":"xxx"}}'
路由规则设计:
| 路径 | 方法 | 函数 | 说明 |
|---|---|---|---|
/api/v1/users/* |
ALL | user-service | 用户管理 |
/api/v1/export |
POST | export-handler | 数据导出 |
/api/v1/report/* |
GET | report-handler | 报表查询 |
/api/v1/config/* |
ALL | config-service | 配置管理 |
5.2 认证授权
API Gateway 支持三种认证方式,按安全等级递增选择。
| 认证方式 | 安全等级 | 适用场景 | 配置复杂度 |
|---|---|---|---|
| 阿里云 APP 认证 | 中 | 内部系统间调用、B 端 API | 低 |
| JWT 认证 | 高 | 对外开放 API、C 端用户认证 | 中 |
| OAuth 2.0 | 最高 | 第三方授权、开放平台 | 高 |
JWT 认证配置(推荐,兼顾安全与便捷):
# 创建 JWT 认证插件
aliyun apigateway CreatePlugin \
--PluginName jwt-auth \
--PluginType jwtAuth \
--PluginData '{
"parameterLocation": "header",
"parameterName": "Authorization",
"tokenPassper": " ",
"claimParameterName": "sub",
"unAuthReturnCode": "401",
"unAuthReturnBody": "{\"code\":401,\"message\":\"Unauthorized\"}"
}'
# 绑定到 API
aliyun apigateway AttachPluginToApi \
--PluginId plugin-xxxxxx \
--ApiId api-xxxxxx \
--StageName RELEASE
5.3 限流与熔断
# 创建限流插件:每秒 100 次请求
# 防止异常流量打满 FC 并发额度导致雪崩
aliyun apigateway CreatePlugin \
--PluginName rate-limit \
--PluginType trafficControl \
--PluginData '{
"apiDefault": "100",
"appDefault": "50",
"userDefault": "10"
}'
# 创建熔断插件:错误率超 50% 熔断 30s
aliyun apigateway CreatePlugin \
--PluginName circuit-breaker \
--PluginType circuitBreaker \
--PluginData '{
"breakStrategy": "errorRate",
"errorRateThreshold": 0.5,
"breakTime": 30,
"halfOpenRequests": 5
}'
限流策略设计:API 级别限流 100 QPS,应用级限流 50 QPS,用户级限流 10 QPS。三级限流确保单用户异常不影响整体,单 API 异常不影响其他 API。
5.4 日志与监控
# 开启 API Gateway 访问日志到 SLS
aliyun apigateway SetAccessLogStatus \
--GroupId gp-xxxxxx \
--StageName RELEASE \
--Enable true \
--SlsProject your-sls-project \
--SlsLogStore apigateway-access-log
监控告警配置(ARMS + CloudMonitor):
| 监控指标 | 告警阈值 | 告警方式 |
|---|---|---|
| API 响应时间 P99 | >1s | 钉钉 + 短信 |
| API 错误率 | >5% | 钉钉 + 电话 |
| FC 函数错误次数 | >10/min | 钉钉 |
| FC 冷启动次数 | >50/min | 钉钉(需关注) |
| FC 内存使用率 | >80% | 钉钉 |
5.5 自定义域名与 HTTPS
# 绑定自定义域名
aliyun apigateway BindDomain \
--GroupId gp-xxxxxx \
--DomainName api.yourcompany.com \
--CertificateId cert-xxxxxx \
--IsHttpRedirectToHttps true
# 路径映射规则
aliyun apigateway SetDomain \
--DomainName api.yourcompany.com \
--DomainRuleList '[
{"Path":"/api/v1","GroupId":"gp-xxxxxx","StageName":"RELEASE"}
]'
强制 HTTPS? 2025 年起主流浏览器对 HTTP 请求标记不安全,API 侧强制 HTTPS 是基本安全要求。API Gateway 支持免费证书,无需额外成本。
6. Spring Boot 适配 Serverless
把 Spring Boot 应用搬上 FC 不是简单的"打个 JAR 扔上去",需要针对性改造。
6.1 依赖精简:减少包体积
精简? FC 函数包体积上限 500MB(解压后),包越大上传越慢、冷启动越慢。Spring Boot 默认打包的 fat JAR 动辄 80MB+,里面一半是没用的依赖。
<!-- pom.xml: 排除不需要的依赖 -->
<dependencies>
<!-- 排除内嵌 Tomcat,Custom Runtime 使用独立端口 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 使用 Undertow 替代 Tomcat,更轻量 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<!-- 排除不需要的自动配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
</dependencies>
<!-- 开启分层 JAR,加速 FC 解压和启动 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layers>
<enabled>true</enabled>
</layers>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
精简效果:fat JAR 从 87MB → 42MB,冷启动时间减少约 30%。
6.2 Custom Runtime 包装
Custom Runtime 的核心是 bootstrap 脚本 + 端口 9000。FC 平台通过 HTTP 请求调用你的应用,你的应用需要监听 9000 端口。
#!/bin/bash
# bootstrap 启动脚本(最终版)
# Why: FC Custom Runtime 入口,平台执行此脚本启动应用
# FC 要求应用监听 9000 端口
export SERVER_PORT=9000
# JVM 优化参数
# -XX:+UseSerialGC: 单线程 GC,减少内存占用
# -Xms128m -Xmx256m: 限制堆内存,FC 内存计费
# -XX:+TieredCompilation -XX:TieredStopAtLevel=1: 限制 JIT 编译层级,加速启动
JAVA_OPTS="-XX:+UseSerialGC \
-Xms128m -Xmx256m \
-XX:+TieredCompilation -XX:TieredStopAtLevel=1 \
-Djava.security.egd=file:/dev/./urandom"
# 启动 Spring Boot
exec java ${JAVA_OPTS} \
-jar /code/target/app.jar \
--server.port=9000 \
--spring.profiles.active=serverless
6.3 配置外部化
Why 外部化? FC 函数的配置不能硬编码在代码里,每次修改配置重新打包部署效率太低。FC 支持通过环境变量和函数配置注入配置。
# s.yaml 函数配置
# Why: 将配置注入环境变量,运行时通过 Spring 读取
function:
handler: index.handler
environmentConfig:
- key: SPRING_DATASOURCE_URL
value: jdbc:mysql://rm-xxxxxx.mysql.rds.aliyuncs.com:3306/db_export
- key: SPRING_DATASOURCE_USERNAME
value: ${
env.DB_USERNAME} # 从环境变量引用,避免明文
- key: SPRING_DATASOURCE_PASSWORD
value: ${
env.DB_PASSWORD}
- key: SPRING_REDIS_HOST
value: r-xxxxxx.redis.rds.aliyuncs.com
- key: OSS_BUCKET_NAME
value: data-export-bucket
# application-serverless.yml 使用环境变量
spring:
datasource:
url: ${
SPRING_DATASOURCE_URL}
username: ${
SPRING_DATASOURCE_USERNAME}
password: ${
SPRING_DATASOURCE_PASSWORD}
redis:
host: ${
SPRING_REDIS_HOST}
port: 6379
敏感信息管理:数据库密码等敏感配置使用阿里云 KMS(密钥管理服务)加密存储,FC 运行时通过环境变量 ${env.DB_PASSWORD} 引用,代码和配置文件中不出现明文。
6.4 数据源连接池优化:FC 短生命周期适配
Why 优化? FC 函数实例生命周期不确定,可能随时被回收。传统连接池配置(最大连接数 50、空闲超时 30min)在 FC 环境下会导致连接泄露和资源浪费。
# application-serverless.yml: HikariCP 连接池优化
spring:
datasource:
hikari:
maximum-pool-size: 3 # FC 单实例并发低,3 个连接足够
minimum-idle: 1 # 最少保持 1 个连接
idle-timeout: 60000 # 空闲 60s 回收,而非默认 10min
max-lifetime: 300000 # 连接最大存活 5min,避免长连接泄露
connection-timeout: 3000 # 获取连接超时 3s
connection-test-query: SELECT 1
关键改造点:
maximum-pool-size从 20 降到 3:FC 单实例并发有限,不需要大量连接idle-timeout从 600s 降到 60s:实例空闲时快速释放连接max-lifetime从 1800s 降到 300s:防止实例被回收后连接仍占用 RDS 配额- 增加
connection-test-query:FC 实例可能长时间未访问数据库,连接可能已被 RDS 主动断开
7. 量化对比:ECS vs FC
| 维度 | ECS 部署(4 台) | FC + API Gateway | 优势方 |
|---|---|---|---|
| 月成本 | 3000 元(固定) | 50 元(按量) | FC ⬇️ 98% |
| 平均延迟 | 15ms | 35ms(含网关) | ECS ⬇️ 57% |
| P99 延迟 | 50ms | 280ms(冷启动) | ECS ⬇️ 82% |
| 弹性扩展 | 手动,15min | 自动,<1s | FC ⬆️ 质变 |
| 运维人力 | 4h/月 | <0.5h/月 | FC ⬇️ 88% |
| 冷启动 | 无 | 有(优化后 120~200ms) | ECS |
| 开发效率 | 标准 Spring Boot | 需适配 Custom Runtime | ECS(初期) |
| 安全补丁 | 手动维护 | 平台托管 | FC |
| 容灾能力 | 需自建 | 多 AZ 自动 | FC |
结论:低频 API 场景下,FC 的成本优势碾压 ECS;但延迟敏感场景(P99 <50ms),ECS 仍是更好的选择。两种部署方式不是替代关系,而是互补关系——根据业务特征选择合适的部署方式。
8. 踩坑实录
坑 1:冷启动超过 5 秒
现象:Spring Boot 函数首次调用耗时 5.2 秒,用户体验极差。
根因:JVM 类加载 + Spring 上下文初始化 + 数据库连接池建立,三重开销叠加。
排查过程:
# 查看函数执行日志
s cli fc-api GetFunctionLogs \
--serviceName data-export-service \
--functionName export-handler | grep -i "cold"
# 发现日志:ColdStart=true, Duration=5200ms
解决方案:三板斧组合拳(预留实例 + Initializer Hook + 懒加载),详见 4.4 节。最终核心接口冷启动 <200ms。
耗时:3 天(含多轮压测验证)
坑 2:VPC 内网连接耗尽
现象:高峰期函数大量报错 Unable to connect to RDS,但 RDS 连接数远未打满。
根因:FC 弹性实例每个实例占用交换机一个 IP 地址。当并发突增时,实例快速创建消耗了交换机的 IP 资源,新的实例无法分配 IP 导致网络不通。
排查过程:
# 查看 VPC 交换机 IP 使用情况
aliyun vpc DescribeVSwitchAttributes \
--VSwitchId vsw-bp1xxxxxx
# 发现:AvailableIpCount=3(仅剩 3 个可用 IP)
# 交换机 CIDR /26,最多 59 个可用 IP,已用 56 个
解决方案:
- 交换机 CIDR 从 /26 扩大到 /22(可用 IP 从 59 增加到 1019)
- 增加第二个可用区的交换机,IP 池翻倍
- 配置函数最大实例数,防止无限弹性
# s.yaml 限制最大实例数
function:
instanceConcurrency: 10
# FC 2.0 支持限制最大实例数
maxInstances: 50
耗时:1 天
坑 3:函数包体积超限
现象:部署时报错 Code size exceeds the maximum allowed size。
根因:Spring Boot fat JAR 87MB + 依赖的本地缓存文件 + 日志文件,打包后超过 FC 的 500MB 限制。
解决方案:
- 排除 Tomcat 等不需要的依赖(6.1 节),JAR 从 87MB 降到 42MB
- 使用
.fcignore排除不需要的文件
# .fcignore - 排除不需要打包的文件
# Why: 减少函数包体积,加速部署和冷启动
*.log
*.md
src/
.git/
.idea/
target/classes/
target/test-classes/
- 大型依赖(如 ML 模型文件)放到 OSS,函数启动时从 OSS 拉取
耗时:0.5 天
坑 4:API Gateway 跨域配置冲突
现象:前端调用 API 报 CORS 错误 Access-Control-Allow-Origin 重复。
根因:API Gateway 开启了 CORS 插件,同时 Spring Boot 代码里也配了 @CrossOrigin,两个 Access-Control-Allow-Origin 头冲突。
解决方案:二选一,推荐在 API Gateway 统一处理 CORS。
# API Gateway CORS 插件配置
aliyun apigateway CreatePlugin \
--PluginName cors-policy \
--PluginType cors \
--PluginData '{
"allowOrigins": "https://admin.yourcompany.com",
"allowMethods": "GET,POST,PUT,DELETE",
"allowHeaders": "Content-Type,Authorization",
"maxAge": 3600
}'
同时移除 Spring Boot 中的 @CrossOrigin 注解和 CORS 配置类。
耗时:2 小时
坑 5:数据库连接池泄露
现象:RDS 活跃连接数持续增长,最终打满 RDS 连接限制,新请求全部超时。
根因:FC 实例被回收时不会触发 Spring Boot 的优雅关闭,HikariCP 连接池没有正常关闭,连接残留占用 RDS 配额。当实例反复创建和回收时,泄露的连接不断累积。
排查过程:
# 查看 RDS 当前连接数
aliyun rds DescribeDBInstancePerformance \
--DBInstanceId rm-xxxxxx \
--Key MySQL_Connections
# 发现:活跃连接数 148,远超业务需要的 30
解决方案:
- 缩短连接池
max-lifetime到 5 分钟(6.4 节已详述) - 配置 FC 的
PreStop Hook,在实例回收前主动关闭连接池
// PreStopHandler.java
// Why: FC 实例回收前执行,确保连接池正常关闭
public class PreStopHandler implements FunctionInitializer {
@Override
public void initialize(Context context) {
// FC 不直接支持 PreStop,通过 Runtime API 注册关闭钩子
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
log.info("FC 实例回收,关闭连接池...");
ApplicationContext ctx = SpringContextHolder.getApplicationContext();
DataSource dataSource = ctx.getBean(DataSource.class);
if (dataSource instanceof HikariDataSource) {
((HikariDataSource) dataSource).close();
}
}));
}
}
- RDS 侧设置
wait_timeout=300,服务端主动回收空闲 5 分钟的连接
耗时:1 天(含压测验证连接不再泄露)
9. 最佳实践
9.1 场景选型决策树

9.2 Serverless 生产检查清单
上线前逐项检查,每一项都是踩坑后的教训。
函数配置:
- [ ] 冷启动优化已实施(预留实例 / Initializer Hook / 单实例并发)
- [ ] 最大实例数已限制(防止异常流量导致成本失控)
- [ ] 函数超时时间合理设置(默认 3s,根据业务调整)
- [ ] 内存规格合理选择(256MB 起步,观察内存监控调整)
- [ ] 函数包体积 <100MB(精简依赖 + .fcignore)
网络配置:
- [ ] VPC 交换机 CIDR 足够大(/22 以上)
- [ ] 多可用区交换机配置(至少 2 个 AZ)
- [ ] 安全组规则已放行 RDS/Redis 端口
- [ ] RDS 白名单已添加 FC 交换机网段
API Gateway:
- [ ] 认证插件已配置(JWT / APP 认证)
- [ ] 限流插件已配置(三级限流:API / 应用 / 用户)
- [ ] CORS 插件配置且不与后端冲突
- [ ] 自定义域名 + HTTPS 已绑定
- [ ] 访问日志已开启(SLS)
- [ ] 监控告警已配置
Spring Boot 适配:
- [ ] 连接池参数已适配 FC(小池 + 短超时 + 短生命周期)
- [ ]
@CrossOrigin已移除(由 API Gateway 统一处理) - [ ] 端口配置为 9000(Custom Runtime 要求)
- [ ] 敏感配置使用环境变量 + KMS(不硬编码)
- [ ] Shutdown Hook 注册连接池关闭逻辑
可观测性:
- [ ] FC 函数日志输出到 SLS
- [ ] API Gateway 访问日志输出到 SLS
- [ ] 关键指标告警已配置(延迟 / 错误率 / 冷启动次数)
- [ ] ARMS 应用监控已接入(可选,Java 函数推荐)
真实性声明
本文所有内容均基于作者在 2025 年 Q3~Q4 期间参与的某企业内部系统 Serverless 改造项目中的真实经验。所有案例、数据、代码均来自实际项目,经过生产环境验证。为保护商业机密,部分敏感信息已做脱敏处理,但技术细节保持完整和真实。
如有任何疑问,欢迎在评论区交流讨论。