首页 > 技术文章 > 微信内嵌浏览器sessionid丢失问题,nginx ip_hash将所有请求转发到一台机器

coolid 2016-09-08 17:17 原文

 

现象
微信中打开网页,图形验证码填写后,经常提示错误,即使填写正确也会提示错误,并且是间歇性出现。
系统前期,用户使用主要集中在pc浏览器中,一直没有出现这样的问题。近期有部分用户是在微信中访问的,才出现的这个问题。

抓包
由于只有在手机微信中出现这种情况,并且手机连的无线与pc不在一个局域网中,所以,只能在pc上用360wifi创建热点(无线),然后手机连接热点,再用wireshark抓360wifi网卡,查看数据。
一个页面中的,多次请求,带着sessionid发送的,但是回来分别返回不同的sessionid,cookie中的信息是set-cookie:jessionid=xxxxx,返回的sessionid跟发送的sessionid不同

分析
经过测试发现
1、sessionid丢失只在微信中打开网址时才会出现,尤其在安卓的微信中,丢失次数最多,iphone中几乎不出现。
2、在非微信中,比如手机浏览器中,不会出现sessionid丢失
3、在pc浏览器中,也不会出现sessionid丢失

推理论证
至此,怀疑是服务器没做session保持,经查证,服务器有2个tomcat,都放在腾讯云上,做了黏性session,黏性session是通过hash ip,然后取余做的,所以怀疑是黏性session不起作用。
为了验证这个猜测,通过不断request,发现request一会到server1,一会又到server2,而且没有规律可循。由于手机的ip是固定的,并且腾讯云坚持他们的黏性session没有问题,所以推测ip在中间传输过程被修改了。经过在服务器上抓包发现,腾讯云收到的ip的确是变化的。经过以上的验证加上网上search,原来凶手是微信内嵌浏览器对所有请求都做了代理,从而导致腾讯云收到的ip发生了变化。

解决
由于微信内嵌浏览器对所有请求都做了代理,导致出去的ip不固定,从而导致腾讯云服务器的黏性session(基于客户端ip做的黏性session)失效。所以,解决方案有两个,要么服务器端只用一台tomcat,要么tomcat做session共享,比如 session放在redis中。这样server1产生的sessionid,即使到了server2,也能从redis中找到。

 

 

nginx ip_hash将所有请求转发到一台机器 

 

问题描述:阿里云nginx ip_hash将所有请求转发到一台机器
排查:查看nginx的error.log, 发现大部分的client ip是来自于10.159.95网段

点击(此处)折叠或打开

  1. 2015/10/25 12:01:03 [warn] 21580#0: *12412231 an upstream response is buffered to a temporary file /home/work/nginx/proxy_temp/2/55/0000075552 while reading upstream, client: 10.159.95.***, server: ***.***.***, request: "POST *** HTTP/1.0", upstream: "http://***", host: "***.***.***"

怀疑nginx未获取到真实的用户ip,而获取到上游SLB集群的IP。

nginx官方网站中关于ip_hash的说明
Specifies that a group should use a load balancing method where requests are distributed between servers based on client IP addresses. The first three octets of the client IPv4 address, or the entire IPv6 address, are used as a hashing key. The method ensures that requests from the same client will always be passed to the same server except when this server is unavailable. In the latter case client requests will be passed to another server. Most probably, it will always be the same server as well.
飘红字段的意思是,对于IPv4,ip_hash会使用IP的前3部分作为哈希的Key,一个SLB集群对应的网段都是一致的,导致ip_hash之后的请求全部落到一台upstream机器。

解决方案:让nginx获取到用户的真实IP,使用real client ip做ip_hash
1. 重新编译nginx, 增加--with-http_realip_module模块
2. 在nginx.conf中http{}中,增加

点击(此处)折叠或打开

  1. real_ip_header X-Forwarded-For;
  2. set_real_ip_from 0.0.0.0/0;
  3. real_ip_recursive on;

3. 重启nginx,查看error.log检查client: 对应日志,已经变成了用户真实的外网出口IP
4. 检查upstream的机器流量,已趋于均衡

 

推荐阅读