nginx根据客户IP和unionid做分流

avatar 2020年3月30日18:07:20 评论 5,785 次浏览

前言

目前的需求是,根据用户的IP,让他跳转到不同的后端服务上。在后端的服务中有线上的也有新开发的内容,为了减少影响,所以需要使用灰度发布,在尽量减少线上用户的情况下,做一下灰度发布。这里我测试使用客户端的IP,也可以选择用户的ID等等。使用客户端IP做分流,也可以用到安全里,进行限制,比如我指定部分IP能够访问我的服务,其他的IP都返回一个304状态。

nginx安装

这里我就不多说了,前面已经说过nginx的安装了,可以参考:https://www.wulaoer.org/?p=295

配置nginx(用户ip)

这里使用的是map去匹配一些规则,map是有 ngx_http_map_module 模块提供的,默认nginx都会有安装,这里也就不说了,注意map的规则必须要放到http里,不能放到server中。

[root@www.wulaoer.org nginx]# vim nginx.conf

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
	worker_connections  1024;
}


http {
	include       /etc/nginx/mime.types;
	default_type  application/octet-stream;

	log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
					  '$status $body_bytes_sent "$http_referer" '
					  '"$http_user_agent" "$http_x_forwarded_for"';

	access_log  /var/log/nginx/access.log  main;

	sendfile        on;
	#tcp_nopush     on;

	keepalive_timeout  65;

	#gzip  on;

	include /etc/nginx/conf.d/*.conf;

	#map $http_wx_unionid $request_from_intranet {
	map $remote_addr $request_ip {    #这里使用map做的规则
		default "true";
		10.211.55.142 "false";
	}

}

在http中定义的map规则是除了IP(10.211.55.142)访问nginx会生成变量$request_ip等于false以外,其他的都会把变量$request_ip等于true。我们在nginx的server中在根据变量$request_ip进行用户返回结果。

[root@www.wulaoer.org nginx]# cat conf.d/default.conf 
server {
	listen       80;
	server_name  localhost;

	#charset koi8-r;
	access_log  /var/log/nginx/host.access.log  main;

	location / {
		root   /usr/share/nginx/html;
		index  index.html index.htm;
		if ($request_ip = "true" ) {    #map的规则需要在这里进行判断
			rewrite ^/(.*)$ http://www.baidu.com;
		}
		if ($request_ip = "false" ) {
			rewrite ^/(.*)$ http://www.360.cn;
		}

	}

	#error_page  404              /404.html;

	# redirect server error pages to the static page /50x.html
	#
	error_page   500 502 503 504  /50x.html;
	location = /50x.html {
		root   /usr/share/nginx/html;
	}
}

这里注意,我们的map定义的规则,需要进行一个判断,这里是在location中进行判断,如果变量$request_ip等于true会返回百度的网址信息,如果变量$request_ip等于false会返回360的信息,定义好后,我们reload一下nginx。

[root@www.wulaoer.org nginx]# /usr/sbin/nginx -s reload

至此,nginx的IP分流已经设置好了。

验证(用户ip)

我分别在两台虚拟机进行curl验证,这里注意,使用curl不会自动做301跳转,需要加一个参数"-L",如果不加"-L"不会跳转判断下的url上。

[root@wulaoer ~]# ifconfig -a|grep -o -e 'inet [0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]\{1,3\}'|grep -v "127.0.0"|awk '{print $2}'
10.211.55.142
[root@wulaoer ~]# curl -L --head http://10.211.55.143
HTTP/1.1 302 Moved Temporarily
Server: nginx/1.16.1
Date: Tue, 24 Mar 2020 02:17:34 GMT
Content-Type: text/html
Content-Length: 145
Connection: keep-alive
Location: http://www.360.cn

HTTP/1.1 301 Moved Permanently
Server: nginx/1.2.9
Date: Tue, 24 Mar 2020 02:18:02 GMT
Content-Type: text/html
Content-Length: 178
Connection: close
Location: https://www.360.cn

HTTP/1.1 200 OK
Server: nginx/1.2.9
Date: Tue, 24 Mar 2020 02:18:05 GMT
Content-Type: text/html
Content-Length: 74108
Connection: close
Last-Modified: Fri, 20 Mar 2020 12:35:15 GMT
ETag: "5e74b883-1217c"
Accept-Ranges: bytes

wulaoer这台机器的ip是map规则中返回变量$request_ip等于false,所以应该跳转到360。下看另外一个没有加入到map规则的IP:

[root@wolf ~]# ifconfig -a|grep -o -e 'inet [0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]\{1,3\}'|grep -v "127.0.0"|awk '{print $2}'
10.211.55.141
[root@wolf ~]# curl -L --head http://10.211.55.143
HTTP/1.1 302 Moved Temporarily
Server: nginx/1.16.1
Date: Tue, 24 Mar 2020 02:17:45 GMT
Content-Type: text/html
Content-Length: 145
Connection: keep-alive
Location: http://www.baidu.com

HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
Connection: keep-alive
Content-Length: 277
Content-Type: text/html
Date: Tue, 24 Mar 2020 02:18:06 GMT
Etag: "575e1f60-115"
Last-Modified: Mon, 13 Jun 2016 02:50:08 GMT
Pragma: no-cache
Server: bfe/1.0.8.18

wolf这台机器的ip没有加入到map规则中,所以变量$request_ip等于true,返回用户百度的信息。

配置nginx(使用用户http_wx_unionid做分流)

上面使用的用户IP做的分流,这里尝试使用用户的http_wx_unionid做分流,和上面的思路是一样的,这里配置一下

[root@www.wulaoer.org nginx]# cat nginx.conf

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
	worker_connections  1024;
}


http {
	include       /etc/nginx/mime.types;
	default_type  application/octet-stream;

	log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
					  '$status $body_bytes_sent "$http_referer" '
					  '"$http_user_agent" "$http_x_forwarded_for"';

	access_log  /var/log/nginx/access.log  main;

	sendfile        on;
	#tcp_nopush     on;

	keepalive_timeout  65;

	#gzip  on;

	include /etc/nginx/conf.d/*.conf;
	
	#map $http_wx_unionid $request_from_intranet {
	map $http_wx_unionid $request_ip {
		default "false";
		9999999 "true";
		8888888 "true";
	}

}

下面在server中对http_wx_unionid进行一个判断,判断的是变量$http_wx_unionid后面的$request_ip,这里的$request_ip必须要和server中判断的变量一样。$request_ip可以随意修改,两边一致即可。

	[root@www.wulaoer.org nginx]# cat conf.d/default.conf 
	server {
		listen       80;
		server_name  localhost;

		#charset koi8-r;
		access_log  /var/log/nginx/host.access.log  main;
		underscores_in_headers on;
		location / {
			#root   /usr/share/nginx/html;
			#index  index.html index.htm;
			if ($request_ip = "true" ) {    #等于map中的$request_ip进行判断
				#rewrite ^/(.*)$ http://www.baidu.com;
				proxy_pass http://www.baidu.com;
			}
			if ($request_ip = "false" ) {
				proxy_pass http://www.google.cn;
			}

		}

		#error_page  404              /404.html;

		# redirect server error pages to the static page /50x.html
		#
		error_page   500 502 503 504  /50x.html;
		location = /50x.html {
			root   /usr/share/nginx/html;
		}
	}

这里判断使用的不太一样了,使用IP用的rewrite,这里使用的是proxy_pass,如果使用rewrite会跳转不过去。

验证(http_wx_unionid)

我们在用户访问的时候加个参数http_wx_unionid,这样nginx才会循环map中的http_wx_unionid进行一个判断,然后根据判断进行返回。

[root@www.wulaoer.org nginx]# curl -L -v -H  'wx_unionid:8888888' http://10.211.55.143
* About to connect() to 10.211.55.143 port 80 (#0)
*   Trying 10.211.55.143...
* Connected to 10.211.55.143 (10.211.55.143) port 80 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 10.211.55.143
> Accept: */*
> wx_unionid:8888888
> 
< HTTP/1.1 200 OK
< Server: nginx/1.16.1
< Date: Tue, 24 Mar 2020 03:32:28 GMT
< Content-Type: text/html
< Content-Length: 2381
< Connection: keep-alive
< Accept-Ranges: bytes
< Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
< Etag: "588604c8-94d"
< Last-Modified: Mon, 23 Jan 2017 13:27:36 GMT
< Pragma: no-cache
< Set-Cookie: BDORZ=27315; max-age=86400; domain=.baidu.com; path=/
< 
<!DOCTYPE html>
<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=http://s1.bdstatic.com/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=百度一下 class="bg s_btn"></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>新闻</a> <a href=http://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>地图</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>视频</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>贴吧</a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&amp;tpl=mn&amp;u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>登录</a> </noscript> <script>document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" class="lb">登录</a>');</script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">更多产品</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>关于百度</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>&copy;2017&nbsp;Baidu&nbsp;<a href=http://www.baidu.com/duty/>使用百度前必读</a>&nbsp; <a href=http://jianyi.baidu.com/ class=cp-feedback>意见反馈</a>&nbsp;京ICP证030173号&nbsp; <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>
* Connection #0 to host 10.211.55.143 left intact
[root@www.wulaoer.org nginx]# curl -L -v -H  'wx_unionid:1111' http://10.211.55.143
* About to connect() to 10.211.55.143 port 80 (#0)
*   Trying 10.211.55.143...
* Connected to 10.211.55.143 (10.211.55.143) port 80 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 10.211.55.143
> Accept: */*
> wx_unionid:1111
> 
< HTTP/1.1 200 OK
< Server: nginx/1.16.1
< Date: Tue, 24 Mar 2020 03:32:35 GMT
< Content-Type: text/html
< Transfer-Encoding: chunked
< Connection: keep-alive
< Accept-Ranges: none
< Vary: Accept-Encoding
< Content-Security-Policy-Report-Only: script-src 'nonce-Bp6bcOLD0CuEzrS2QSazCQ' 'strict-dynamic' 'unsafe-eval' 'unsafe-inline' http: https:; object-src 'none'; report-uri https://csp.withgoogle.com/csp/static-on-bigtable; base-uri 'none'
< Pragma: no-cache
< Expires: Fri, 01 Jan 1990 00:00:00 GMT
< Cache-Control: no-cache, must-revalidate
< Last-Modified: Mon, 02 Dec 2019 19:30:00 GMT
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 0
< 
<!DOCTYPE html>
<html lang="zh">
  <meta charset="utf-8">
  <title>Google</title>
  <style>
    html { background: #fff; margin: 0 1em; }
    body { font: .8125em/1.5 arial, sans-serif; text-align: center; }
    h1 { font-size: 1.5em; font-weight: normal; margin: 1em 0 0; }
    p#footer { color: #767676; font-size: .77em; }
    p#footer a { background: url(//www.google.cn/intl/zh-CN_cn/images/cn_icp.gif) top right no-repeat; padding: 5px 20px 5px 0; }
    ul { margin: 2em; padding: 0; }
    li { display: inline; padding: 0 2em; }
    div { -moz-border-radius: 20px; -webkit-border-radius: 20px; border: 1px solid #ccc; border-radius: 20px; margin: 2em auto 1em; max-width: 650px; min-width: 544px; }
    div:hover, div:hover * { cursor: pointer; }
    div:hover { border-color: #999; }
    div p { margin: .5em 0 1.5em; }
    img { border: 0; }
  </style>
  <div>
    <a rel="external nofollow" target="_blank" href="https://www.wulaoer.org/wp-content/themes/begin/go.php?url=aHR0cDovL3d3dy5nb29nbGUuY29tLmhrL3dlYmhwP2hsPXpoLUNOJmFtcDthbXA7c291cmNlaWQ9Y25ocA==">
      <img src="//www.google.cn/landing/cnexp/google-search.png" alt="Google" width="586" height="257">
    </a>
    <h1><a rel="external nofollow" target="_blank" href="https://www.wulaoer.org/wp-content/themes/begin/go.php?url=aHR0cDovL3d3dy5nb29nbGUuY29tLmhrL3dlYmhwP2hsPXpoLUNOJmFtcDthbXA7c291cmNlaWQ9Y25ocA=="><strong id="target">google.com.hk</strong></a></h1>
    <p>请收藏我们的网址
  </div>
  <ul>
    <li><a rel="external nofollow" target="_blank" href="https://www.wulaoer.org/wp-content/themes/begin/go.php?url=aHR0cDovL3RyYW5zbGF0ZS5nb29nbGUuY24vP3NvdXJjZWlkPWNuaHA=">翻译</a>
  </ul>
  <p id="footer">&copy;2011 - <a rel="external nofollow" target="_blank" href="https://www.wulaoer.org/wp-content/themes/begin/go.php?url=aHR0cDovL3d3dy5taWliZWlhbi5nb3YuY24v">ICP证合字B2-20070004号</a>
  <script nonce="Bp6bcOLD0CuEzrS2QSazCQ">
    var gcn=gcn||{};gcn.IS_IMAGES=(/images\.google\.cn/.exec(window.location)||window.location.hash=='#images'||window.location.hash=='images');gcn.HOMEPAGE_DEST='http://www.google.com.hk/webhp?hl=zh-CN&sourceid=cnhp';gcn.IMAGES_DEST='http://images.google.com.hk/imghp?'+'hl=zh-CN&sourceid=cnhp';gcn.DEST_URL=gcn.IS_IMAGES?gcn.IMAGES_DEST:gcn.HOMEPAGE_DEST;gcn.READABLE_HOMEPAGE_URL='google.com.hk';gcn.READABLE_IMAGES_URL='images.google.com.hk';gcn.redirectIfLocationHasQueryParams=function(){if(window.location.search&&/google\.cn/.exec(window.location)&&!/webhp/.exec(window.location)){window.location=String(window.location).replace('google.cn','google.com.hk')}}();gcn.replaceHrefsWithImagesUrl=function(){if(gcn.IS_IMAGES){var a=document.getElementsByTagName('a');for(var i=0,len=a.length;i<len;i++){if(a[i].href==gcn.HOMEPAGE_DEST){a[i].href=gcn.IMAGES_DEST}}}}();gcn.listen=function(a,e,b){if(a.addEventListener){a.addEventListener(e,b,false)}else if(a.attachEvent){var r=a.attachEvent('on'+e,b);return r}};gcn.stopDefaultAndProp=function(e){if(e&&e.preventDefault){e.preventDefault()}else if(window.event&&window.event.returnValue){window.eventReturnValue=false;return false}if(e&&e.stopPropagation){e.stopPropagation()}else if(window.event&&window.event.cancelBubble){window.event.cancelBubble=true;return false}};gcn.resetChildElements=function(a){var b=a.childNodes;for(var i=0,len=b.length;i<len;i++){gcn.listen(b[i],'click',gcn.stopDefaultAndProp)}};gcn.redirect=function(){window.location=gcn.DEST_URL};gcn.setInnerHtmlInEl=function(a){if(gcn.IS_IMAGES){var b=document.getElementById(a);if(b){b.innerHTML=b.innerHTML.replace(gcn.READABLE_HOMEPAGE_URL,gcn.READABLE_IMAGES_URL)}}};
    gcn.listen(document, 'click', gcn.redirect);
    gcn.setInnerHtmlInEl('target');
  </script>
* Connection #0 to host 10.211.55.143 left intact

第一个返回的是百度的数据,第二个返回的是谷歌的数据,第一个的wx_unionid匹配到了,第二个没有匹配到,所以返回的数据不一样,整个实验验证成功。这里注意验证的时候参数必须使用"-V","-H"不能加参数"--head",否则参数过多过不去。

以上就是nginx通过IP进行分流的实验,如果分流的是一个集群,可以通过proxy_pass到upstream实现负载。

avatar

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: