今天迁移至阿里云后,出现了一个问题,有些站点记录的访问者ip全是阿里云的两个内网ip,在程序中是通过request.userhostaddress读取ip地址的,之前从没遇到过这个问题,很是奇怪。经过分析比较发现,出现这些问题的站点都是跑在使用了负载均衡的web服务器上(使用负载均衡,也是这次迁移在性能上的一处提升),这个问题应该与负载均衡有关。
今天迁移至阿里云后,出现了一个问题,有些站点记录的访问者ip全是阿里云的两个内网ip,而程序中是通过request.userhostaddress读取ip地址的,之前从没遇到过这个问题,很是奇怪。经过分析比较发现,出现这些问题的站点都是跑在使用了负载均衡的web服务器上(使用负载均衡,也是这次迁移在性能上的一处提升),这个问题应该与负载均衡有关。
于是,上阿里云网站的管理控制台查看了一下负载均衡的相应设置,发现了线索,见下图:
原来负载均衡器在向web服务器转发请求时,将真实ip存储在服务器变量x-forwarded-for中,而web服务器中执行的request.userhostaddress实际是从服务器变量remote_addr中获取ip地址的值,由于收到的请求是负载均衡器转发过来的,所以remote_addr中存储的是负载均衡器的ip。要解决这个问题,需要改为通过request.servervariables["http_x_forwarded_for"]读取。当知道这个原因时,条件反射地就想马上动手——将代码中的request.userhostaddress改为request.servervariables["http_x_forwarded_for"]。但是,准备动手时,有些犹豫了,要改的地方不少。。。开始是冲动占上风,这时懒惰开始向冲动发起进攻。
最终懒惰战胜了冲动,冷静下来思考有没有更好的解决方法。很快就想到了,这个场景正是http module可以用武的地方,只要在http module中把servervariables["remote_addr"]的值改为servervariables["http_x_forwarded_for"]的值,不用改一行代码,request.userhostaddress就能获取到正确的ip。的确是一个更好的解决方法,于是问题变成了怎么写这样的http module?
懒惰继续占着上风,去网上找找有没有现成的http module,还真有(x-forwarded-for http module for iis7),而且是鼎鼎大名的f5负载均衡器的生产厂商 f5 networks, inc. 的开发人员开发的,名叫f5xffhttpmodule,2009年发布的,基于isapi(非托管的)。原以为通过它就能解决问题,而残酷的现实是这个http module在我们的iis上怎么也加载不了,而且会引起整个站点无法正常访问。又继续找,网上多数提到的还是这个http module,没找到更好的。
找现成的http module的懒惰想法没能成行,但懒惰依然痴心不改,冒出了一个更加懒惰的想法——是不是可以不用另外安装专门的http module,用现有的url rewrite module来解决这个问题呢(web服务器已经安装有这个module)?借助url rewrite module修改servervariables["remote_addr"]的值。根据这个懒惰的想法竟然很快在网上找到一篇博文——如何讓在reverse proxy 之後的網站正常運行(url rewrite),根据这篇博文成功地以懒惰的方式解决了问题。
下面是具体的操作步骤:
1. 如果iis上没有安装url rewrite module,安装它(下载地址);
2. 在iis根节点或某个站点中打开url rewrite module;
3. 在右侧的actions中点击view server variables...
4. 点击add,添加名为remote_addr的服务器变量
5. back to rules,添加一条下图所示的规则
这条规则的作用就是将每个请求中servervariables["remote_addr"]的值替换为servervariables["http_x_forwarded_for"] 的值。
applicationhost.config中的对应配置如下:
<rewrite> <allowedservervariables> <add name="remote_addr" /> </allowedservervariables> <globalrules> <rule name="http_x_forwarded_for-to-remote_addr" enabled="true"> <match url=".*" /> <servervariables> <set name="remote_addr" value="{http_x_forwarded_for}" /> </servervariables> <action type="none" /> <conditions> <add input="{http_x_forwarded_for}" pattern="^$" negate="true" /> </conditions> </rule> </globalrules> </rewrite>
移花接木,借花献佛,这就是我们找到的解决这个问题的最简单的方法。
【注意事项】
添加该url重写规则会造成iis内核模式缓存不工作,详见微软的坑:url重写竟然会引起iis内核模式缓存不工作。
发表评论