13 性能、安全与生产实践
大约 4 分钟编程语言Python性能安全生产实践
13 性能、安全与生产实践
先测量,再优化;先减少信任边界,再考虑复杂框架。生产可靠性来自明确配置、超时、日志、权限和停机行为。
1. 性能测量顺序
- 定义用户可感知的指标:延迟、吞吐、CPU、内存、连接数或成本。
- 建立可重复基准,固定数据量、环境和版本。
- 使用 profiler 找主要热点。
- 只改变一个关键变量并复测。
- 保留回归基准和容量说明。
微基准示例:
python -m timeit -s "values=list(range(1000))" "sum(values)"函数级 profiler:
python -m cProfile -s cumulative app.py内存分配:
import tracemalloc
tracemalloc.start()
payload = [str(number) for number in range(100_000)]
current, peak = tracemalloc.get_traced_memory()
print(current, peak)
tracemalloc.stop()2. 常见优化方向
- 选择正确数据结构和算法,优于局部语法微调。
- 避免不必要的复制、序列化和对象创建。
- 批量访问远端、数据库和文件,降低往返次数。
- 把 CPU 密集工作移到进程池、原生扩展或独立服务。
- 缓存前先确认命中率、内存占用和失效策略。
- 用生成器处理流式数据,减少峰值内存。
优化不能破坏错误处理、安全边界和可观测性。若复杂度显著增加但收益不足,应保留简单实现。
3. 安全输入边界
高风险接口:
eval、exec、动态导入不可信名称。pickle反序列化不可信数据。subprocess搭配shell=True。- SQL 字符串拼接。
- 用户控制的文件路径、URL、主机名和正则。
安全替代:
import shlex
import subprocess
arguments = shlex.split("--limit 10") # 仅在需要将已有命令行文本拆分时使用
completed = subprocess.run(
["tool", *arguments],
check=False,
capture_output=True,
text=True,
timeout=30,
)能接收参数列表时直接接收列表,不要先把不可信字符串拼接成 shell 命令。
4. 反序列化
import json
payload = json.loads('{"service": "api"}')pickle 在加载时可能执行任意代码,只应加载可信来源且来源完整性受保护的数据。YAML 也应使用安全加载方式;JSON 解析本身相对安全,但结果仍需校验。
5. 密码、令牌与随机数
import hmac
import secrets
token = secrets.token_urlsafe(32)
expected = b"expected"
actual = b"expected"
print(token)
print(hmac.compare_digest(actual, expected))原则:
- 令牌使用
secrets。 - 比较签名或令牌使用常量时间比较。
- 密钥从环境变量、密钥管理服务或受限文件读取。
- 日志、异常和监控标签不包含明文秘密。
- 密码使用成熟密码哈希库和参数,不自行设计加密方案。
6. TLS 与网络
生产客户端应:
- 校验证书和主机名,不关闭验证。
- 设置连接和读取超时。
- 控制重试次数、退避和幂等性。
- 明确代理、证书链、SNI 和 DNS 行为。
- 记录目标服务的逻辑名称而不是秘密 URL。
标准库示例:
import ssl
import urllib.request
context = ssl.create_default_context()
with urllib.request.urlopen("https://www.python.org/", context=context, timeout=10) as response:
print(response.status)7. 生产配置
命令行参数、环境变量和配置文件应集中解析并校验:
import os
from dataclasses import dataclass
@dataclass(frozen=True, slots=True)
class Settings:
listen_host: str
listen_port: int
log_level: str
def load_settings() -> Settings:
host = os.getenv("APP_HOST", "127.0.0.1")
port = int(os.getenv("APP_PORT", "8080"))
if not 1 <= port <= 65535:
raise ValueError("APP_PORT out of range")
return Settings(host, port, os.getenv("APP_LOG_LEVEL", "INFO"))配置错误应在启动时失败,不要等第一个请求到达后才暴露。
8. 信号与优雅停机
import signal
import threading
stopping = threading.Event()
def request_stop(signum: int, frame: object) -> None:
stopping.set()
signal.signal(signal.SIGTERM, request_stop)
signal.signal(signal.SIGINT, request_stop)
while not stopping.is_set():
stopping.wait(1.0)容器和 systemd 会发送信号。程序应停止接收新工作、给在途任务短暂完成窗口、取消剩余任务、关闭连接并记录退出原因。
9. 可观测性
至少暴露:
- 运行版本和启动时间。
- 活跃任务、队列长度和连接数。
- 请求/任务总数、失败数和耗时分布。
- 超时、重试、取消和限流计数。
- dependency 的可用性状态。
日志、指标和 trace 的标识要能关联,避免每个系统使用不同请求 ID。
10. 容器与资源限制
- 固定基础镜像和依赖摘要。
- 使用非 root 用户。
- 只复制运行所需文件。
- 设置 CPU、内存、文件描述符和进程限制后做压测。
- 正确处理
SIGTERM。 - 不把构建工具、测试数据和密钥带入运行镜像。
11. 安全检查清单
- 代码、依赖和镜像是否扫描。
- 是否禁用不可信输入进入动态执行接口。
- 是否使用最小权限账号和临时凭据。
- 是否有超时、限流和总任务预算。
- 是否记录敏感数据的访问和失败。
- 是否能够快速撤销令牌和回滚版本。
