3.3 解耦的服务网格——让股、债、商、金各自独立运行
2018年双十一我参与过一个电商系统的全链路压测。压测过程中有一个场景让我至今难忘当模拟流量达到某个阈值时一个负责生成推荐列表的微服务响应时间突然从200毫秒飙升到了8秒。紧接着调用它的商品详情页服务开始大量超时超时重试又加剧了推荐服务的负载。几分钟之内这个故障沿着服务之间的调用链逐级传播从推荐服务到商品详情从商品详情到购物车从购物车到下单——最终整个交易链路全部堵塞。事后复盘故障的起点只有一个推荐服务但整个系统的链路追踪图上红色的错误节点像癌变一样从中心向四周扩散。这就是服务网格需要解决的经典问题不是防止单个服务出故障而是防止一个服务的故障通过同步调用链把整个集群都拖死。你的投资组合里股、债、商品、黄金这四类资产就像你集群里四个独立部署的微服务。你有没有想过它们之间当前的调用关系可能比你那台被压测打爆的推荐服务还要混乱服务发现资产应该在它们该响应的场景下才被调用在微服务架构里服务发现是一个基础组件。一个服务不需要硬编码其他服务的IP地址它只需要向服务注册中心查询“我要调用支付服务告诉我现在有哪些健康的实例在线。”注册中心返回一个可用实例的列表然后负载均衡器选择一个实例转发请求。如果某个实例下线了注册中心会从列表里把它摘掉调用方不需要做任何修改。你的四类核心资产——股票、债券、商品包括黄金、现金——每一类都有自己的“健康运行场景”。它们在各自擅长的宏观经济象限里响应良好在不擅长的象限里强行调用只会超时和报错。股票这个服务最擅长响应的是经济复苏和温和增长的环境。企业盈利上升消费者信心增强信贷扩张顺畅——这时候你调用股票这个服务它会给你返回一个漂亮的响应。但在高通胀加速、央行被迫暴力加息的场景下股票服务会严重超时。你在这个场景下反复重试——加仓、补仓、抄底——就相当于在对方已经挂掉的时候还拼命发请求结果只会把你的整个组合拖进超时队列。债券这个服务恰恰相反。它在经济衰退、央行降息、市场恐慌的时候响应最快。资金从风险资产撤出涌向避险资产国债价格上涨收益率下行。你在2020年3月或者2008年秋天调用债券服务它是全场最快返回200 OK的节点。但在高通胀、央行加息的环境下债券服务会直接返回5xx错误。2022年全球债市遭遇了半个世纪以来最惨烈的下跌就是因为通胀这个场景不是债券擅长处理的请求类型。商品和黄金这个服务更特殊。它专门响应通胀高企、货币贬值、地缘政治动荡的场景。黄金在大部分时间里是一个不太活跃的服务涨跌不明显甚至跑不过通胀。但当极端场景来临时——比如2020年全球央行集体放水、2022年俄乌冲突爆发——黄金突然成了整个集群里唯一返回200的节点。你的资产配置要做的事情不是预测下一个被调用的应该是哪个服务。你没有这个预测能力没有任何人有。你要做的事情是服务发现层的静态配置提前在你的配置文件里写清楚每一类资产在什么场景下是健康节点什么场景下可能会超时。然后当某些资产表现优异而另一些资产表现不佳时不要慌张不要把所有流量都切到当前表现最好的那个服务上去。这个集群之所以是集群就是因为它需要不同类型的节点来应对不同类型的请求。一个只在晴天能用的系统不是高可用系统。一个必须在牛市里才能赚钱的组合不是组合是赌注。断路器模式连续回撤超过阈值自动熔断服务网格里有一个至关重要的弹性设计模式断路器。它的工作原理和电路断路器完全一致。你给一个服务调用设置一个失败阈值——比如连续5次调用失败或者一分钟内错误率超过50%。一旦触发阈值断路器跳闸后续的调用请求不再发送到故障服务上而是直接返回一个fallback响应。同时断路器会每隔一段时间放一个探测请求过去检查故障服务是否已经恢复。如果恢复了断路器闭合流量重新接入如果还没恢复继续保持熔断状态。这个机制的唯一目的就是防止雪崩。你知道当一个服务已经开始超时的时候不断重试只会让它死得更快。更重要的是重试请求会占用调用方自己的线程池和连接池把调用方自己也拖死。断路器就是那个在所有人都失去理智的时候强制所有人停止重试的机制。现在请你想一想你是怎么对待一个正在下跌的资产的。一只股票开始下跌。你买入的时候它估值合理基本面健康行业前景光明。但市场不买账它跌了10%。你觉得市场错了这是机会你加了仓。它继续跌跌到20%。你开始焦虑但你告诉自己好公司越跌越有价值你又加了仓。跌到30%的时候你已经不敢看账户了。跌到40%的时候你割肉了。这个过程就是你不断向一个已经超时的服务重试请求的过程。每一次加仓都是一次重试。每一次重试都占用了你更多的资金——你的线程池和连接池正在被一个无响应的服务耗尽。等到你终于决定停止重试的时候你的现金已经打光了你的其他资产也被迫变卖来补仓。一个服务的故障通过你不断重试的操作成功地把你的整个组合都拖成了雪崩。断路器模式给你的解决方案很简单给每一个资产类别预设一个回撤熔断阈值。比如任何一个单一行业的配置如果从近期高点回撤超过25%同时你在这个行业上的仓位已经超过了预设比例2.5节说的10%单票上限和30%单行业上限就不再允许新增资金进入这个类别。它可以在你的组合里继续持有但你不能再往里面追加任何一块钱。这就是跳闸。你的理性在行情暴跌的时候可能不工作但你在冷静时写下的这条熔断规则可以替你做决定。熔断期不是永久的。你设定一个探测间隔——比如一个季度。到了下一个季度的架构评审日2.8节你重新评估这个资产的情况。如果基本面已经恶化直接把剩余仓位清掉释放全部资源。如果基本面没有问题估值已经进入深度价值区间你可以把仓位恢复到目标比例的50%但不能一次性全部恢复。这就是探测请求。慢慢地、有控制地放开流量而不是在情绪最激动的时候全量切换。边车代理每一笔交易都必须经过规则引擎的过滤在服务网格的架构里每个服务实例旁边都部署着一个边车代理。这个边车是一个轻量级的网络代理它不参与业务逻辑但它拦截所有进出该服务实例的网络流量。服务实例自己不需要知道服务发现、负载均衡、熔断、加密、监控这些能力的存在这些全部由边车在流量通过的瞬间透明地完成。你的每一次投资决策都是一次进出你财富系统的网络请求。问题是你的这些请求大多数时候是裸奔的——它们没有经过任何边车代理的过滤直接从你的情绪中枢发起到你的证券账户中间没有任何安全校验。你需要一个边车。它不是一个真实的软件而是你为每一类资产提前写好的一组“准入规则”。在你每一次买入、卖出、加仓、减仓之前这个边车必须拦截你的操作请求回答以下三个校验问题第一个问题这笔操作是否符合你在冷静时设定的资产配置比例你当前的权益类仓位是否已经超过了60%的目标比例如果是边车直接拒绝这笔买入请求不管你现在多想买。第二个问题这笔操作的触发条件是什么是你提前写在交易计划里的止盈止损条件还是你看到K线突然拉了一根大阳线之后的冲动如果触发条件不在你预设的规则清单里边车返回403 Forbidden不允许执行。第三个问题这笔操作发生之后它会不会导致你违反任何一条你已经写下来的铁律比如单票不超过10%单行业不超过30%不加杠杆不动用应急储备金。如果你即将违反其中任何一条边车直接熔断并且在你的投资日志里写一条告警记录。这三个校验逻辑需要你在冷静的、理性的、不处于任何市场情绪中的时候一条一条写下来。写在你每个季度的投资计划书里写在你每只股票的元数据记录里2.6节。然后在每一次你想要操作的时候强迫自己先打开这份规则清单一条一条对照。如果你发现自己在对照的过程中产生了“这次特殊情况可以不遵守”的念头恭喜你——你刚刚捕获了一次差点绕过边车的非法请求。边车的存在意义就是在这一刻拦住你。如果你觉得自己一个人执行这套边车规则太困难——大多数人确实执行不了包括我自己在早期也经常绕过自己定的规矩——那就给你的边车加一个物理级别的执行层。让你信任的配偶持有交易账户的二次验证密码。任何超过一定金额的操作必须经过两个人的确认。这不是不信任你自己而是你充分理解自己作为人类在情绪波动时的不可靠性然后主动给自己设计了一个更可靠的系统。服务网格的这三个组件——服务发现、断路器、边车代理——合在一起做的事情和你运维微服务集群时做的事情一模一样不消灭故障但限制故障的爆炸半径不预测流量但确保每一种流量都能被正确地路由到最合适的处理节点不阻止你发出请求但保证每一个请求都经过了预设规则的校验。在下一节我们会进入负载均衡的具体策略。当你的资产集群已经通过服务网格实现了解耦和熔断之后你还需要一个动态权重调整机制让资金在不同的市场环境里自动流向那些性价比更高的资产节点。这个机制用Nginx的负载均衡算法来翻译会比你想象中更简单。

相关新闻