服务共享准入

一个客户端限制自己同时发一个请求,两个客户端一起使用时,服务可能收到两个。一个模型进程,也可能同时处理多项请求。进程数量看起来很少,不代表请求并发已经受控。

并发上限首先要说清作用范围:每个调用者、每个服务实例,还是所有实例共享的后端。之后才讨论哪一层负责执行。

三层都有约束,但约束对象不同

服务准入控制多少任务进入处理;模型运行时决定任务怎样批处理、排队或执行;操作系统限制进程、CPU和内存等资源。它们相互配合,却不提供相同保证。

例如运行时批处理可以把多个请求放到同一轮执行,应用仍需知道有多少任务已被接受;系统任务数限制可能包含线程,却不理解业务请求是否完整。不能用一个系统参数代替用户任务的准入合同。

客户端限流也很有用,它能控制自己的行为,但无法自然代表所有调用者的总量。

让两个线程竞争同一份名额

下面是一个同进程共享的最小门控对象。检查和登记放在同一把锁里,避免两个线程分别看到空位后同时进入。

from threading import Lock

class Gate:
 def __init__(self,limit):self.limit=limit;self.active=0;self.lock=Lock()
 def enter(self):
  with self.lock:
   if self.active>=self.limit:return False
   self.active+=1;return True
 def leave(self):
  with self.lock:
   if self.active<=0:raise ValueError('unbalanced release')
   self.active-=1

Python threading官方文档说明锁对象的获取、释放与上下文管理行为。示例依赖显式锁保护组合操作,不把解释器运行方式当作业务计数不会竞争的证明。

本次实际创建两个线程,用带超时的屏障让它们先同时尝试进入,再等双方都完成尝试后才释放获准名额。结果恰有一个进入,最后共享计数回到零。所有自建线程已经结束。

同一脚本还创建两个独立门控对象,各自上限为一,二者都可进入,总量为二。这是客户端各自限制不能代表服务总限制的最小示例。

实验没有模型、网络服务或真实API,也没有测量延迟与吞吐量。它只验证同一进程内共享对象的有限竞争条件。

并发控制三层职责

共享到哪个范围,要与部署一致

如果服务有多个工作进程,每个进程各自创建一个门控对象,总量仍可能超过单个对象的上限。多服务实例也一样。需要依据实际部署选择统一的请求入口或其他协调方式,并验证失败恢复。

本篇没有实现跨进程共享或分布式协调,因此不能声称已经得到全局上限。把同一段代码复制到多个实例,并不会自动共享其中的计数。

即使只有一个控制端,重启后也要处理仍在运行的工作。计数归零可能只是丢失状态,不代表旧任务已经结束。

名额对应哪个阶段,也要定义

“并发一项”可以指已经接受的一项、正在推理的一项,或包括排队在内的一项。不同定义会影响资源与用户等待,不能在指标里混用。

如果只限制执行阶段,还需要单独限制队列长度和等待时间;如果在接受时就占名额,则要处理启动失败、取消和超时后的归还。用户关闭页面,不意味着工作已经停止。

示例的 leave 只检查计数不为零,不能识别名额归属。生产实现应把令牌与任务尝试绑定,防止错误任务归还了别人的名额。示例没有冒充完整任务管理器。

运行时上限也要看真实语义

某个后端配置可能限制批量大小、执行线程或同时序列数,它未必等同于业务请求数量。应该核对所用运行时的具体版本和接口,不能根据参数名称猜测。

本文没有选择具体模型后端,因此不提供未经核对的运行时配置,也不声称系统限制能保证每个请求获得固定资源。目标设备上的资源与任务行为仍需有限实测。

给用户的行为要一致

达到上限时,是立即拒绝、进入有界队列,还是允许选择稍后重试,都应明确。不同入口不应绕过同一项服务承诺,否则用户会遇到“从这个页面能进,从另一个入口却超限”的状态。

把每项约束的对象写清楚,再让真实部署中的请求流经过对应门控。并发控制才是可验证的服务行为,而不是几个看起来相似的数字。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐