NGINX动态反向代理
Posted iTutor技术
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NGINX动态反向代理相关的知识,希望对你有一定的参考价值。
动态反向代理典型的一个应用场景是:
公司内网中有相当多的Uri资源,包含不同的IP、端口等,对终端用户来说,记录每个资源的Uri相当容易出错。所以这些资源需要统一的反向代理服务器包装成统一访问入口。
在对Uri资源进行可编程式的分类后,动态反向代理则可使用可编程脚本进行Uri资源路由,避免人为介入配置,完成动态反向代理。
正向代理(Proxy)
正向代理也就是我们平时常说的代理服务器。是位于客户端和远端服务器之间的服务器,为了从远端服务器取得内容。
客户端向正向代理发送一个请求并指定目标(远端服务器),然后正向代理向远端服务器转交请求并将获得的内容返回给客户端。
正向代理就像客户端和远端服务器之间的跳板。
反向代理(Reverse Proxy)
反向代理是代理服务器的一种,根据客户端的请求,从后端的服务器(如Web服务器)上获取资源,然后再将这些资源返回给客户端。
客户端都通过反向代理访问不同后端服务器上的资源,而不需要知道这些后端服务器的存在,认为所有资源都来自于这个反向代理服务器。
反向代理的最主要作用如下:
Ø 外网发布
² 将防火墙后面的服务器提供给互联网用户访问,加强安全防护。
² 使用高级 URL策略管理,可使处于不同服务器系统的URL资源于同一个 URL 空间下。(本文所讨论的动态反向代理)
Ø 负载均衡
² 后端的多台服务器提供负载均衡,或为后端较慢的服务器提供缓冲服务
正向代理 VS 反向代理
正向代理(代理客户端): 代理(Proxy)与客户端(Client)站一边,负责与远端服务器(Server)通讯。
反向代理(代理服务端): Proxy与Server站一边,负责接受外界Client请求,根据策略与内部Server通讯。
具体如下图所示:
nginx反向代理
Nginx是一款由俄罗斯人创建面向性能设计的HTTP服务器,具备高性能、高并发、低内存的特色。不采用每客户机单一线程的设计模型,而是充分使用异步逻辑,削减了上下文调度开销,所以并发服务能力更强。在一定的高并发优化下,单Nginx服务器已可承受近10万的并发量。
Nginx整体采用模块化设计,有丰富的模块库和第三方模块库,配置灵活。在Linux操作系统下,Nginx使用epoll事件模型,效率相当高。
Nginx作为web服务器一个重要的功能就是反向代理,其反向代理的指令不需要新增额外的模块,使用默认自带proxy_pass指令,只需要修改配置文件就可以实现反向代理。
Nginx的反向代理配置参考如下案例:
Ø 案例I:用户访问反向代理服务器http://proxy.itutorgroup.com:80的请求将被反向代理至http://www.tutorabc.com.cn/,配置中仅需添加proxy_pass的指令。
#nginx.conf http { server { listen 80; server_name proxy.itutorgroup.com; location / { proxy_pass http://www.tutorabc.com.cn/; } } } |
Ø 案例II:用户访问反向代理服务器http://proxy.itutorgroup.com:80的请求被反向代理至http://www1~www3.tutorabc.com.cn/中持有最少活跃连接数的一台服务器。配置中的proxy_pass反向代理指令引用了上游服务器的负载均衡upstream块。
#nginx.conf http { upstream tutorabcWebSrv { least_conn; server http://www2.tutorabc.com.cn/; server http://www3.tutorabc.com.cn/; }
server { listen 80; server_name proxy.itutorgroup.com; location / { proxy_pass http://tutorabcWebSrv } } } |
案例中引用到的nginx.conf有包含几个配置块,主要职能如下:
配置块 |
简介 |
http块 |
处理http请求的主要配置模块,大多数配置都在这里面进行 |
server块 |
主机的配置块,可以配置多个虚拟主机 |
location块 |
server中对应的目录级别的控制块,可以有多个 |
upstream块 |
反向代理和负载均衡的配置块,可以有多个 |
OpenResty
OpenResty是一个基于Nginx与Lua的高性能Web平台,其内部集成了大量精良的Lua库、第三方Nginx模块以及大多数的依赖项。
其充分利用Nginx的非阻塞I/O 模型,不仅仅对 HTTP 客户端请求,甚至于对远程后端诸如 mysql、PostgreSQL、Memcached以及Redis等都进行一致的高性能响应。
开发人员可使用Lua脚本语言调动Nginx支持的各种C以及Lua模块,快速构造出足以胜任10K乃至1000K以上单机并发连接的高性能Web应用系统。
Lua语言具有如下特性,非常适合快速开发:
Ø 轻量级:它用标准C语言编写并以源代码形式开放,编译后仅仅一百余K,可以很方便的嵌入别的程序里。
Ø 可扩展性: Lua提供了非常易于使用的扩展接口和机制:由宿主语言(通常是C或C++)提供这些功能,Lua可以使用它们,就像是本来就内置的功能一样。
Ø 多编程方式:面向过程(procedure-oriented)编程和函数式编程(functional programming)。
Lua同时提供了代码JIT的功能,使运行时速度上升到CPU指令级,非常高效。
OpenResty+Lua Script+Redis完成动态反向代理
在OpenResty中,Nginx模块与Lua库的集成都可在nginx.conf中完成,参考如下配置案例,
Ø 在/proxy的location块中,定义了remoteaddr的变量。
Ø 在请求访问阶段处理使用script文件夹下的routing.lua脚本处理remoteaddr变量并赋值。
#nginx.conf http { server { listen 80; server_name proxy.itutorgroup.com; location /proxy { set $remoteaddr ""; default_type text/plain; access_by_lua_file /usr/local/openresty/script/routing.lua; proxy_pass $remoteaddr; } } } |
假设经过routing.lua脚本的动态解析,proxy_pass指令可将用户访问反向代理,
http://proxy.itutorgroup.com/proxy/department/team/project/index.html
http://lp.tutorabc.com.cn/lpgs/XssLdiDUOK/index.html
我们来看下脚本包含什么逻辑,可以协助完成动态反向代理。这段Lua脚本会在后面安装配置指南中用到。
--#1. 获取用户访问的Uri,args则赋值为/proxy/department/team/project/index.html local args= ngx.var.uri
--#2. 将"/",作为分隔符,将Uri分割并保存至lua Table内。split函数声明暂放Lua脚本最后 local uriTable = split(args,"/")
--#3. 获取Table中department、team、project栏位的值,uriTable下标从1开始 local department = uriTable[2] local team = uriTable[3] local project = uriTable[4]
--#4. 将三个值拼接后封装成Redis缓存的Key值 local cacheKey = department..'_'.. team..'_'.. project
--#5. 连接本地监听6379端口的Redis缓存,获取值。 local redis = require "resty.redis" local cache = redis.new() cache.connect(cache, '127.0.0.1', 6379) local cacheValue = cache:get(cacheKey)
--#6. 如缓存值未发现,则返回HTTP 404 if res==ngx.null then return ngx.exit(404) end
local count = 0; for _,subUri in ipairs(uriTable) do if count>3 then cacheValue = cacheValue..'/'..subUri end count = count + 1 end
--#8. 对脚本外部location中定义的remoteaddr赋值 ngx.var.remoteaddr = cacheValue
--#附:分割函数 function split(pString, pPattern) local Table = {} -- NOTE: use {n = 0} in Lua-5.0 local fpat = "(.-)" .. pPattern local last_end = 1 local s, e, cap = pString:find(fpat, 1) while s do if s ~= 1 or cap ~= "" then table.insert(Table,cap) end last_end = e+1 s, e, cap = pString:find(fpat, last_end) end if last_end <= #pString then cap = pString:sub(last_end) table.insert(Table, cap) end return Table end |
通过脚本的解释,我们可以了解当确保本地Redis缓存中包含如下kv时,location块则可获得动态反向代理所需参数。
Key |
Value |
Department_Team_Project |
http://lp.tutorabc.com.cn/lpgs/XssLdiDUOK/ |
Nginx Lua模块指令
Ngin+Lua动态反向代理的核心是在location块植入Lua脚本,支持植入满足Lua语法的语句块、或者有访问权限的Lua语句文件,Lua的植入可在Nginx的各个处理阶段。
Nginx共11个处理阶段,而相应的处理阶段是可以做插入式处理,即可插拔式架构;另外指令可以在http、server、server if、location、location if几个范围进行配置,不仅仅局限于location块。
Nginx处理阶段植入Lua脚本的指令表如下:
指令 |
所处处理阶段 |
使用范围 |
解释 |
init_by_lua init_by_lua_file |
loading-config |
http |
nginx Master进程加载配置时执行; 通常用于初始化全局配置/预加载Lua模块 |
init_worker_by_lua init_worker_by_lua_file |
starting-worker |
http |
每个Nginx Worker进程启动时调用的计时器,如果Master进程不允许则只会在init_by_lua之后调用; 通常用于定时拉取配置/数据,或者后端服务的健康检查 |
set_by_lua set_by_lua_file |
rewrite |
server,server if,location,location if |
设置nginx变量,可以实现复杂的赋值逻辑;此处是阻塞的,Lua代码要做到非常快; |
rewrite_by_lua rewrite_by_lua_file |
rewrite tail |
http,server,location,location if |
rrewrite阶段处理,可以实现复杂的转发/重定向逻辑; |
access_by_lua access_by_lua_file |
access tail |
http,server,location,location if |
请求访问阶段处理,用于访问控制 |
content_by_lua content_by_lua_file |
content |
location,location if |
内容处理器,接收请求处理并输出响应 |
header_filter_by_lua header_filter_by_lua_file |
output-header-filter |
http,server,location,location if |
设置header和cookie |
body_filter_by_lua body_filter_by_lua_file |
output-body-filter |
http,server,location,location if |
对响应数据进行过滤,比如截断、替换。 |
log_by_lua log_by_lua_file |
log |
http,server,location,location if |
log阶段处理,比如记录访问量/统计平均响应时间 |
OpenResty安装配置指南
以分步骤引导的方式,介绍在CentOS上进行OpenResty安装、与动态反向代理配置的方法。主要配置将在/usr目录下完成。
1. 下载LuaJIT 2.0.2
http://luajit.org/download/LuaJIT-2.0.2.tar.gz
解压:sudo tar zxvf LuaJIT-2.0.2.tar.gz
2. 下载pcre 8.39
https://sourceforge.net/projects/pcre/files/pcre/8.39/pcre-8.39.tar.gz/download
解压:sudo tar zvxf pcre-8.39.tar.gz
3. 下载zlib 1.2.8
http://zlib.net/fossils/zlib-1.2.8.tar.gz
解压:sudo tar vxf zlib-1.2.8.tar.gz
4. 下载OpenResty 1.11.2.3
https://openresty.org/download/openresty-1.11.2.3.tar.gz
解压:sudo tar zxvf openresty-1.11.2.3.tar.gz
5. 安装LuaJIT 2.0.2
安装在/usr/local目录下
cd /usr/LuaJIT-2.0.2/ make make install export LUAJIT_LIB=/usr/local/lib export LUAJIT_INC=/usr/local/include/luajit-2.0 |
6. 安装OpenResty 1.11.2.3
安装在/usr/local目录下
cd /usr/openresty-1.11.2.3/ ./configure --with-pcre=/usr/pcre-8.39 --with-zlib=/usr/zlib-1.2.8 --with-http_ssl_module --with-luajit make make install |
7. 配置Nginx.conf
vim /usr/local/openresty/nginx/conf/nginx.conf
在location块中加入如下shell指令后保存退出
location /proxy { set $remoteaddr ""; default_type text/plain; access_by_lua_file /usr/local/openresty/script/myscript.lua; proxy_pass $remoteaddr; } |
7. 配置Lua脚本
将之前章节介绍的Lua脚本保存为/usr/myscript.lua,使用如下shell将Lua脚本准备好
mkdir /usr/local/openresty/script/ sudo cp -f/usr/myscript.lua /usr/local/openresty/script/ |
7. 注册Nginx服务,并设置开机自动启动
保存开机启动脚本为/usr/nginx,然后运行shell进行注册。并启动Nginx服务
sudo cp -f /usr/nginx /etc/rc.d/init.d/ sudo chmod +x /etc/rc.d/init.d/nginx chkconfig nginx on systemctl start nginx |
启动脚本:
#!/bin/bash # Tengine Startup script# processname: nginx # chkconfig: - 85 15 # description: nginx is a World Wide Web server. It is used to serve # pidfile: /var/run/nginx.pid # config: /usr/local/nginx/conf/nginx.conf nginxd=/usr/local/openresty/nginx/sbin/nginx nginx_config=/usr/local/openresty/nginx/conf/nginx.conf nginx_pid=/usr/local/openresty/nginx/logs/nginx.pid RETVAL= prog="nginx" # Source function library. . /etc/rc.d/init.d/functions # Source networking configuration. . /etc/sysconfig/network # Check that networking is up. [ ${NETWORKING} = [ -x $nginxd ] || exit # Start nginx daemons functions. start() { if [ -e $nginx_pid ];then echo "tengine already running...." exit fi echo -n $"Starting $prog: " daemon $nginxd -c ${nginx_config} RETVAL=$? echo [ $RETVAL = ] && touch /var/lock/subsys/nginx return $RETVAL } # Stop nginx daemons functions. stop() { echo -n $"Stopping $prog: " killproc $nginxd RETVAL=$? echo [ $RETVAL = ] && rm -f /var/lock/subsys/nginx /usr/local/nginx/logs/nginx.pid } reload() { echo -n $"Reloading $prog: " #kill -HUP `cat ${nginx_pid}` killproc $nginxd -HUP RETVAL=$? echo } # See how we were called. case "$1" in start) start ;; stop) stop ;; reload) reload ;; restart) stop start ;;
status) status $prog RETVAL=$? ;; *) echo $"Usage: $prog {start|stop|restart|reload|status|help}" exit esac exit $RETVAL |
8. 安装并配置Redis
Redis的编译安装配置不在本文介绍,仅提供简单的yum源安装脚本。安装后使用redis-cli set命令将动态反向代理需要的Key准备好。
sudo yum install epel-release sudo yum update sudo yum install redis sudo systemctl enable redis sudo systemctl start redis |
Redis KV参考值如下
Key |
Value |
Department_Team_Project |
http://lp.tutorabc.com.cn/lpgs/XssLdiDUOK/ |
9. 测试动态反向代理
在Nginx安装服务器上访问http://localhost:port/proxy/Department/Team/Project/index.html
则会根据Redis中配置的KV跳转至http://lp.tutorabc.com.cn/lpgs/XssLdiDUOK/index.html
就此Nginx动态反向代理配置成功了,只要通过后端API对Redis缓存进行更新,可以做到运行时的路由切换。
参考文献:
https://en.wikipedia.org/wiki/Reverse_proxy
https://www.zhihu.com/question/24723688
http://nginx.org/en/docs/
http://tool.oschina.net/apidocs/apidoc?api=nginx-zh
http://nginx.org/en/docs/http/ngx_http_upstream_module.html
https://openresty.org/cn/
http://jinnianshilongnian.iteye.com/blog/2186448
https://github.com/openresty/lua-nginx-module
https://www.lua.org/start.html
以上是关于NGINX动态反向代理的主要内容,如果未能解决你的问题,请参考以下文章