Serverless 架构模式深度解析

简介: Serverless并非“无服务器”,而是开发者无需管理服务器,专注业务逻辑。具备按需付费、弹性伸缩、事件驱动等优势,适用于突发流量、定时任务等场景,结合FaaS与BaaS可构建高效应用,是云原生发展的重要方向。

一、引言:Serverless不是“无服务器”,而是“无需关心服务器”

在云计算从IaaS、PaaS演进到SaaS的全链路中,Serverless(无服务器架构)是近十年最具颠覆性的架构模式之一。根据CNCF(云原生计算基金会)2025年度调研报告,全球Serverless市场规模已突破800亿美元,年复合增长率达35%,超过60%的中大型企业已将至少20%的业务迁移至Serverless架构。

很多开发者对Serverless的第一认知是“不用管服务器了”,这是典型的认知偏差——Serverless的核心不是“没有服务器”,而是开发者无需关注服务器的采购、部署、扩容、运维、安全补丁 等底层基础设施工作,只需聚焦业务逻辑本身。这种架构模式彻底重构了传统的开发、部署和运维流程,带来了“按需付费、弹性伸缩、快速迭代”的核心价值。

二、Serverless核心概念与底层逻辑

2.1 权威定义:Serverless的官方边界

根据CNCF对Serverless的权威定义,符合以下两个核心特征的架构可被称为Serverless:

  1. 无服务器管理:开发者无需感知底层服务器的存在,云厂商负责基础设施的全生命周期管理;
  2. 事件驱动+弹性伸缩:应用以事件为触发源运行,资源自动根据请求量扩缩容(甚至缩容至0);
  3. 按量计费:仅为实际运行的计算资源付费,空闲时不产生任何成本。

2.2 Serverless vs 传统架构 vs 微服务:核心差异

维度 传统单体架构 微服务架构 Serverless架构
服务器管理 自研/采购,全量运维 容器化部署,部分运维 云厂商托管,零运维
资源伸缩 手动扩容,资源固定 自动扩缩容,需配置阈值 毫秒级弹性,缩容至0
计费模式 按服务器资源付费 按容器/实例数付费 按执行时长+调用次数付费
开发聚焦点 全栈开发(含运维) 业务逻辑+服务治理 纯业务逻辑
冷启动问题 存在(可优化)
状态管理 本地/数据库 分布式缓存/数据库 无状态(需依赖BaaS服务)

2.3 Serverless底层运行原理(流程图)

image.png

原理拆解

  1. 事件触发:用户请求、定时任务、消息队列等事件通过API网关/事件源进入云厂商调度系统;
  2. 实例调度:调度系统检查是否有预热的函数实例,有则直接执行,无则初始化运行时(如JVM)、加载代码;
  3. 函数执行:执行完业务逻辑后返回结果,空闲时资源自动释放(或保留少量预热实例减少冷启动)。

2.4 Serverless核心特性

  1. 无状态性:函数实例不保存任何本地状态,每次执行都是独立的(需状态时依赖Redis/MongoDB等BaaS服务);
  2. 事件驱动:函数的执行必须由事件触发(HTTP请求、MQ消息、定时任务等),无事件则函数不运行;
  3. 按需付费:以阿里云FC为例,计费单位是“GB·秒”,即内存规格×执行时长,空闲时费用为0;
  4. 毫秒级弹性:支持每秒数万次的请求峰值,云厂商自动扩容,无需人工配置;
  5. 零运维:无需关注服务器的补丁、监控、故障转移,云厂商全托管。

三、Serverless关键架构模式(2025主流)

3.1 函数即服务(FaaS)模式(核心)

FaaS(Function as a Service)是Serverless的核心载体,开发者将业务逻辑封装为“函数”,部署到云厂商的FaaS平台(如AWS Lambda、阿里云FC、腾讯云SCF),平台负责函数的运行、扩容、运维。

核心特征:

  • 函数是最小部署单元,独立运行、独立扩缩容;
  • 函数生命周期短(毫秒到分钟级),避免长时间占用资源;
  • 支持多语言(Java/Go/Python/Node.js等),2025年主流FaaS平台已全面支持JDK 17。

3.2 后端即服务(BaaS)模式

BaaS(Backend as a Service)是FaaS的补充,指云厂商提供的开箱即用的后端服务,无需开发者自建和运维,常见的BaaS服务包括:

  • 数据库服务:MySQL/Redis/MongoDB托管版;
  • 消息队列:RocketMQ/Kafka托管版;
  • 存储服务:对象存储OSS;
  • 认证服务:OAuth2.0/SSO托管版。

FaaS+BaaS是Serverless的经典组合:FaaS负责业务逻辑,BaaS负责基础能力,开发者只需聚焦业务代码。

3.3 事件驱动架构(EDA)+ Serverless

事件驱动是Serverless的核心运行模式,事件生产者(如用户请求、订单创建、文件上传)产生事件,事件总线(如阿里云EventBridge)将事件路由到对应的FaaS函数,函数处理完后可产生新事件触发下一个函数,形成“事件流”。

image.png

3.4 微服务Serverless化模式

传统微服务需要部署到K8s/容器平台,仍需关注容器编排、资源调度;微服务Serverless化则将每个微服务拆分为多个FaaS函数,通过API网关聚合,核心优势:

  • 每个函数独立扩缩容,避免“一损俱损”;
  • 降低微服务的部署和运维成本;
  • 按需付费,减少闲置资源浪费。

3.5 批处理Serverless模式

针对大数据批处理场景(如日志分析、数据ETL),Serverless批处理模式无需搭建Hadoop/Spark集群,直接通过FaaS函数并行处理数据,优势:

  • 弹性扩容,处理速度随数据量自动提升;
  • 处理完成后资源自动释放,成本仅为传统集群的10%-30%;
  • 2025年主流FaaS平台已支持函数的“批量触发”和“结果聚合”能力。

四、Serverless核心技术栈与生态(2025最新)

4.1 云厂商FaaS产品

云厂商 FaaS产品 2025最新特性
AWS Lambda 支持JDK 17、冷启动优化至10ms内
阿里云 函数计算FC 支持Spring Cloud Function原生集成、GPU函数
腾讯云 云函数SCF 与微信生态深度融合、Serverless容器
华为云 函数工作流FunctionGraph 支持跨区域函数编排

4.2 开源Serverless框架

  • Serverless Framework:跨云厂商的部署工具,支持一键部署函数到AWS/阿里云/腾讯云;
  • Knative:基于K8s的Serverless框架,将容器转换为Serverless服务;
  • OpenFaaS:轻量级开源FaaS平台,支持私有化部署;
  • Spring Cloud Function:Spring官方出品,将Spring应用转换为云函数,支持多云厂商适配。

五、企业级实战:基于Spring Cloud Function + 阿里云FC构建Serverless订单服务

5.1 需求背景

构建一个Serverless订单查询服务,满足以下需求:

  1. 支持根据订单ID查询订单详情(HTTP触发);
  2. 支持根据用户ID查询今日订单列表(HTTP触发);
  3. 数据存储在MySQL 8.0,使用MyBatisPlus操作数据库;
  4. 接口需接入Swagger3文档;
  5. 部署到阿里云FC,支持弹性扩缩容;
  6. 符合阿里Java开发手册,代码规范、无空指针等常见问题。

5.2 技术选型(2025最新稳定版)

技术栈 版本号 说明
JDK 17 符合阿里手册,长期支持版
Maven 3.9.6 项目构建工具
Spring Boot 3.2.5 基础框架
Spring Cloud Function 4.1.0 Serverless函数核心框架
MyBatisPlus 3.5.5 持久层框架
MySQL 8.0.36 关系型数据库
Lombok 1.18.30 简化代码
Fastjson2 2.0.49 JSON处理
Swagger3 2.2.0 接口文档
Alibaba Cloud FC SDK 2.7.0 阿里云FC适配SDK
Guava 33.2.1-jre 集合工具类
Spring Context Support 6.1.6 上下文支持

5.3 项目结构

com.jam.demo
├── config/            # 配置类
│   ├── MyBatisPlusConfig.java
│   └── SwaggerConfig.java
├── entity/            # 实体类
│   └── OrderEntity.java
├── mapper/            # Mapper接口
│   └── OrderMapper.java
├── service/           # 服务层
│   ├── OrderService.java
│   └── impl/
│       └── OrderServiceImpl.java
├── function/          # Serverless函数
│   └── OrderFunction.java
├── util/              # 工具类
│   └── DateUtil.java
├── Application.java   # 启动类
└── pom.xml            # 依赖配置

5.4 完整代码实现

5.4.1 pom.xml(核心依赖)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

   <modelVersion>4.0.0</modelVersion>
   <parent>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-parent</artifactId>
       <version>3.2.5</version>
       <relativePath/>
   </parent>
   <groupId>com.jam.demo</groupId>
   <artifactId>serverless-order-demo</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <name>serverless-order-demo</name>
   <description>Serverless订单服务示例</description>
   <properties>
       <java.version>17</java.version>
       <mybatis-plus.version>3.5.5</mybatis-plus.version>
       <fastjson2.version>2.0.49</fastjson2.version>
       <guava.version>33.2.1-jre</guava.version>
       <swagger.version>2.2.0</swagger.version>
       <lombok.version>1.18.30</lombok.version>
       <aliyun-fc.version>2.7.0</aliyun-fc.version>
   </properties>
   <dependencies>
       <!-- Spring Boot核心 -->
       <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>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-undertow</artifactId>
       </dependency>
       <!-- Spring Cloud Function -->
       <dependency>
           <groupId>org.springframework.cloud</groupId>
           <artifactId>spring-cloud-function-web</artifactId>
           <version>4.1.0</version>
       </dependency>
       <dependency>
           <groupId>org.springframework.cloud</groupId>
           <artifactId>spring-cloud-function-adapter-aws</artifactId>
           <version>4.1.0</version>
       </dependency>
       <!-- MyBatisPlus -->
       <dependency>
           <groupId>com.baomidou</groupId>
           <artifactId>mybatis-plus-boot-starter</artifactId>
           <version>${mybatis-plus.version}</version>
       </dependency>
       <!-- MySQL驱动 -->
       <dependency>
           <groupId>com.mysql</groupId>
           <artifactId>mysql-connector-j</artifactId>
           <version>8.0.36</version>
           <scope>runtime</scope>
       </dependency>
       <!-- Lombok -->
       <dependency>
           <groupId>org.projectlombok</groupId>
           <artifactId>lombok</artifactId>
           <version>${lombok.version}</version>
           <scope>provided</scope>
       </dependency>
       <!-- Fastjson2 -->
       <dependency>
           <groupId>com.alibaba.fastjson2</groupId>
           <artifactId>fastjson2</artifactId>
           <version>${fastjson2.version}</version>
       </dependency>
       <!-- Guava -->
       <dependency>
           <groupId>com.google.guava</groupId>
           <artifactId>guava</artifactId>
           <version>${guava.version}</version>
       </dependency>
       <!-- Swagger3 -->
       <dependency>
           <groupId>io.springfox</groupId>
           <artifactId>springfox-boot-starter</artifactId>
           <version>${swagger.version}</version>
       </dependency>
       <!-- 阿里云FC SDK -->
       <dependency>
           <groupId>com.aliyun.fc.runtime</groupId>
           <artifactId>fc-java-core</artifactId>
           <version>${aliyun-fc.version}</version>
       </dependency>
       <!-- Spring工具类 -->
       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-context-support</artifactId>
           <version>6.1.6</version>
       </dependency>
       <!-- 测试 -->
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-test</artifactId>
           <scope>test</scope>
       </dependency>
   </dependencies>
   <build>
       <plugins>
           <plugin>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-maven-plugin</artifactId>
               <configuration>
                   <mainClass>com.jam.demo.Application</mainClass>
                   <excludes>
                       <exclude>
                           <groupId>org.projectlombok</groupId>
                           <artifactId>lombok</artifactId>
                       </exclude>
                   </excludes>
               </configuration>
           </plugin>
           <!-- 打包为可执行JAR -->
           <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-assembly-plugin</artifactId>
               <version>3.6.0</version>
               <configuration>
                   <descriptorRefs>
                       <descriptorRef>jar-with-dependencies</descriptorRef>
                   </descriptorRefs>
                   <archive>
                       <manifest>
                           <mainClass>com.jam.demo.Application</mainClass>
                       </manifest>
                   </archive>
               </configuration>
               <executions>
                   <execution>
                       <id>make-assembly</id>
                       <phase>package</phase>
                       <goals>
                           <goal>single</goal>
                       </goals>
                   </execution>
               </executions>
           </plugin>
       </plugins>
   </build>
</project>

5.4.2 配置文件(application.yml)

spring:
 application:
   name: serverless-order-demo
 datasource:
   driver-class-name: com.mysql.cj.jdbc.Driver
   url: jdbc:mysql://localhost:3306/serverless_demo?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
   username: root
   password: root123456
 cloud:
   function:
     definition: getOrderById;getTodayOrdersByUserId # 定义函数名称
mybatis-plus:
 configuration:
   map-underscore-to-camel-case: true
   log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
 global-config:
   db-config:
     logic-delete-field: deleted
     logic-delete-value: 1
     logic-not-delete-value: 0
 mapper-locations: classpath:mapper/**/*.xml
server:
 port: 8080
springdoc:
 swagger-ui:
   path: /swagger-ui.html
   enabled: true
 api-docs:
   enabled: true

5.4.3 MyBatisPlus配置类

package com.jam.demo.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
* MyBatisPlus配置类
* @author ken
* @date 2025-05-20
*/

@Configuration
@MapperScan("com.jam.demo.mapper")
public class MyBatisPlusConfig {

   /**
    * 分页插件配置
    * @return MybatisPlusInterceptor
    */

   @Bean
   public MybatisPlusInterceptor mybatisPlusInterceptor() {
       MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
       interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
       return interceptor;
   }
}

5.4.4 Swagger3配置类

package com.jam.demo.config;

import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
* Swagger3配置类
* @author ken
* @date 2025-05-20
*/

@Configuration
public class SwaggerConfig {

   /**
    * 配置API文档信息
    * @return OpenAPI
    */

   @Bean
   public OpenAPI customOpenAPI() {
       return new OpenAPI()
               .info(new Info()
                       .title("Serverless订单服务API文档")
                       .version("1.0.0")
                       .description("基于Spring Cloud Function + 阿里云FC的Serverless订单服务"));
   }
}

5.4.5 日期工具类

package com.jam.demo.util;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;

/**
* 日期工具类
* @author ken
* @date 2025-05-20
*/

public class DateUtil {

   /**
    * 获取今日开始时间(00:00:00)
    * @return LocalDateTime
    */

   public static LocalDateTime getStartOfDay() {
       return LocalDate.now().atStartOfDay();
   }

   /**
    * 获取今日结束时间(23:59:59.999999999)
    * @return LocalDateTime
    */

   public static LocalDateTime getEndOfDay() {
       return LocalDate.now().atTime(LocalTime.MAX);
   }
}

5.4.6 订单实体类

package com.jam.demo.entity;

import com.baomidou.mybatisplus.annotation.*;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
* 订单实体类
* @author ken
* @date 2025-05-20
*/

@Data
@TableName("t_order")
@Schema(description = "订单实体")
public class OrderEntity {

   /**
    * 订单ID
    */

   @TableId(type = IdType.AUTO)
   @Schema(description = "订单ID")
   private Long id;

   /**
    * 用户ID
    */

   @Schema(description = "用户ID")
   private Long userId;

   /**
    * 订单金额
    */

   @Schema(description = "订单金额")
   private BigDecimal amount;

   /**
    * 订单状态(0-待支付,1-已支付,2-已取消)
    */

   @Schema(description = "订单状态")
   private Integer status;

   /**
    * 创建时间
    */

   @TableField(fill = FieldFill.INSERT)
   @Schema(description = "创建时间")
   private LocalDateTime createTime;

   /**
    * 更新时间
    */

   @TableField(fill = FieldFill.INSERT_UPDATE)
   @Schema(description = "更新时间")
   private LocalDateTime updateTime;

   /**
    * 逻辑删除标识(0-未删除,1-已删除)
    */

   @TableLogic
   @Schema(description = "逻辑删除标识")
   private Integer deleted;
}

5.4.7 订单Mapper接口

package com.jam.demo.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jam.demo.entity.OrderEntity;
import org.apache.ibatis.annotations.Param;

import java.time.LocalDateTime;
import java.util.List;

/**
* 订单Mapper接口
* @author ken
* @date 2025-05-20
*/

public interface OrderMapper extends BaseMapper<OrderEntity> {

   /**
    * 根据用户ID和时间范围查询订单列表
    * @param userId 用户ID
    * @param startTime 开始时间
    * @param endTime 结束时间
    * @return 订单列表
    */

   List<OrderEntity> selectByUserIdAndTimeRange(
           @Param("userId")
Long userId,
           @Param("startTime") LocalDateTime startTime,
           @Param("endTime") LocalDateTime endTime)
;
}

5.4.8 订单Mapper XML(resources/mapper/OrderMapper.xml)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jam.demo.mapper.OrderMapper">
   <select id="selectByUserIdAndTimeRange" resultType="com.jam.demo.entity.OrderEntity">
       SELECT * FROM t_order
       WHERE user_id = #{userId}
       AND create_time >= #{startTime}
       AND create_time <= #{endTime}
       AND deleted = 0
   </select>

</mapper>

5.4.9 订单服务接口

package com.jam.demo.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.jam.demo.entity.OrderEntity;

import java.util.List;

/**
* 订单服务接口
* @author ken
* @date 2025-05-20
*/

public interface OrderService extends IService<OrderEntity> {

   /**
    * 根据订单ID查询订单详情
    * @param orderId 订单ID
    * @return 订单详情
    * @throws IllegalArgumentException 订单ID为空时抛出
    */

   OrderEntity getOrderById(Long orderId);

   /**
    * 根据用户ID查询今日订单列表
    * @param userId 用户ID
    * @return 今日订单列表
    * @throws IllegalArgumentException 用户ID为空时抛出
    */

   List<OrderEntity> getTodayOrdersByUserId(Long userId);
}

5.4.10 订单服务实现类

package com.jam.demo.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.jam.demo.entity.OrderEntity;
import com.jam.demo.mapper.OrderMapper;
import com.jam.demo.service.OrderService;
import com.jam.demo.util.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.time.LocalDateTime;
import java.util.List;

/**
* 订单服务实现类
* @author ken
* @date 2025-05-20
*/

@Service
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderMapper, OrderEntity> implements OrderService {

   /**
    * 根据订单ID查询订单详情
    * @param orderId 订单ID
    * @return 订单详情
    * @throws IllegalArgumentException 订单ID为空时抛出
    */

   @Override
   public OrderEntity getOrderById(Long orderId) {
       // 判空校验(符合阿里手册)
       if (ObjectUtils.isEmpty(orderId)) {
           log.error("查询订单详情失败:订单ID为空");
           throw new IllegalArgumentException("订单ID不能为空");
       }
       OrderEntity order = this.getById(orderId);
       if (ObjectUtils.isEmpty(order)) {
           log.warn("查询订单详情失败:订单ID={}不存在", orderId);
           return null;
       }
       log.info("查询订单详情成功:订单ID={}", orderId);
       return order;
   }

   /**
    * 根据用户ID查询今日订单列表
    * @param userId 用户ID
    * @return 今日订单列表
    * @throws IllegalArgumentException 用户ID为空时抛出
    */

   @Override
   public List<OrderEntity> getTodayOrdersByUserId(Long userId) {
       // 判空校验(符合阿里手册)
       if (ObjectUtils.isEmpty(userId)) {
           log.error("查询今日订单失败:用户ID为空");
           throw new IllegalArgumentException("用户ID不能为空");
       }
       LocalDateTime startTime = DateUtil.getStartOfDay();
       LocalDateTime endTime = DateUtil.getEndOfDay();
       List<OrderEntity> orderList = baseMapper.selectByUserIdAndTimeRange(userId, startTime, endTime);
       // 空集合处理(避免返回null)
       List<OrderEntity> result = ObjectUtils.isEmpty(orderList) ? Lists.newArrayList() : orderList;
       log.info("查询今日订单成功:用户ID={},订单数量={}", userId, result.size());
       return result;
   }
}

5.4.11 Serverless函数实现类

package com.jam.demo.function;

import com.alibaba.fastjson2.JSON;
import com.jam.demo.entity.OrderEntity;
import com.jam.demo.service.OrderService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Map;

/**
* 订单Serverless函数
* @author ken
* @date 2025-05-20
*/

@Component
@Slf4j
@RequiredArgsConstructor
@Tag(name = "订单函数接口", description = "基于Serverless的订单查询接口")
public class OrderFunction {

   private final OrderService orderService;

   /**
    * 根据订单ID查询订单详情函数
    * @param request 请求参数(JSON格式,包含orderId)
    * @return 订单详情JSON字符串
    */

   @Operation(summary = "根据订单ID查询订单详情", description = "Serverless函数:根据订单ID查询订单详情")
   public String getOrderById(
           @Parameter(description = "请求参数,格式:{\"orderId\":1}")
String request) {
       // 参数校验
       if (!StringUtils.hasText(request)) {
           log.error("查询订单详情失败:请求参数为空");
           return JSON.toJSONString(Map.of("code", 400, "msg", "请求参数不能为空"));
       }
       try {
           Map<String, Object> paramMap = JSON.parseObject(request);
           Long orderId = Long.valueOf(paramMap.get("orderId").toString());
           OrderEntity order = orderService.getOrderById(orderId);
           if (order == null) {
               return JSON.toJSONString(Map.of("code", 404, "msg", "订单不存在", "data", null));
           }
           return JSON.toJSONString(Map.of("code", 200, "msg", "查询成功", "data", order));
       } catch (IllegalArgumentException e) {
           log.error("查询订单详情失败:参数非法", e);
           return JSON.toJSONString(Map.of("code", 400, "msg", e.getMessage()));
       } catch (Exception e) {
           log.error("查询订单详情失败:系统异常", e);
           return JSON.toJSONString(Map.of("code", 500, "msg", "系统异常"));
       }
   }

   /**
    * 根据用户ID查询今日订单列表函数
    * @param request 请求参数(JSON格式,包含userId)
    * @return 今日订单列表JSON字符串
    */

   @Operation(summary = "根据用户ID查询今日订单列表", description = "Serverless函数:根据用户ID查询今日订单列表")
   public String getTodayOrdersByUserId(
           @Parameter(description = "请求参数,格式:{\"userId\":1}")
String request) {
       // 参数校验
       if (!StringUtils.hasText(request)) {
           log.error("查询今日订单失败:请求参数为空");
           return JSON.toJSONString(Map.of("code", 400, "msg", "请求参数不能为空"));
       }
       try {
           Map<String, Object> paramMap = JSON.parseObject(request);
           Long userId = Long.valueOf(paramMap.get("userId").toString());
           List<OrderEntity> orderList = orderService.getTodayOrdersByUserId(userId);
           return JSON.toJSONString(Map.of("code", 200, "msg", "查询成功", "data", orderList));
       } catch (IllegalArgumentException e) {
           log.error("查询今日订单失败:参数非法", e);
           return JSON.toJSONString(Map.of("code", 400, "msg", e.getMessage()));
       } catch (Exception e) {
           log.error("查询今日订单失败:系统异常", e);
           return JSON.toJSONString(Map.of("code", 500, "msg", "系统异常"));
       }
   }
}

5.4.12 启动类

package com.jam.demo;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.function.context.config.EnableFunctionRegistrar;

/**
* 应用启动类
* @author ken
* @date 2025-05-20
*/

@SpringBootApplication
@EnableFunctionRegistrar
@MapperScan("com.jam.demo.mapper")
public class Application {

   public static void main(String[] args) {
       SpringApplication.run(Application.class, args);
   }
}

5.4.13 MySQL表结构(t_order)

CREATE DATABASE IF NOT EXISTS serverless_demo DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE serverless_demo;

CREATE TABLE IF NOT EXISTS t_order (
   id BIGINT AUTO_INCREMENT COMMENT '订单ID',
   user_id BIGINT NOT NULL COMMENT '用户ID',
   amount DECIMAL(10,2) NOT NULL COMMENT '订单金额',
   status TINYINT NOT NULL DEFAULT 0 COMMENT '订单状态(0-待支付,1-已支付,2-已取消)',
   create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
   update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   deleted TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除标识(0-未删除,1-已删除)',
   PRIMARY KEY (id),
   INDEX idx_user_id (user_id),
   INDEX idx_create_time (create_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';

-- 插入测试数据
INSERT INTO t_order (user_id, amount, status) VALUES (1, 99.99, 1);
INSERT INTO t_order (user_id, amount, status) VALUES (1, 199.99, 0);
INSERT INTO t_order (user_id, amount, status) VALUES (2, 299.99, 1);

5.5 本地测试

  1. 启动MySQL 8.0,执行上述表结构和测试数据SQL;
  2. 启动Spring Boot应用(JDK 17环境);
  3. 访问Swagger文档:http://localhost:8080/swagger-ui.html,测试接口:
  • 测试getOrderById:请求参数{"orderId":1},返回订单详情;
  • 测试getTodayOrdersByUserId:请求参数{"userId":1},返回今日订单列表。

5.6 部署到阿里云FC

5.6.1 打包应用

执行Maven命令打包:

mvn clean package -DskipTests

打包完成后,生成serverless-order-demo-0.0.1-SNAPSHOT-jar-with-dependencies.jar文件。

5.6.2 创建阿里云FC函数

  1. 登录阿里云FC控制台,创建“自定义运行时”函数;
  2. 上传上述JAR包;
  3. 配置函数入口:com.jam.demo.Application
  4. 配置环境变量(数据库连接信息):
  • SPRING_DATASOURCE_URL: jdbc:mysql://xxx.xxx.xxx.xxx:3306/serverless_demo?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
  • SPRING_DATASOURCE_USERNAME: root
  • SPRING_DATASOURCE_PASSWORD: root123456
  1. 配置触发器:创建HTTP触发器,支持公网访问。

5.6.3 线上测试

调用阿里云FC的HTTP触发器地址,测试接口:

# 测试getOrderById
curl -X POST -H "Content-Type: application/json" -d "{\"orderId\":1}" https://xxx.cn-shanghai.fcapp.run/getOrderById

# 测试getTodayOrdersByUserId
curl -X POST -H "Content-Type: application/json" -d "{\"userId\":1}" https://xxx.cn-shanghai.fcapp.run/getTodayOrdersByUserId

六、Serverless架构的坑与避坑指南(2025实战总结)

6.1 冷启动问题

问题本质:

首次调用函数时,云厂商需要初始化运行时环境(如JVM)、加载代码、初始化依赖(如数据库连接池),导致响应时间变长(Java函数冷启动通常100ms-3s)。

避坑方案:

  1. 预热实例:阿里云FC/腾讯云SCF支持配置“预留实例”,保持一定数量的预热实例,避免冷启动;
  2. 运行时优化
  • Java函数使用GraalVM编译为原生镜像,冷启动时间可降至10ms内;
  • 减少不必要的依赖(如移除无用的JAR包),缩小包体积;
  1. 函数拆分:将大函数拆分为小函数,减少初始化时间;
  2. 触发策略:定时触发函数(如每分钟调用一次),保持实例预热。

6.2 状态管理问题

问题本质:

Serverless函数是无状态的,无法在本地保存状态(如用户会话、缓存数据),多次调用可能使用不同的实例。

避坑方案:

  1. 使用BaaS服务:将状态存储在Redis/MongoDB等托管服务中;
  2. 避免本地缓存:不要在函数中使用本地缓存(如HashMap),改用Redis分布式缓存;
  3. 幂等性设计:函数需支持幂等性(如通过订单号+流水号去重),避免重复执行导致数据错误。

6.3 性能与成本平衡

问题本质:

高内存规格的函数执行速度快,但计费更高;低内存规格的函数计费低,但执行速度慢(甚至超时)。

避坑方案:

  1. 性能测试:通过压测确定最优内存规格(阿里云FC推荐Java函数使用1GB内存);
  2. 超时配置:根据函数执行时间合理配置超时时间(避免过早终止,也避免过长占用资源);
  3. 批量处理:批处理场景下,将小批量任务合并为大批量任务,减少函数调用次数。

6.4 监控与排障

问题本质:

Serverless函数的日志分散,难以定位问题;函数实例是临时的,无法远程调试。

避坑方案:

  1. 日志聚合:将函数日志输出到阿里云SLS/腾讯云CLS,统一聚合分析;
  2. 链路追踪:接入阿里云ARMS/OpenTelemetry,实现全链路追踪;
  3. 本地调试:使用云厂商提供的本地调试工具(如阿里云FC Local),模拟线上环境调试。

七、Serverless架构的适用场景与不适用场景

7.1 适用场景

  1. 突发流量场景:秒杀、促销、直播带货等流量波动大的场景,Serverless可弹性扩容;
  2. 定时任务场景:数据同步、报表生成、日志清理等定时任务,按需执行,成本低;
  3. 事件驱动场景:订单创建、支付回调、文件上传等事件触发的业务;
  4. 长尾流量场景:访问量低且分散的接口(如后台管理系统),Serverless按需付费,成本仅为传统部署的10%;
  5. 微服务拆分场景:将微服务拆分为多个小函数,独立部署、独立扩缩容。

7.2 不适用场景

  1. 长连接场景:WebSocket、TCP长连接等需要长时间保持连接的业务;
  2. 高CPU/IO密集型场景:视频转码、大数据计算等长时间占用资源的业务(推荐使用容器/裸金属);
  3. 低延迟要求场景:金融交易、实时风控等要求响应时间<10ms的业务(冷启动无法满足);
  4. 强状态场景:需要大量本地缓存、会话管理的业务(无状态设计成本过高)。

八、Serverless架构未来趋势(2025-2030)

  1. Serverless 4.0:云厂商将推出“零冷启动”函数,通过智能预热和运行时优化,将冷启动时间降至10ms内;
  2. 分布式Serverless:Serverless从单云厂商走向跨云、跨地域分布式部署,支持全球弹性;
  3. 边缘计算+Serverless:函数部署到边缘节点(如CDN节点),降低访问延迟,支持本地化计算;
  4. AI+Serverless:云厂商推出AI原生的Serverless函数,支持一键集成大模型能力,简化AI应用开发;
  5. 私有化Serverless:开源Serverless框架(如Knative)将更成熟,企业可在私有云部署Serverless平台。

九、总结

Serverless架构不是银弹,但它是云计算发展的必然趋势——它彻底改变了开发者的工作模式,让开发者从“全栈运维”中解放出来,聚焦业务价值。本文从底层逻辑出发,拆解了Serverless的核心架构模式,实现了企业级订单服务案例,同时给出了避坑指南和场景选型建议。

要掌握Serverless架构,核心是理解“无服务器不是没有服务器,而是无需关心服务器”,在实际落地时,需结合业务场景选择合适的架构模式,平衡性能、成本和可维护性。随着云厂商技术的不断迭代,Serverless将在更多场景下替代传统架构,成为企业数字化转型的核心技术选择。

目录
相关文章
|
2月前
|
人工智能 运维 安全
一文看懂函数计算 AgentRun,让 Agentic AI 加速进入企业生产环境
AgentRun 的愿景很简单:让 AI Agent 从 Demo 到生产级部署,变得前所未有的简单。通过 Serverless 架构持续优化成本并解放运维负担,通过企业级 Runtime 提供生产级的执行环境和安全保障,通过开源生态集成避免框架锁定,通过全链路可观测让每个环节都清晰可控——这就是 AgentRun 要为企业提供的完整解决方案。
|
3月前
|
消息中间件 存储 Java
庖丁解牛:RocketMQ Broker/Consumer/Producer源码深度剖析与实战
本文深入剖析了RocketMQ的核心机制,从源码层面解析了Producer、Broker和Consumer三大组件。Producer部分详细分析了消息发送流程、队列选择策略和重试机制;Broker部分重点讲解了消息存储架构(CommitLog、ConsumeQueue)、请求处理和刷盘策略;Consumer部分则解析了推/拉模式、偏移量管理和重试机制。通过实战案例展示了分布式事务消息和消息过滤功能,并提供性能优化建议。
260 1
|
SQL 关系型数据库 数据库
学习分布式事务Seata看这一篇就够了,建议收藏
学习分布式事务Seata看这一篇就够了,建议收藏
21692 2
|
2月前
|
人工智能 数据可视化 API
看完《疯狂动物城》心痒痒?试试ComfyUI,让朱迪和尼克走进你的画布
看完《疯狂动物城》意犹未尽?用ComfyUI+Flux文生图模型,让朱迪和尼克跃然纸上!通过节点式工作流精准控制生成细节,还原动画级质感。毛发、表情、服饰皆栩栩如生,支持风格定制与角色一致性强的图像创作。无需高配硬件,Lab4AI平台一键部署,轻松实现你的创意构想。Anyone can create anything!
509 1
看完《疯狂动物城》心痒痒?试试ComfyUI,让朱迪和尼克走进你的画布
|
运维 监控 Serverless
揭秘云计算中的Serverless架构:优势、挑战与实践
揭秘云计算中的Serverless架构:优势、挑战与实践
505 0
|
9月前
|
运维 Prometheus 监控
别再靠“运维小哥半夜报警”了!大模型搞定实时事件监测!
别再靠“运维小哥半夜报警”了!大模型搞定实时事件监测!
553 15
|
10月前
|
SQL 人工智能 自然语言处理
Text2SQL圣经:从0到1精通Text2Sql(Chat2Sql)的原理,以及Text2Sql开源项目的使用
Text2SQL圣经:从0到1精通Text2Sql(Chat2Sql)的原理,以及Text2Sql开源项目的使用
Text2SQL圣经:从0到1精通Text2Sql(Chat2Sql)的原理,以及Text2Sql开源项目的使用
|
人工智能 移动开发 前端开发
WeaveFox:蚂蚁集团推出 AI 前端智能研发平台,能够根据设计图直接生成源代码,支持多种客户端和技术栈
蚂蚁团队推出的AI前端研发平台WeaveFox,能够根据设计图直接生成前端源代码,支持多种应用类型和技术栈,提升开发效率和质量。本文将详细介绍WeaveFox的功能、技术原理及应用场景。
6647 68
WeaveFox:蚂蚁集团推出 AI 前端智能研发平台,能够根据设计图直接生成源代码,支持多种客户端和技术栈
|
12月前
|
Cloud Native 安全 Serverless
云原生应用实战:基于阿里云Serverless的API服务开发与部署
随着云计算的发展,Serverless架构日益流行。阿里云函数计算(Function Compute)作为Serverless服务,让开发者无需管理服务器即可运行代码,按需付费,简化开发运维流程。本文从零开始,介绍如何使用阿里云函数计算开发简单的API服务,并探讨其核心优势与最佳实践。通过Python示例,演示创建、部署及优化API的过程,涵盖环境准备、代码实现、性能优化和安全管理等内容,帮助读者快速上手Serverless开发。