微服务依赖管理的陷阱与模式( 四 )


让我们考虑另一个PetPic的使用场景 , 但这次将重点放在控制(Control)组件上 。 该组件会执行一系列内容质量验证 。 开发团队最近基于机器学习(ML)将自动滥用检测程序集成到了控制组件中 , 这使得每张新图片在上传到服务后立即得到验证 。 当Happytails的新客户开始将大量不同动物的图片上传到PetPic时 , 问题开始出现了 , 因为PetPic设计为只提供狗和猫的图片 。 上传流激活了我们控制组件中的自动滥用检测 , 但新的ML例程无法跟上请求的数量 。
该组件运行在1000个线程池中 , 并将专用于滥用例程的线程数量限制为一半 , 即500个线程 。 如果大量长处理请求一起到达 , 这应该有助于防止线程饥饿 , 就像我们这里的例子一样 。 工程师没想到的是 , 一半的线程最后消耗了所有可用的内存和CPU , 导致这两个地区的客户在将图像上传到PetPic时开始遇到很高的延迟 。
我们如何减轻用户在这种情况下所经历的痛苦呢?如果我们将控制组件运维活动隔离到单个区域 , 就可以进一步限制这种滥用情况的影响范围 。 即使服务运行在云中 , 确保每个区域都有自己的专用控制实例可以保证只有Happytails中的客户会受到不良图像上传流的影响 。 请注意 , 无状态服务很容易被限制在故障域中 。 隔离数据库并不总是可行的 , 但你可以考虑从缓存中实现本地读取 , 以及偶尔的跨区域一致性作为一个很好的折衷方案 。 处理栈应该尽可能实现区域隔离 。
要点
将服务栈中的所有服务保持在同一位置 , 并限制在同一故障域中 , 可以防止广泛传播的全局中断 。 将无状态服务隔离到故障域通常比隔离有状态组件更容易 。 如果无法避免跨区域通信 , 请考虑优雅降级和最终一致性的策略 。
场景三:规划SLO
在这最后一个场景中 , 我们将查看PetPic的SLO , 并验证每个数据包提供的SLO情况 。 简而言之 , SLO是提供服务时要达成的目标 , 可以通过合同绑定在我们与客户的SLA中 。 让我们看一下图5中的表格:
微服务依赖管理的陷阱与模式
文章图片
图5:PetPic的SLO
此表显示了工程师眼中将为PetPic客户提供出色用户体验的SLO 。 在这里 , 我们还可以看到每个内部组件提供的SLO 。 请注意 , APISLO必须基于API后端(例如Control和Data)的SLO构建 。 如果需要更好的APISLO , 但我们又无法做到 , 我们需要考虑更改产品设计并与后端所有者合作以提供更高的性能和可用性 。 考虑到我们最新的PetPic架构 , 让我们看看API的SLO是否有意义 。
让我们从运维后端(我们将其称为“Ops”)开始 , 它是后端的一部分 , 用于收集PetPicAPI的健康指标 。 API服务仅调用Ops来提供与运维相关的请求、错误和处理时间的监控数据 。 所有对Ops的写入都是异步完成的 , 故障不会影响API服务质量 。 考虑到这些因素 , 我们在为PetPic设计外部SLO时可以忽略OpsSLO 。
微服务依赖管理的陷阱与模式
文章图片
图6:将读取SLO与数据库对齐
现在 , 让我们来看看从PetPic读取图片的用户旅程 。 内容质量只在新数据注入PetPic时才会进行验证 , 因此数据读取不会受到控制服务性能表现的影响 。 除了检索图像信息外 , API服务还需要处理请求 , 我们的基准测试表明这需要大约30毫秒 。 准备好发送图像后 , API需要构建一个响应 , 平均需要大约20毫秒 。 仅在API中 , 每个请求的处理时间就增加了50毫秒 。
如果我们能保证至少有一半的请求会命中本地缓存中的一个条目 , 那么承诺第50个百分位数和100毫秒的SLO是非常合理的 。 请注意 , 如果我们没有本地缓存?? , 请求延迟将至少为150毫秒 。 对于其他所有请求 , 图像需要从数据库中查询 。 数据库需要100到240毫秒才能回复 , 并且它可能不会与API服务共存 。 网络延迟平均为100毫秒 。 如果我们考虑这些数字的最坏情况 , 请求可能花费的最长时间是50毫秒(API处理)+10毫秒(考虑缓存未命中)+100毫秒(网络)+240毫秒(数据) , 总计400毫秒 。 如果我们查看图6左列中的SLO , 我们可以看到这些数字与API后端结构很好地对齐了 。