故障问题
最近公司的一个系统用上了 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笔记,可以更及时回复你的讨论。