十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

AWS SaaS多租户架构实战:从隔离模型到成本归属

AWS SaaS多租户架构实战:从隔离模型到成本归属 简介SaaS 平台架构正成为传统软件厂商转型的方向而 AWS 提供了完整的实施路径。这份 PPT 面向云计算架构师、SaaS 产品经理与技术决策者以解决方案视角系统梳理了在 AWS 上设计 SaaS 平台的核心模块包括身份管理与租户隔离、Silo、Bridge、Pool 三种多租户模式的关键取舍、应用分层与数据隔离方式以及基于 CloudWatch、CloudTrail、Config 的监控运维和基于标签、API 计费与统一视图的租户计量方案同时延伸到 DevOps 敏捷交付、大数据分析、物联网和人工智能服务集成帮助读者理解云原生 SaaS 的全景。资源为单个 PPTX 文件大小约 1.21MB内容紧凑、图文结合适合作为架构选型、方案评审和团队内部培训的参考资料。目前已有 143 人浏览/学习读者可快速从这套架构梳理中掌握 AWS SaaS 的设计要点、模式权衡与业务扩展路径减少前期调研和试错成本。1. 从“租户隔离”到“账单归属”AWS SaaS 架构的真正分水岭SaaS 平台架构在 AWS 上落地最容易犯的错误不是选错服务而是把“多租户”理解成了“多套环境”。一套租户一套 VPC、一套数据库、一套 EKS 集群看似隔离彻底实际上运维成本随租户数量线性增长很快会把毛利率吃掉。反过来说全池化共享虽然资源利用率高但一个租户的慢查询或暴力请求会拖垮所有人SLA 没法交代。业界对 AWS SaaS 平台架构的共识是按“租户模型”分层决策而不是按“环境”复制。控制面共享、数据面按合规和性能要求选择隔离级别计费维度独立于技术隔离维度。这条思路能同时解决三个问题——新租户上线速度、单租户资源爆炸半径、以及月底成本分摊时财务和研发不吵架。这篇文章面向的是要亲自搭平台、或者要给已有系统做 SaaS 化改造的工程师。下面按一条完整落地路径展开先定租户模型和 AWS 服务选型再做身份与数据面隔离然后把计费与成本归属做进架构里最后补控制面扩展和排错技巧。内容全部基于可直接执行的 AWS 服务展开不涉及任何第三方代理或网络穿透方案。2. 多租户模型选型先定隔离粒度再谈 AWS 服务映射2.1 共享与隔离的光谱三种基础模型SaaS 多租户架构不存在唯一的正确答案只有基于业务约束的取舍。AWS 官方在 SaaS 架构白皮书里把租户模型分成三层池化Pool、桥接Bridge、隔离Silo。这个分法不抽象直接对应你能接受的故障爆炸半径和运维复杂度。池化模型是所有租户共享同一套应用实例和数据库用租户 ID 做逻辑隔离。优点是资源利用率最高、新租户上线几乎零成本缺点是任何一个租户的异常查询、热点 Key、全表扫描都可能影响全局。桥接模型是部分资源共享、部分资源独享比如应用层共享但数据库按租户分 Schema或者反过来。隔离模型是每个租户一套独立资源栈隔离最彻底但成本最高通常只留给金融、医疗或对数据驻留有硬性要求的租户。选型时我会先画一张表把每个租户维度的合规要求、性能基线和预算上限列出来然后按“能否接受与别人共享物理资源”来归类。这不是一次性决策——同一个平台里免费版租户走池化、企业版租户走隔离完全可以并存。比如免费版放在共享 ECS 自动扩缩容组里数据库用共享 RDS PostgreSQL 实例加 Row Level Security。企业版用独立 EKS 命名空间加独立 Aurora 集群。这样既不牺牲免费用户的体验也不让大客户为小客户的问题买单。租户模型隔离粒度AWS 典型承载成本曲线适合场景池化 Pool应用共享 数据库共享ECS RDS / DynamoDB 单表恒定边际成本趋近零SaaS 免费版、内部工具、POC 验证桥接 Bridge应用共享 数据库隔离EKS 按租户 Schema / 按租户 DynamoDB 表中等随租户缓慢上升成长型企业客户性能可预期隔离 Silo计算 存储全独立独立 ECS 服务 独立 Aurora / S3 桶线性每个租户都有基础开销金融、医疗、政府、大客户定制2.2 AWS 服务与控制面/数据面映射架构上要把“控制面”和“数据面”拆开思考。控制面负责租户生命周期管理注册、开通、配置下发、计费采集数据面负责业务请求处理API 调用、数据读写、文件存取。两者在 AWS 上的服务选型完全不同。控制面我一般用 API Gateway Lambda Step Functions 编排。一个新租户注册进来Step Functions 依次执行创建租户记录、初始化数据库 Schema、创建或绑定资源、发放初始配置。这套流程天然异步、可重试、可观测。数据面则根据第 2.1 节的模型选择池化租户走共享 ECS 服务隔离租户走独立 ECS 服务或 App Runner。一个常被忽略的点是数据面服务的自动扩缩容策略必须与租户模型绑定。池化模式下扩缩容看的是整体请求量和平均延迟用 Application Auto Scaling 按 CPU 和请求数组合扩缩。隔离模式下每个租户的服务要单独配置最小实例数避免冷启动延迟影响客户体验。2.2.1 租户上下文如何穿透到 AWS 资源层级租户上下文不只是一个数据库字段。在 AWS 上它要穿透到 IAM 策略、KMS 密钥、CloudWatch 日志组、S3 桶前缀等各个层级。常见做法是API Gateway 在请求进入时校验 JWT从 Token 的custom:tenant_id声明中提取租户标识写入请求头x-tenant-id下游服务统一从这个头读取。# Lambda 授权器从 JWT 提取租户上下文并注入请求头 def lambda_handler(event, context): # event 是 API Gateway 传入的授权请求 method_arn event[methodArn] token event[authorizationToken] # 解码 JWT提取租户 ID 和用户角色 # 生产环境请用 AWS Cognito 的 verify 接口或自管 JWKS claims decode_jwt(token) tenant_id claims.get(custom:tenant_id) # 生成 IAM 策略限定该租户只能访问自己的资源前缀 policy { Version: 2012-10-17, Statement: [{ Action: execute-api:Invoke, Effect: Allow, Resource: f{method_arn}/*/*/{tenant_id}/* }] } return { principalId: claims[sub], policyDocument: policy, context: { tenantId: tenant_id, userId: claims[sub] } }这段代码的逻辑是把租户 ID 注入 IAM 策略的 Resource ARN 中API Gateway 会将路径参数传给下游并注入context里的tenantId。这样每个租户的请求只能触达自己前缀下的资源路径即使拿到了别人的 API URL也无法越权访问。参数说明method_arn来自 API Gateway 的事件结构格式为arn:aws:execute-api:region:account-id:api-id/stage/method/pathcustom:tenant_id是 Cognito 用户池自定义属性的标准写法注册租户管理员账号时通过 AdminCreateUser 写入。这套机制能把租户身份从应用代码里彻底剥离下沉到基础设施层。3. 身份体系与数据面隔离Cognito 池化、DynamoDB 分区键与 RDS Schema 方案3.1 Cognito 多租户身份一个用户池还是多个用户池SaaS 平台最纠结的问题之一是所有租户共用一个 Cognito User Pool还是每个租户一个 Pool两个方案都有实践者但适用场景完全不同。共用用户池的好处是运维简单一个 Pool 管所有用户Lambda trigger 统一配置。但坏处也很明显用户名天然全局唯一两个租户想要同样的用户名比如 admincompany.com就会冲突密码策略、MFA 配置也只能全局生效无法按租户定制。每租户独立用户池则相反隔离彻底、支持每租户独立的安全策略但 Cognito 服务配额默认每个 AWS 账号最多 1000 个 User Pool会成为硬上限。我的建议是默认共用 Pool按租户做分组隔离仅当客户明确要求独立身份存储时才创建独立 Pool。共用 Pool 下用 Cognito Group 映射租户 ID用户在注册时通过custom:tenant_id属性标记归属。登录后从 ID Token 的cognito:groups声明读取角色从custom:tenant_id读取租户归属。3.1.1 用 Pre Token Generation Trigger 注入租户上下文Cognito 的 Pre Token Generation Lambda Trigger 是 SaaS 架构的关键钩子。它可以动态修改 ID Token 和 Access Token 的内容把租户角色和权限声明注入 Token下游服务无需查数据库就能完成鉴权和租户路由。# 每次签发/刷新 Token 时触发动态注入租户角色 import json def lambda_handler(event, context): # event 结构中包含 request.userAttributes 和 groupConfiguration user_attrs event[request][userAttributes] tenant_id user_attrs.get(custom:tenant_id, unknown) # 根据用户在租户内的角色生成自定义 claims # 这里可以从 DynamoDB 读取租户级角色映射避免硬编码 claims_to_add { tenant_id: tenant_id, tenant_role: user_attrs.get(custom:tenant_role, member), tenant_plan: get_plan_from_tenant(tenant_id) # 从订阅表查询套餐类型 } # 覆写 ID Token 的 claims event[response][claimsOverrideDetails] { claimsToAddOrOverride: claims_to_add, groupOverrideDetails: { groupsToOverride: [ftenant_{tenant_id}_{claims_to_add[tenant_role]}] } } return event说明claimsOverrideDetails里添加的 claim 会出现在 ID Token 和 Access Token 中API Gateway 的 Lambda 授权器可以直接读取。groupsToOverride可以动态生成租户级角色分组IAM 策略可以基于这个动态分组做资源授权。注意get_plan_from_tenant要做缓存这个 Trigger 每次刷新 Token 都会触发如果每次都查数据库高并发登录时会成为瓶颈。我一般用 ElastiCache Redis 做 5 分钟 TTL 缓存。3.2 数据面隔离的三种落地方式3.2.1 DynamoDB单表 分区键设计如果业务以键值读取为主DynamoDB 是最合适的 SaaS 数据面。核心设计原则用分区键承载租户 ID用排序键承载业务主体 ID。这样 AWS 会自动把不同租户的数据分布到不同分区查询天然隔离。分区键 tenant_id排序键 entity_id业务属性t_001user_xyz用户基础信息t_001order_1234订单记录t_002user_abc另一个租户的用户这套设计的陷阱在于单租户数据量过大时分区键会出现热分区。AWS 对单分区的吞吐有上限默认 3000 RCU / 1000 WCU 左右一个租户的数据量达到这个量级吞吐就会受限。解决办法是引入“大租户换键”策略当租户的数据量超过阈值比如 5GB将其拆分为多个逻辑分片分区键变为tenant_id#shard_0、tenant_id#shard_1。3.2.2 RDS PostgreSQL每租户 Schema 还是 Row Level Security关系型数据库的 SaaS 化比 NoSQL 复杂得多因为表结构、索引、外键都牵扯其中。两种主流方案是“每租户 Schema”和“共享 Schema RLS”。每租户 Schema 是把同一个建表脚本在每个租户的 Schema 下各执行一次。好处是数据物理隔离、备份恢复可按租户独立操作、SQL 层面完全隔离坏处是连接数管理困难——一个 PostgreSQL 实例能支撑的连接数是有限的几百个 Schema 就需要几百个连接。解法是用 PgBouncer 做连接池用set search_path在不同租户间切换。这套方案适合企业级 SaaS租户数量在几十到几百这个量级。共享 Schema RLS 是所有租户共用同一套表通过tenant_id列和一个数据库政策函数自动过滤行级可见性。适合租户数量上千的场景运维最简单但 DBA 对性能调优的难度会上升——因为所有租户的数据挤在同一张表里索引和统计信息互相干扰。-- 每租户 Schema 的连接路由示例 -- PgBouncer 按 database 路由每个租户一个逻辑数据库名 -- 应用侧连接串示例 -- postgresql://user:passpgbouncer.example.com:6432/saas_t_001 -- 在 PgBouncer 配置中添加租户映射 [databases] saas_t_001 hostpostgres-primary port5432 dbnamesaas_platform saas_t_002 hostpostgres-primary port5432 dbnamesaas_platform -- 应用层查询前设置 search_path 隔离租户 SET search_path TO tenant_t_001, public; SELECT * FROM orders WHERE order_id ord_123;这段配置的关键在于所有租户的 Schema 都在同一个 PostgreSQL 实例上但通过search_path精确路由到各自 Schema。这样物理资源共享、逻辑完全隔离。参数说明dbnamesaas_platform是物理数据库名saas_t_001是逻辑库名PgBouncer 做翻译映射。查询前必须执行SET search_path否则会默认访问publicSchema。实际生产中我建议在连接池初始化阶段用server_reset_query DISCARD ALL来清空上一个租户的会话状态避免会话串租户。3.3 租户元数据服务所有隔离方案的统一入口无论用哪种数据面方案都需要一个统一的租户元数据服务。这个服务维护一张核心表租户 ID、租户名、套餐类型、数据面类型pool/silo、数据面终端节点、状态active/suspended/deleted。所有下游服务在处理请求前先查这个表确认租户状态并获取数据面终端地址。# 租户元数据服务查询租户的数据面位置和状态 import boto3 import json dynamodb boto3.resource(dynamodb) TABLE_NAME saas_tenant_metadata def get_tenant_context(tenant_id): table dynamodb.Table(TABLE_NAME) # 强一致读取保证状态是最新的 resp table.get_item( Key{tenant_id: tenant_id}, ConsistentReadTrue ) item resp.get(Item) if not item: raise TenantNotFoundException(tenant_id) # 租户挂起/欠费时直接阻断避免数据面被无效请求打爆 if item[status] ! ACTIVE: raise TenantSuspendedException(tenant_id, item[status]) return { tenant_id: tenant_id, data_plane_type: item[data_plane_type], endpoint: item[endpoint], plan: item[plan], # 拿到数据面连接配置 config: json.loads(item[config_json]) }这个服务在架构上是“配置中心”和“断路器”的结合体——租户被标记为SUSPENDED后所有请求在到达业务逻辑之前就被拦截。生产环境里这个表用 DynamoDB 加 DAX 加速读取避免每次请求都打到 DynamoDB 本身。4. 计费与成本归属SaaS 架构里最容易做错的一层4.1 从“按用量计费”倒推架构设计很多 SaaS 团队把计费做成事后补丁先上线功能再想办法统计用量结果月底对账时发现资源用了多少说不清、每个租户的成本摊不明白。正确做法是在架构设计阶段就把计费数据的采集点埋好。AWS 上的成本归属有两个维度。第一个是基础设施维度——EC2、RDS、S3 这些资源本身产生了多少费用。第二个是业务维度——某个租户发了多少请求、存储了多少数据、消耗了多少计算量。前者用 Cost Explorer 的标签分组就能解决后者必须依赖应用层埋点。基础设施维度我一般这样做创建 AWS 成本分配标签tenant_id所有按租户隔离的资源独立 EC2、独立 RDS、独立 S3 桶打上对应标签池化共享的资源共享 ECS 集群、共享数据库打上shared_platform标签在 Cost Explorer 里按标签分组生成每日成本报表共享资源的成本按月摊到各租户——摊分公式不能乱拍脑袋要基于应用层埋点的实际用量# 应用层用量采集在共享数据面服务里埋点 import boto3 import time import json cloudwatch boto3.client(cloudwatch) def report_usage(tenant_id, resource_type, amount): # 将租户用量指标推送到 CloudWatch # 后续通过 GetMetricData 汇总并按量计费 cloudwatch.put_metric_data( NamespaceSaaS/Usage, MetricData[{ MetricName: f{resource_type}_usage, Dimensions: [ {Name: TenantId, Value: tenant_id}, {Name: ResourceType, Value: resource_type} ], Value: amount, Unit: Count }] ) # 示例文件上传服务在每次上传后记录用量 def upload_file(tenant_id, file_size_bytes): # ... 业务逻辑 ... report_usage(tenant_id, storage_bytes, file_size_bytes)这段代码说明了一个关键点用量指标要产线级埋点不能靠事后查日志。CloudWatch Metrics 保留 15 个月支持按维度聚合和告警。NamespaceSaaS/Usage是自定义命名空间避免污染 AWS 内置指标。put_metric_data每次调用可以批量上报多条生产环境建议在内存里做聚合每 30 秒批量同步一次减少 API 调用成本。4.2 按量计费的常见坑高峰时段、最小计费单位、退款按量计费最怕的是“用量尖峰导致费用暴涨”。用户可能在某天突然导入大量数据或者某个 API 被脚本循环调用账单直接失控。我的做法是在用量统计层加两个机制软限制和硬限制。软限制是设置用量阈值接近阈值时发告警通知租户管理员硬限制是直接阻断超额请求。这两个限制应该在 API Gateway 层实现而不是在业务代码里做判断——因为业务代码可能被绕过API Gateway 是唯一入口。对于 API 调用量的限制AWS 的 API Gateway 本身就支持按 API Key 做 Usage Plan# 创建 API Key 并关联 Usage Plan # 用 AWS CLI 完成租户配额管理 # 1. 创建 API Key每个租户一个 aws apigateway create-api-key \ --name tenant_t_001_key \ --enabled \ --value optional-custom-key-value # 2. 创建 Usage Plan定义限流和配额 aws apigateway create-usage-plan \ --name tier_basic_plan \ --description Basic tier: 1000 req/day, 5 rps \ --quota limit1000,periodDAY \ --throttle burstLimit10,rateLimit5 # 3. 将 API Key 关联到 Usage Plan aws apigateway create-usage-plan-key \ --usage-plan-id plan_id \ --key-type API_KEY \ --key-id api_key_id # 4. 将 API 阶段关联到 Usage Plan aws apigateway update-usage-plan \ --usage-plan-id plan_id \ --patch-operations opadd,path/apiStages,valueapi_id:stage_name参数说明--quota limit1000,periodDAY表示每天最多 1000 次请求超限后 API Gateway 直接返回429 Too Many Requests不会真正打到后端。--throttle burstLimit10,rateLimit5表示每秒最多 5 个稳定请求允许 10 个突发请求。这样即使某个租户的客户端有 bug 在疯狂重试平台侧不至于被拖垮。4.3 共享基础设施的成本摊分定一个租户“单价”模型共享资源ECS 集群、RDS 实例、S3 桶无法直接按标签归属到租户必须做摊分。我见过最荒唐的摊分方式是按租户数量均摊——大客户和小客户付一样钱小客户永远觉得亏。比较合理的做法是引入“租户加权用量”模型单租户成本 共享资源总成本 × (该租户加权用量 / 所有租户加权用量之和)加权用量由应用层埋点数据算出常用的权重因子包括API 调用次数、存储量、数据传输量、Lambda 执行时长。每个因子分配一个权重系数这个系数要根据实际成本结构定期调整。比如存储贵就调高存储权重计算贵就调高执行时长权重让摊分结果尽量贴近真实消耗。5. 控制面扩展与高级技巧多区域、离线迁移、灰度发布5.1 控制面的多区域部署策略SaaS 平台的“控制面”和“数据面”在部署上应该有完全不同的策略。控制面租户注册、配置下发、计费采集对延迟不敏感但对一致性和可用性要求高部署在单一主区域通过 RDS 跨区域只读副本实现灾备就够了。数据面则按租户归属部署在离客户最近的区域降低延迟。多区域数据面的关键设计是“区域选址表”。在控制面的租户元数据表中要增加region字段每个租户绑定一个主区域和一个灾备区域。请求路由时应用先查租户元数据拿到主区域再调用该区域的数据面服务。这里对延迟敏感路由表必须缓存推荐用 ElastiCache Redis 加 TTL 5 分钟缓存未命中才回源查 DynamoDB。# 租户请求路由先查缓存再查元数据失败时切换灾备区域 import boto3 import redis CACHE_PREFIX tenant_route: redis_client redis.Redis( hostsaas-route-cache.redis.cache.amazonaws.com, port6379, decode_responsesTrue ) def get_tenant_region(tenant_id): cache_key f{CACHE_PREFIX}{tenant_id} # 先查缓存 region redis_client.get(cache_key) if region: return region # 缓存未命中查 DynamoDB 租户元数据 dynamodb boto3.resource(dynamodb) table dynamodb.Table(saas_tenant_metadata) resp table.get_item(Key{tenant_id: tenant_id}) metadata resp.get(Item, {}) primary_region metadata.get(primary_region, ap-northeast-1) standby_region metadata.get(standby_region, ap-southeast-1) # 如果主区域被标记为不健康自动切到灾备 if check_region_health(primary_region) is False: region standby_region else: region primary_region # 写缓存并设置过期时间 redis_client.setex(cache_key, 300, region) return regioncheck_region_health这个函数要监控主区域数据面的可用性我一般用 Route 53 Health Check 加 CloudWatch Alarm 组合实现——每 30 秒发出一次健康检查请求如果连续 3 次失败则视为区域不健康触发自动切换。这里的缓存 5 分钟过期意味着最坏情况下从区域故障到路由全部切到灾备需要 5 分钟业务侧要有重试机制兜底。5.2 租户数据迁移从池化到隔离的平滑演进SaaS 平台最经典的演进路径是前期全部池化省钱后期大客户要求独立部署。你需要一套“租户数据迁移工具”把指定租户的数据从共享库搬到独立库过程中不停止业务。DynamoDB 的迁移方案用 AWS Glue 或 Data Pipeline 读取源表按tenant_id过滤写入目标表。但需要注意Glue 默认的 ETL 任务是全量扫描大表上跑全量扫描成本很高。更高效的做法是用 DynamoDB Streams 做持续复制——先做全量导出再追平增量数据。# DynamoDB 全量导出到 S3然后用 Athena 过滤后导入目标表 # 这套流程可以做成 Step Functions 状态机按租户粒度触发 # 1. 导出整个 DynamoDB 表到 S3AWS 原生支持无额外服务费 aws dynamodb export-table-to-point-in-time \ --table-arn arn:aws:dynamodb:ap-northeast-1:123456789012:table/saas_shared \ --s3-bucket saas-migration-bucket \ --s3-prefix exports/tenant_t_001/ \ --export-format DYNAMODB_JSON \ --s3-sse-algorithm AES256 # 2. 在 Athena 中查询并过滤目标租户数据 # 假设导出后的表结构为Item 列包含整个租户的 JSON 数据 SELECT item FROM saas_migration.exports WHERE item.tenant_id t_001 LIMIT 100;说明export-table-to-point-in-time是 DynamoDB 原生的导出功能不消耗读取容量单位对线上业务没有性能影响。导出格式DYNAMODB_JSON是专门的 JSON 结构和标准 JSON 不兼容Athena 建表时要用mapstring, string或直接查询原始文本。增量追平部分需要开启 DynamoDB Streams 的NEW_AND_OLD_IMAGES将增量数据写入 SQS 队列由迁移消费者同步到目标表。迁移完成后将租户元数据表中的数据面类型从pool改为silo下一个请求就会路由到新的独立数据面。5.3 灰度发布与租户级回滚SaaS 应用的发布策略不能是“全局一次发布”而应该按租户灰度。最实用的方案是基于租户 ID 哈希的灰度规则。把租户 ID 的散列值映射到 0-99灰度 10% 就是散列值 0-9 的租户先升级。# 租户级灰度路由按租户 ID 哈希值决定走哪个版本 import hashlib # 服务发现中维护两个版本的终端地址 VERSION_MAP { v1: service-v1.internal:8080, v2: service-v2.internal:8080 } def route_by_tenant(tenant_id, gray_percent10): 根据租户 ID 哈希值决定路由到哪个版本 gray_percent 控制灰度比例0 到 100 # MD5 哈希后取整数值分布比直接取模均匀 hash_val int(hashlib.md5(tenant_id.encode()).hexdigest()[:8], 16) bucket hash_val % 100 if bucket gray_percent: return VERSION_MAP[v2] # 灰度版本 return VERSION_MAP[v1] # 稳定版本灰度发布的关键参数是“灰度比例”和“回滚开关”。灰度比例可以放在 Systems Manager Parameter Store 里发布过程中动态调整不需要重新部署代码。回滚也简单——把灰度比例调回 0新版本就停止接收流量。这里的VERSION_MAP我用的是 ECS Service Discovery 的命名空间地址每次部署新版本会创建新的服务发现记录无需改动路由代码。最后说一个容易踩的坑灰度期间的数据兼容性。v2 版本如果改了数据库 Schema比如新增了字段v1 版本的查询可能会失败。所以灰度发布之前必须保证数据库变更向后兼容——只加字段不删字段或者用双写方案。SaaS 平台一旦租户数量上来不可能做到所有租户同时升级数据层的向前兼容是硬约束必须写进发布检查单。除了灰度另外一个容易被忽略但很重要的点是租户配置的“按版本生效”机制。灰度发布时新版本代码可能读取到旧的租户配置导致运行异常。我一般会把租户配置连同版本号一起存入元数据表请求进入后先取当前租户的配置版本号与当前服务版本比对不一致时走兼容逻辑或返回明确错误。这样即使灰度中途出现配置不匹配也能快速定位是配置问题还是代码问题不用整个回滚。本文还有配套的精品资源点击获取
返回列表