齐宣王使人吹竽,必三百人。南郭处士请为王吹竽,宣王说之,廪食以数百人。 宣王死,湣王立,好一一听之,处士逃。
《韩非子·内储说上》
背景
某日,突然收到报警,某发现实时异常监控趋势图上,静态资源 404 异常有明显的上升。 分析详细的异常信息发现是某个发布操作引起某几个系统引起的。
找到对应的开发,分析结论是部分配置文件未生效,导致拼接资源文件地址错误。 按照业务量计算,这个异常量大约是其中一、两台机器有问题。
可能发布系统或其他问题,重新发布一次仍没能修复这个异常。
我们需要快速定位到这只滥竽,尽快将问题修复。 但是实际上却没有这么简单,我们最终花费 了 7个多小时,最终是因为这个系统正好有 其他发布才顺便解决。
上图有两个造成异常的发布,分别是『CMS 发布』和『后台系统发布』。
橙色是 JavaScript 实时异常,绿色是静态资源 404 异常。
方案
事后我们坐下来讨论如何解决这个问题,避免在以后遇到类似问题时能快速有效的应对。
如上图,问题出在某个问题业务应用服务器节点配置文件未生效,拼接出来的 JavaScript 资源地址不正确导致 CDN 节点未命中而最终回源,源服务器也没有这个文件,最终发生 404 悲剧。
实际的业务系统中,会提供用户访问的节点信息,但是请求静态资源时无法获得这个信息, 是不是有办法在静态资源源服务器上获得来源页面所在的服务器节点信息呢?
- 方案一:我们首先想到 Cookie。但是应用服务器和静态资源服务器分别在不同的域名 下,即使在访问应用服务器时写入节点信息到 Cookie 中,这个信息也不会携带到静态 资源服务器。『失败』。
- 方案二:CDN 节点域提供外部写 Cookie 的接口,用户访问应用服务时,同时调用接口
在 CDN 节点所在域写入用户访问应用服务器节点信息。『放弃』,缺点:
- 成本高。
- 风险大。
- 方案三:然后想到每个应用系统重写静态资源地址,在地址中携带当前应用服务器节点
信息。『放弃』,缺点:
- 成本过高。
- 导致 CDN 命中率下降。
方案四:重写用户访问页面地址,附带应用服务器节点信息。成本过高,『放弃』。
从前端角度直接解决这个看似无望,换个思路。
方案五:在网络内部,使用内部代理。如
curl -I -x internal-host:port url
『可行』,缺点:- 需要在服务器所在内网执行,指定目标服务器内部 IP,因此只有系统管理员可以操作。
- 而系统管理员不清楚实际应用业务。
方案六:如果可以在外部指定服务器节点访问应用,是不是可行呢?
这个跟负载均衡规则有关,如果负载均衡服务器支持这个特性,或可一试。
跟相关同学沟通后,表示可以做。而且从外网直接访问,清楚业务的开发同学可以自行 排查。
『可行』。需要注意的问题:
- 逐个击破,定向攻击。
- 可以通过拒绝频繁的指定节点访问请求来防范。
- 可以通过白名单内部 IP 才可以使用。
- 逐个击破,定向攻击。
- 方案七:客户端脚本扫描所有外部资源,并发起请求来确认各个资源状态。『放弃』,
缺点:
- 增加静态资源服务器压力。
- 增加网络请求和流量。
- 方案八:客户端脚本监听静态资源异常事件。HTML5 现在提供了 Performance Timing API,可以用来监控网页及其引用的外部资源加载时间,将来提供类似的 API 可以监听 静态资源的 error 事件也未可知。『期待』
- 方案九:Web 服务器端输出时拦截器扫描。尤其目前前端往后端延伸,能做的事情更多 了。『可行』。
结论
综上所述,在成本、风险、自动化、扩展性等方面来讲,方案九是最优的。
延伸思考
图二所示,这次问题出在业务应用服务器集群的节点 1。
- 如果问题节点是静态资源 CDN 集群的节点 2 呢?
- 问题节点是节点 3?