今天聊个让所有调过API的人都血压升高的话题——不稳定。
你代码写得没毛病,Key也活蹦乱跳,可接口就是时不时抽风。超时、限流、报错轮番上阵,尤其是业务量一上来,各种奇奇怪怪的问题全冒头。这些坑我全都踩过,而且还不止一次,今天直接给你说解法。
超时处理
超时分两种,你得分清楚。
第一种叫连接超时(connect timeout),意思是你的服务器死活连不上厂商的网关,通常是你网络环境的问题,或者厂商那边挂了。第二种叫读取超时(read timeout),意思是连接是建好了,但模型生成太慢,左等右等不回你,时间到了只能断掉。
大多数SDK默认超时就30秒。写封邮件够用了,但要是让模型写一篇1500字的行业分析报告,光生成就得40秒往上。30秒一到,代码直接给你抛个异常,一脸无辜。
解决办法简单粗暴,把读取超时拉到120秒甚至180秒。连接超时保持10秒就行,连不上就是连不上,不用等太久。
限流处理
429状态码,老朋友了。翻译成人话就是你发得太猛,厂商那边在喊“慢一点慢一点”。
最憨憨的办法就是硬等,但具体等多久有点讲究。如果只是临时超了一点,等个一两秒重发基本就过了。但如果你是用脚本在疯狂打压力测试,那得自己动手做限速。
代码里自己加个令牌桶或者漏桶算法,实在嫌麻烦就直接用现成的库,控制每秒最多发多少个请求。记住一个原则,别卡着厂商给的上限跑,留个10%到20%的余量。网络是有抖动的,卡着上限就等于精准撞墙。
还有个实战中非常好用的技巧叫多Key轮转。你手头准备三到五个Key,代码里维护一个队列,每次取一个发请求。这样一来,单个Key的请求频率自然就降到原来的三分之一甚至五分之一。厂商限流是按Key来算的,多个Key等于多了几条车道。我自己的平台就是这么干的,单个Key几乎从来没触发过限流。
重试策略
重试这事,最忌讳的就是无脑重试。
你先得搞清楚什么错值得重试。像429限流、408超时、还有5xx这种服务端错误,都可以重试。但如果是401说Key不对,或者400说你参数传错了,你重试一万遍也一样,先去改代码再说话。
重试的核心心法是指数退避。第一次失败等1秒,再失败等2秒,然后是4秒、8秒,一般到16秒就差不多了。别无限等下去,设个次数上限,比如最多重试3到5次。重试次数太多了,不光救不了你,还会把厂商的网关堵得更死,大家一起遭罪。
这里有个小细节,重试的时候要不要换Key?分情况看。如果是429限流,换个Key确实有用,相当于换了个身份重新排队。但如果是500这种服务端故障,换Key也没用,所有Key走的是一套后端,该炸还是炸。
并发优化
单线程一个一个地发,慢得跟散步一样。把并发拉起来,吞吐量能涨一大截。
Python里用ThreadPoolExecutor,开个五到十个线程。每个线程持有自己的Client实例,别多个线程共用一个连接对象,容易出各种灵异问题。每个线程发请求的时候,要么带自己的Key,要么从刚才说的Key池里取。
但并发这东西不是越高越好。开太高了,你自己的机器CPU和带宽先扛不住。另外厂商那边看你突然涌进去一大堆请求,第一反应就是把你按下来限流甚至封Key。我建议从并发数2开始慢慢往上调,一边调一边盯着429的出现频率,找到一个既不触发限流、吞吐量又满意的平衡点。
熔断保护
这是一个很有用的防御手段,稍微高级一点,但非常值得用。
举个例子,你调用的那个模型服务如果连续报错了5次,说明它大概率已经出问题了。这时候你继续拼命发请求也没意义,反而把你自己服务器的资源耗光了。
正确的做法是把这路服务直接掐断,接下来的一分钟内任何请求直接返回错误,连试都不去试。等一分钟到了,放一个请求进去探探路,通了就恢复,不通就继续熔断。
这个模式自己用状态表就能实现,记一下最近N次请求的成功率就行。嫌麻烦的话,Python有现成的circuitbreaker库,直接拿来用,不用自己造轮子。
监控是命根子
以上所有机制能跑通的前提是你得看到数据。
你把每个请求的耗时、状态码、重试次数都记到日志里,然后配个简单的看板或者告警。至少盯住这三个数:失败率、平均响应时间、限流触发的次数。
什么时候需要主动介入?比如某个模型的失败率从0.5%突然涨到5%,不用犹豫,熔断加切备用模型一起上。再比如平均响应时间从2秒涨到8秒,说明厂商那边负载高了,你这边要么降并发,要么临时切到备用模型顶上。
最后说句掏心窝的
不稳定的根源就两个:要么是厂商那边真的在抖,要么是你自己的策略不够硬。
厂商抖这事你控制不了,但你可以通过多模型备用来抵消。阿里云挂了切腾讯云,腾讯云抖了切火山引擎,别把所有鸡蛋放一个篮子里。你自己的策略硬不硬,就看上面说的超时、限流、重试、并发、熔断这五个点有没有落实。
把这些都做到了,不敢说百分之百稳,但至少能让你睡个安稳觉。半夜被报警吵醒的概率会低很多。