之前遇到一个问题,在深圳的阿里云服务器向匈牙利和捷克的供应商接口请求数据,一次完整交互需要大概 10s,其中包含 4 次 POST 来回,以及写深圳数据库、深圳 OSS 存储等操作。而需要的指标是 90% 的完整交互在 3 秒内完成,差距非常大。过程如下:
sequenceDiagram
ECS-SZ ->>服务商: "POST-1, 300ms"
服务商 ->> ECS-SZ: "RESP-1, 300ms"
ECS-SZ ->>服务商: "POST-2, 300ms"
服务商 ->> ECS-SZ: "RESP-2, 300ms"
ECS-SZ ->>服务商: "POST-3, 300ms"
服务商 ->> ECS-SZ: "RESP-3, 300ms"
ECS-SZ ->>服务商: "POST-4, 300ms"
服务商 ->> ECS-SZ: "RESP-4, 300ms"
ECS-SZ ->>OSS-SZ: "store data"
ECS-SZ ->>RDS-SZ: "commit data"
不算服务器处理时间,这在路上就花了 2.4s,于是想到把发请求的服务器放到服务商附近,比如德国,使得来回传数据的时间压缩。
sequenceDiagram
ECS-SZ ->>ECS-DE: "POST-0, 300ms"
ECS-DE ->> ECS-SZ: "RESP-0, 300ms"
ECS-DE ->>服务商: "POST-1, 30ms"
服务商 ->> ECS-DE: "RESP-1, 30ms"
ECS-DE ->>服务商: "POST-2, 30ms"
服务商 ->> ECS-DE: "RESP-2, 30ms"
ECS-DE ->>服务商: "POST-3, 30ms"
服务商 ->> ECS-DE: "RESP-3, 30ms"
ECS-DE ->>服务商: "POST-4, 30ms"
服务商 ->> ECS-DE: "RESP-4, 30ms"
ECS-DE ->>OSS-SZ: "store data"
ECS-DE ->>RDS-SZ: "commit data"
ECS-DE ->> ECS-SZ: "DONE, 300ms"
ECS-SZ ->> ECS-DE: "ACK, 300ms"
这样把路上的时间减少了大概 1-2s,由于略有成效,但发现最后 RDS 和 OSS 的写入非常差劲,因为这两步又成了跨国调用,导致时间更长了,再次优化,OSS 就地写在当地,等异步用资源的时候慢一点点倒无所谓。
sequenceDiagram
ECS-SZ ->>ECS-DE: "POST-0, 300ms"
ECS-DE ->> ECS-SZ: "RESP-0, 300ms"
ECS-DE ->>服务商: "POST-1, 30ms"
服务商 ->> ECS-DE: "RESP-1, 30ms"
ECS-DE ->>服务商: "POST-2, 30ms"
服务商 ->> ECS-DE: "RESP-2, 30ms"
ECS-DE ->>服务商: "POST-3, 30ms"
服务商 ->> ECS-DE: "RESP-3, 30ms"
ECS-DE ->>服务商: "POST-4, 30ms"
服务商 ->> ECS-DE: "RESP-4, 30ms"
ECS-DE ->>OSS-DE: "store data"
ECS-DE ->> ECS-SZ: "DONE, 300ms"
ECS-SZ ->> ECS-DE: "ACK, 300ms"
ECS-SZ ->>RDS-SZ: "commit data"
这样终于消停,整体下来把时间压缩到了 5s 左右。
结果没过几天,通信又出了问题,交互时间突然大幅延长到了 8-10s,整个链路检查下来,发现是 ECS-DE 和 ECS-SZ 之间的公网特别拥堵,阿里云对自身服务器之间的公网线路完全不优化,等着卖「全球加速」服务呢。不得已,重操旧业,用 traceroute
的方式确定物理路由节点,自己规划路线,从 163 光缆切到 CN2 上。
原路由的地理跳点真是要命:
graph LR
深圳 --> 广州
广州 --> San_Jose
San_Jose --> New_York
New_York --> 荷兰
荷兰 --> 法国
法国 --> 德国
选了两家跳板服务商:
- akkocloud,阿里云直连质量很棒,从广州出境走 CN2 直达法兰克福
- ucloud,深圳连香港,香港连德国
之前自己在 ucloud 用了很长时间,虽然没啥亮眼服务,而且时不时就优化线路发通知说「预计会有秒级网络抖动」,但好在质量还行,没死过机(在准备切掉阿里云德国的当天,那台服务器似乎有所察觉,在空载情况下宕机,头一次收到阿里云宕机警告...)。看在比起私人的 akkocloud 更不容易跑路的份上,就用了起来。
现在的路由变成:
sequenceDiagram
ECS-SZ ->>UC-HK: "POST-0, 30ms"
UC-HK ->> UC-DE: "POST-0, 200ms"
UC-DE ->> UC-HK: "RESP-0, 200ms"
UC-HK ->> ECS-SZ: "RESP-0, 30ms"
UC-DE ->>服务商: "POST-1, 30ms"
服务商 ->> UC-DE: "RESP-1, 30ms"
UC-DE ->>服务商: "POST-2, 30ms"
服务商 ->> UC-DE: "RESP-2, 30ms"
UC-DE ->>服务商: "POST-3, 30ms"
服务商 ->> UC-DE: "RESP-3, 30ms"
UC-DE ->>服务商: "POST-4, 30ms"
服务商 ->> UC-DE: "RESP-4, 30ms"
UC-DE ->>OSS-DE: "store data"
UC-DE ->> UC-HK: "DONE, 200ms"
UC-HK ->> ECS-SZ: "DONE, 30ms"
ECS-SZ ->> UC-HK: "ACK, 30ms"
UC-HK ->> UC-DE: "ACK, 200ms"
ECS-SZ ->>RDS-SZ: "commit data"
尝试过 ECS-SZ 直连 UC-DE,还是绕地球走法,放弃。虽然这样看起来流程冗长,但由于 UC-HK 和 UC-DE 之间的线路质量非常好,几乎没有丢包,因此重传的几率大大降低,节省了非常多的时间。
国内 163 线路 Ping 和 traceroute 延迟 250-300ms,但丢包率接近一半,因此整个交互远远不止延迟时间累加。而不丢包的国外 ISP 延迟稳定且不丢包——特别是长距离通信不丢包,对整个传输质量的提高帮助特别大。
下一步就是架一个负载均衡,把 akkocloud 直连的线路也加上,毕竟少一次中转就少一分出错的机会。