Serverless 架构实战:告别运维烦恼,用云函数构建高并发应用(附完整代码)

你有没有遇到过这样的困境:服务器一闲置就浪费钱,流量一上来就崩?Serverless 可能正是你需要的答案。


什么是 Serverless?先搞清楚概念

很多人听到「Serverless」以为是”没有服务器”,其实不对——准确说法是你不需要管服务器

底层依然有服务器在运行,只不过那是云厂商的事,你只需要写业务代码,按实际调用次数和计算时长付费。

核心特点:

特性传统服务器Serverless
计费方式按时间(包月/包年)按调用次数+时长
运维成本需要维护操作系统、环境零运维
扩容方式手动或半自动自动弹性扩缩容
冷启动有(首次调用有延迟)
适用场景长期稳定型业务事件驱动、波峰业务

Serverless 的三大核心产品形态

1. 函数计算(FaaS)

这是 Serverless 最核心的形态。你写一个函数,部署上去,触发时自动执行。

主流平台:

  • 腾讯云云函数(SCF)
  • 阿里云函数计算(FC)
  • AWS Lambda
  • Azure Functions

2. 无服务器容器(Serverless Container)

如果你的应用太复杂,用容器部署但不想管底层资源,可以用:

  • 腾讯云弹性容器实例(ECI)
  • 阿里云 Serverless Kubernetes
  • AWS Fargate

3. BaaS(后端即服务)

数据库、认证、消息推送等基础设施全托管,如腾讯云开发(CloudBase)、Firebase。


实战:用腾讯云云函数构建图片压缩服务

业务场景

用户上传图片到 COS(对象存储),触发云函数自动压缩,压缩后保存到另一个 Bucket。

传统做法:需要一台持续运行的服务器监听事件。
Serverless 做法:只在有图片上传时才运行,每月调用费用可能不到 1 元。

Step 1:创建 COS Bucket

# 使用腾讯云 CLI 创建 Bucket
coscmd config -a YOUR_SECRET_ID -s YOUR_SECRET_KEY -b your-upload-bucket-1234567890 -r ap-guangzhou
coscmd createbucket

或者在控制台创建两个 Bucket:

  • images-upload:接收原始上传
  • images-compressed:存储压缩后的图片

Step 2:编写云函数代码

import json
import os
import boto3
from PIL import Image
import io

# 云函数入口
def main_handler(event, context):
    """
    触发器:COS 对象创建事件
    功能:压缩图片并保存到目标 Bucket
    """
    
    # 解析触发事件
    cos_records = event.get('Records', [])
    
    for record in cos_records:
        cos_event = record.get('cos', {})
        bucket_name = cos_event['cosBucket']['name']
        object_key = cos_event['cosObject']['key']
        region = cos_event['cosBucket']['appid']
        
        print(f"处理文件: {object_key}")
        
        # 初始化 COS 客户端(云函数内无需密钥,使用临时凭证)
        import qcloud_cos
        from qcloud_cos import CosConfig, CosS3Client
        
        config = CosConfig(
            Region='ap-guangzhou',
            SecretId=os.environ.get('TENCENTCLOUD_SECRETID'),
            SecretKey=os.environ.get('TENCENTCLOUD_SECRETKEY'),
            Token=os.environ.get('TENCENTCLOUD_SESSIONTOKEN')
        )
        client = CosS3Client(config)
        
        # 下载原始图片
        response = client.get_object(
            Bucket=bucket_name,
            Key=object_key
        )
        image_data = response['Body'].get_raw_stream().read()
        
        # 压缩图片(使用 Pillow)
        img = Image.open(io.BytesIO(image_data))
        
        # 转换为 RGB(处理 PNG 透明通道)
        if img.mode in ('RGBA', 'P'):
            img = img.convert('RGB')
        
        # 调整尺寸(最大宽度 1920px)
        max_width = 1920
        if img.width > max_width:
            ratio = max_width / img.width
            new_height = int(img.height * ratio)
            img = img.resize((max_width, new_height), Image.LANCZOS)
        
        # 压缩输出
        output_buffer = io.BytesIO()
        img.save(output_buffer, format='JPEG', quality=85, optimize=True)
        output_buffer.seek(0)
        
        compressed_size = len(output_buffer.getvalue())
        original_size = len(image_data)
        ratio = (1 - compressed_size / original_size) * 100
        
        print(f"压缩率: {ratio:.1f}% ({original_size} -> {compressed_size} bytes)")
        
        # 上传到目标 Bucket
        target_bucket = 'images-compressed-1234567890'
        client.put_object(
            Bucket=target_bucket,
            Key=object_key,
            Body=output_buffer.getvalue()
        )
        
        print(f"已上传到: {target_bucket}/{object_key}")
    
    return {
        'statusCode': 200,
        'body': json.dumps({'message': '处理完成', 'count': len(cos_records)})
    }

Step 3:配置依赖

在函数目录创建 requirements.txt

Pillow==10.3.0
cos-python-sdk-v5==1.9.30

打包上传:

pip install -r requirements.txt -t ./package
cd package && zip -r ../function.zip .
cd .. && zip -g function.zip index.py

Step 4:配置触发器

在腾讯云控制台:

  1. 进入云函数 → 新建函数
  2. 运行环境选 Python 3.9
  3. 上传 function.zip
  4. 添加触发器 → 选择 COS → 选择 images-upload Bucket → 事件类型:全部创建事件

Step 5:设置内存和超时

图片处理推荐配置:

  • 内存:512MB(大图可调到 1GB)
  • 超时:60 秒
  • 并发:100(根据业务需求调整)

解决方案:

# 方案1:预置并发(腾讯云SCF支持)
# 在控制台设置预置并发数 = 10,保持热实例

# 方案2:定时预热,每5分钟触发一次"心跳"请求
import requests
def warm_up():
    requests.get('https://your-function-url/ping', timeout=5)

坑2:依赖包太大

云函数有部署包大小限制(腾讯云 SCF 解压后最大 500MB)。

解决方案:

  • 使用 Layer(层)共享依赖,多个函数复用
  • 精简依赖,只安装必要的子模块

坑3:临时文件路径

云函数只有 /tmp 目录可写,且大小有限制(512MB)。

# ✅ 正确写法
import tempfile
with tempfile.NamedTemporaryFile(dir='/tmp', suffix='.jpg') as f:
    f.write(image_data)

# ❌ 错误写法:尝试写入其他目录会权限拒绝
with open('/var/data/image.jpg', 'wb') as f:
    f.write(image_data)

费用估算:Serverless 到底能省多少钱?

以图片压缩服务为例,假设每天处理 10,000 张图片:

项目传统服务器Serverless
计算成本1核2G ECS:约¥50/月按调用:约¥3/月
流量成本包含在 ECS 费用中单独计费,约¥5/月
运维成本需要人工维护零运维
总计~¥50/月~¥8/月

月省约 ¥42,节省 84%。

当然,如果你的业务是持续高并发(每分钟几万请求),Serverless 的费用可能反而更高——需要根据实际调用量测算。


小结

Serverless 不是银弹,但对于事件驱动、低频调用、波峰业务来说,它是降本提效的利器。

建议行动路线:

  1. 先从一个非核心的小功能开始实践(如文件处理、Webhook 接收)
  2. 熟悉冷启动、依赖管理等坑点
  3. 逐步迁移适合的业务场景

不要一上来就把整个应用 Serverless 化——那会踩很多不必要的坑。

发表评论