故障之 CDN 使用问题

故障问题

最近公司的一个系统用上了 CDN 服务,用户反应快多了,那个系统主要是静态资源展示和一些可公开的数据查询服务。另一个项目组的系统A看了也要上,不过这个A的后台代码是放在我所在项目组M里,是同一个应用,都在同一个域名 main.com 下。网络组把 main.com 的 DNS 解析到 CDN 服务器上去之后,访问也是快了,但是用户反应数据乱了,而且有些服务没法用了,后台统计特定地区用户的访问也不准了,因为所有请求基本上都是从几个 IP 里过来的。

问题原因

稍微对 CDN 有所了解的人看完问题描述之后都会发现问题所在:由于只有一个域名 main.com ,且被解析到 CDN 服务器上,CDN 不仅分发静态资源,还变成代理服务器了,所有动态请求、数据都要经过 CDN 服务器。

很明显,这是错误使用 CDN 带来的问题。这带来的问题不仅是上面描述的问题,更大的业务安全问题:当所有业务数据都经过 CDN 服务器时,CDN 厂商就有了那些业务数据,这相当于把银行卡和密码交给一个中间人,要存钱、取钱的时候就让这个中间人去 ATM/银行 处理,再给自己反馈结果。有这么值得信赖的中间人吗??

正确使用 CDN

对于这个问题,我的建议是启用一个子域名,比如 img.main.com,所有静态资源都放在这个域名下,这个域名的 DNS 解析到 CDN 的服务器上,主域名 main.com 仍然解析为自己的服务器。这样,静态资源有 CDN 提供,动态资源仍然是自己的服务器直接提供。

  • 关于这种两个域名实现动静分离的实现,我之前见一个同事把 index 页面 (泛指决定页面 JS 执行时的域的页面)也放到了 img.main.com,这样,当 index 页面上 js 向 main.com 请求动态时,就面临跨域的问题,把 index 页面放回 main.com 就可以解决了。关于跨域问题,可参考 html5 文件拖拽上传及跨域处理白帽子讲web安全-2.客户端安全 摘记

  • 使用 CDN 带来的另一个问题是获取客户端真实 IP 的问题,直接使用 HttpServletRequest.getRemoteAddr() 是没法拿到真实 IP 的,这个问题在使用了反向代理或负载均衡服务器时也是存在的。一般都是由反向代理服务器把真实的 IP 放置在 HTTP 的请求头里透传到应用服务器,这个请求头一般命名为 X-Forwarded-For,如果中间有多个反向代理(比如我们碰到的是 CDN),那么非直接面向用户的反向代理服务器把上一个服务器的 IP 追加到 X-Forwarded-For头后面,再往后传,直到应用服务器那里。这里要注意的有个:这个请求头的精确名称(大小写敏感)和IP 拼接的顺序(是追加到后面还是插入到第一个)。由于不同的反向代理或负载均衡服务器对这个设置可能不同,所以这两个问题都是需要确认的,然后再在应用代码里编写获取真实 IP 的代码,这个代码应该封装在一个工具类里,方便修改(在第一家公司时,所有请求都是直接到 Resin 服务器的,代码里去客户端 IP 都是直接用 HttpServletRequest.getRemoteAddr() 。后来来了个项目经理,要求用 Nginx 做反向代理,直接处理静态资源和代理动态请求,也就引入了这个 IP 获取问题,所有获取 IP 的地方都要替换,现在集群什么的那么普遍,要预先规划好)。下面是一个简单的封装示例:

package net.coderbee.demo.util;

import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.StringUtils;

public class RequestUtil {
     private static final String IP_SEP = ", "; // 请求头里有多个 IP 地址时的分隔符

     public static String getRemoteAddress(HttpServletRequest request) {
          String ip = request.getHeader("X-Forwarded-For");
          if (StringUtils.isBlank(ip)) {
               ip = request.getRemoteAddr();
          }

          if (ip.contains(IP_SEP)) {
               String[] split = ip.split(IP_SEP);
               ip = split[0]; // 真实 IP 是放在最前面的
          }

          return ip;
     }
}
  • 关于使用 CDN 还要注意的是 CDN 的更新延迟问题。在上家公司时,有一个客户端安装包和升级文件都是用 CDN 来分发的,有一次发布版本后,发现客户端会提示升级,但升级后的还是旧版本的文件,还以为是程序的升级机制有问题,折腾半天,最后发是 CDN 更新延迟的问题。对于使用了 CDN 的服务,发布版本后,如果在公网上验证发现有问题时,首先要确认访问到的确实是发布后的资源。

欢迎关注我的微信公众号: coderbee笔记,可以更及时回复你的讨论。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据