Function Compute + API Gateway 实战:Serverless 从概念验证到生产级应用

简介: 我负责的一组低频 API 用 4 台 ECS 部署,每月成本 3000 元,但日均调用不足 100 次。迁移到阿里云函数计算 FC + API 网关后,月成本降到 50 元,冷启动优化到 200ms 以内。本文从真实项目出发,系统讲解 Serverless 适用场景分析、FC + API Gateway 全链路架构设计、Spring Boot 适配 Serverless 的 4 个关键改造、冷启动优化三板斧、5 个生产级踩坑实录,以及场景选型决策树和生产检查清单,帮你把 Serverless 从 POC 推向生产。

1. 场景:低频 API 的"3000 元 vs 50 元"账单

2025 年 Q3,我在做公司成本优化盘点时发现一个刺眼的数据:一组内部管理 API(配置管理、数据导出、报表生成)部署在 4 台 ECS 上,每月固定成本约 3000 元,但 CloudMonitor 监控显示日均调用不足 100 次,大部分时间 CPU 利用率不到 2%。

更扎心的是,这 4 台 ECS 还需要打安全补丁、升级 JDK、处理偶发的 OOM 告警——运维成本远超业务价值。

迁移 Serverless 后的效果

008-fc-serverless-comparison.png

指标 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 整体架构

008-fc-apigateway-serverless-practice_diagram_1.png

3.2 请求全链路

一个典型的 API 请求链路:客户端 → API Gateway(认证 + 限流 + 路由)→ FC 函数(业务逻辑)→ RDS/Redis/OSS(数据存取)→ 响应返回。

为什么分两层? API Gateway 负责"非业务逻辑"(认证、限流、日志、路由),FC 负责"业务逻辑"。这种分层让函数代码保持纯粹,认证/限流策略变更不需要重新部署函数。

008-fc-apigateway-serverless-practice_diagram_2.png

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 个

解决方案

  1. 交换机 CIDR 从 /26 扩大到 /22(可用 IP 从 59 增加到 1019)
  2. 增加第二个可用区的交换机,IP 池翻倍
  3. 配置函数最大实例数,防止无限弹性
# 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 限制。

解决方案

  1. 排除 Tomcat 等不需要的依赖(6.1 节),JAR 从 87MB 降到 42MB
  2. 使用 .fcignore 排除不需要的文件
# .fcignore - 排除不需要打包的文件
# Why: 减少函数包体积,加速部署和冷启动
*.log
*.md
src/
.git/
.idea/
target/classes/
target/test-classes/
  1. 大型依赖(如 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

解决方案

  1. 缩短连接池 max-lifetime 到 5 分钟(6.4 节已详述)
  2. 配置 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();
            }
        }));
    }
}
  1. RDS 侧设置 wait_timeout=300,服务端主动回收空闲 5 分钟的连接

耗时:1 天(含压测验证连接不再泄露)

9. 最佳实践

9.1 场景选型决策树

008-fc-apigateway-serverless-practice_diagram_3.png

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 改造项目中的真实经验。所有案例、数据、代码均来自实际项目,经过生产环境验证。为保护商业机密,部分敏感信息已做脱敏处理,但技术细节保持完整和真实。

如有任何疑问,欢迎在评论区交流讨论。

相关文章
|
3天前
|
人工智能 安全 测试技术
|
5天前
|
云安全 人工智能 安全
阿里云 Agentic SOC 位居 IDC MarketScape安全运营智能体2026领导者类别
以 Agentic AI 重构安全运营闭环,阿里云云安全在产品能力与市场份额
1179 3
|
6天前
|
缓存 UED 开发者
Codex109天重置23次,明天还要再送一次
Codex近109天完成23次额度重置,7月14日将迎来第24次。Tibo高频响应用户反馈:优化GPT-5.6高消耗问题、补发失效福利、调整重置时间——形成“反馈→回应→修复→补偿”正向闭环,彰显以用户为中心的产品哲学。(239字)
663 11
|
5天前
|
数据采集 机器学习/深度学习 人工智能
田间杂草定位与检测4200张YOLO智慧农业数据集分享
本数据集含4200张真实农田图像,YOLO格式,单类别(杂草)高质量标注,覆盖多作物、多光照、多生长阶段等复杂场景,专为智慧农业杂草检测与智能除草设备研发设计,支持YOLOv5/v8/v10等主流模型训练。
363 94
|
9天前
|
存储 人工智能 JSON
Qwen 本地部署搭配 ComfyUI 生成 AI 漫剧完整实操指南(小白零基础可落地,零成本无限生成+角色一致性天花板)
2026全网最优本地漫剧流水线:零成本、离线运行、角色统一、低配(8G显卡)可跑。融合Qwen本地大模型+ComfyUI双引擎,实现剧本生成→分镜绘图→动态成片全自动,隐私安全、无审核限流,新手30分钟上手,日更无忧。(239字)
|
3天前
|
Web App开发 数据采集 人工智能
|
9天前
|
人工智能 缓存 JSON
刚刚 GPT-5.6 发布,吊打 Claude 5 和 Grok 4.5?一手实测来啦!
GPT-5.6 刚发布,跑分号称超越 Fable 5,真的假的?附一手实测,让 3 个最强 AI 编程模型 GPT-5.6 Sol、Claude Fable 5、Grok 4.5 在 Cursor 里同时一把梭开发网页游戏,看看最新模型到底谁更能打。
381 0