一旦有多个人或多个项目共用一个代理账户,同样的问题就会出现。我们要如何阻止整个团队共用一个凭证。我们要如何区分哪个客户消耗了多少流量。我们要如何切断某个承包商的访问权限,同时不影响其他人。在这个行业里,这些问题通常都会用”sub-users”(子用户)这个词来回答,而这个词的含义会因销售者不同而不同。
下面说明子用户到底是什么,住宅产品提供和不提供哪些功能,以及无论如何都要解决的三个根本问题的方法。
这个市场中”子用户”的含义
如果某个供应商提供子用户功能,那它就是在父账户下签发的第二套代理凭证。通常它可以有自己的带宽上限,有时还有自己的定向限制,并且可以在不影响父账户的情况下被撤销。这是一种附加在流量本身之上的访问控制功能。
团队想要子用户功能通常出于三个不同的原因,值得把它们区分开来,因为它们的解决方案各不相同。
隔离。 不同的人或项目不应共用一个凭证,这样一旦发生泄露或人员离职,影响范围也是有限的。
归因。 需要有人知道哪个客户、团队或项目使用了多少带宽,通常是出于计费或利润核算的目的。
限制。 一个项目不应该能够(无论是意外还是因失控任务)耗尽整个套餐的用量。
住宅产品提供的功能
直接说明:Shifter 的住宅代理并没有按子用户区分的代理凭证。认证方式是单一的用户名和密码,定向和会话选项都通过用户名表达,详见网关与认证文档。没有可以生成第二套具有自己带宽上限的代理凭证的功能。
真正存在的功能分为两种机制,两者结合起来涵盖了上述三个问题。
Team Workspaces(团队工作区) 负责管理账户的访问权限。你可以通过邮箱邀请他人,并分配角色:Viewer(仅可查看成员、流量和账单等信息)、Billing(除上述权限外还可充值和购买)、以及 Admin(拥有完整的成员和团队管理权限)。角色是按工作区划定范围的,而非全局性的,移除成员会立即生效并终止其现有会话,一个人也可以属于多个工作区,并通过一次登录在它们之间切换。完整的行为说明见Team Workspaces 发布公告。
会话标识符(Session identifiers) 负责流量的分段管理。每个请求都可以携带一个你自行设定的会话 ID,这正是无需独立凭证也能实现按客户、按项目归因的原因。
需要牢记的区别是:工作区控制的是谁可以管理账户,会话标识符组织的是流量在做什么。而在其他地方存在的子用户功能,往往把这两者混为一谈。
在没有子用户的情况下解决隔离问题
由于单一凭证服务于所有用途,隔离工作就转移到了你如何存储和暴露这个凭证上。
将凭证保存在密钥管理工具中,而不是写在代码、配置文件或共享文档里,并让应用程序在运行时从环境变量中读取。人们完成工作并不需要这个代理凭证,系统才需要它。需要测试的工程师可以被授予访问使用该凭证的服务的权限,而不是直接获得凭证值本身。
在确实需要完全独立的影响范围的场景下,拥有各自套餐的独立工作区可以给你真正独立的凭证,因为套餐不会跨工作区共享。这比子用户”重”一些,而且意味着独立计费,所以它适用于客户或业务部门本就应该拥有自己账户的情况,而不是你只是想要整洁一点的情况。
然后,要在需要之前就规划好轮换机制。由于凭证是共享的,轮换它会同时影响所有使用方,所以要把它当作一次部署来对待:先更新密钥存储,再逐一更新所有客户端,最后再废止旧值。跳过这个顺序,是团队发现凭证到底散落在多少地方的最常见方式,因为每一处都会同时开始返回407 错误。
对于人员而非系统而言,离职处理通过工作区角色进行:移除该成员,其面板访问权限会立即终止,包括任何正在进行的会话。这涵盖了承包商的场景,而这通常正是人们真正想问的问题。
在没有子用户的情况下解决归因问题
这正是会话标识符发挥作用的地方,值得从一开始就有意识地组织它们。
将租户信息编码进会话 ID 中,这样每个请求都可以追溯到它是为谁发出的。ID 可以是你自行选择的任意字符串,因此采用一套结构化的命名约定并不会带来额外成本,还能让归因变成解析你自己日志的事情:
def proxy_for(client, job, country, ttl=600):
sid = f"{client}-{job}" # e.g. acme-prices-0827
user = f"customer-USERNAME-country-{country}-sid-{sid}-ttl-{ttl}"
url = f"http://{user}:PASSWORD@p.shifter.io:443"
return {"http": url, "https": url}
然后按客户记录每个请求消耗的字节数,因为供应商的账单只会给出账户的总量,无法告诉你具体的分配情况。按租户汇总响应大小可以得到一个用来计费或计算利润的用量数字,并定期将你的总量与账单对账可以保证数据的准确性,因为重试、失败请求和连接开销都会消耗带宽,而简单统计响应体大小的方法会遗漏这些。每个租户需要关注的指标是每条记录的字节数,这在代理关键指标一文中有介绍,正是这个数字把归因变成了一个成本问题。
会话在这里也发挥着它原本的作用:为每个客户设置不同的标识符可以防止两个客户的流程在同一个会话上发生冲突,一旦同时运行多个任务,这一点就变得很重要。相关语义说明见粘性代理与轮换代理对比。
在没有子用户的情况下解决限制问题
供应商端并没有按租户设置的上限,所以这个上限必须存在于你自己的调度系统中,而这本来也是它该存在的地方:你的系统知道一个客户有权使用多少额度,代理并不知道。
为每个租户在计费周期内设定带宽预算,并根据你已经记录的用量来强制执行,当额度耗尽时停止或将该租户的任务排队,而不是让它继续消耗共享额度。要在接近上限之前就发出警报,而不是等到达到上限时才发出。同时配合按租户分配的并发份额,以防一个大型的通宵批处理任务拖垒四个小型的时间敏感任务,以及按租户设置重试预算,以防某个客户的目标故障淹没共享池,这正是重试与退避和限速两篇文章中所讨论的遏制论点。
这一切最自然的归宿是已经能看到每个请求的那个组件,这也是为什么代理管理器才是放置租户预算、并发份额和归因逻辑的正确位置,而不是把它们分散在各个任务里。
在单一账户和多个工作区之间做选择
这个决定通常比看起来简单,关键在于谁拥有商业关系。
当你在提供一项服务、而供应商是你的供应链的一部分时,使用带有逻辑租户的单一账户:你购买带宽,客户从不会看到供应商,分段管理只是你自己代码中的一种命名约定。这是常见的代理机构模式。
当客户应该拥有自己的套餐和账单时,使用每个客户一个工作区的方式,并将你的团队添加为 Admin。计费是真正独立的,流量不会混杂,因为套餐不会跨工作区共享,而且如果合作结束,客户可以干净地接管账户。
从第一天起就围绕租户标识符进行设计,才能让在这两种模式之间切换只是一次配置变更,而不是一次重建。
结论
子用户是三个独立需求的一种实现方式,而且并非唯一的实现方式。在住宅产品中,只有一个代理凭证,所以隔离要靠密钥管理来实现,而在必须做到绝对隔离的地方,则要靠拥有各自套餐的独立工作区;人员的访问控制来自可以立即撤销的工作区角色;归因来自结构化的会话标识符,加上你自己按租户统计的字节数,因为账单只是一个总数;而限制则来自你调度系统中的预算,因为只有那里才知道每个租户有权使用多少额度。如果你从一开始就围绕租户标识符进行构建,就能同时得到这三者,并且保留了以后将某个客户迁移到他们自己的工作区、而不必改动采集代码的选项。
其底层是住宅代理:一个网关,国家、城市、会话和 TTL 都可以在每个请求中单独表达,这正是让按租户分段成为一种命名约定而非产品限制的原因,再加上按 GB 计费,使你内部归因统计的用量单位与账单计费单位保持一致。