首页 > 学院 > 操作系统 > 正文

使用OpenResty控制CDN回源主机

2024-06-28 16:00:32
字体:
来源:转载
供稿:网友

http://scyuan.info/2016/03/07/openresty-cdn-original.html

年前粗略看了一下《OpenResty最佳实践》,感觉OpenResty是个好东西呀,但是一下子又找不到使用场景,所以就放到一边了。最近遇到一个需求,感觉用OpenResty正合适,所以终于在生产环境实践了一把。

需求

一个javaScript脚本分发服务:

        key浏览器 --------------> 分发服务GET /js?key=xxxx        302 CDN地址浏览器 <-------------- 分发服务Location: cdn.x.com/digest/xxxx.js//分发服务根据key获取用户的配置(用户可以通过web界面修改,需要尽快生效)//以及配置对应的静态js文件(分布在分发服务的本地硬盘),//计算配置和静态内容的摘要,拼接到CDN的URL中,//当配置或静态内容更新后,重定向新的URL,CDN将触发回源流程。        CDN地址              回源浏览器 --------------> CDN --------------> 分发服务

由于该服务的请求量还是挺大的(每天的重定向请求量在七百六十万左右,回源请求量在八万左右),所以部署了两个分发服务,前面挡了一个Nginx做负载均衡。

本来做Nginx是为了容错,结果就因为这个还带来了一个小问题:

由于两台分发服务的配置更新存在时间差,特别是静态文件,所以设想一下,服务A配置已经更新,而服务B没有更新,一个请求来到服务A,重定向到新的URL,而回源的请求来到服务B,B返回旧的JS脚本,被CDN缓存,那么新配置的生效时间将会推迟到CDN缓存失效。。。

解决方案

首先想到的是将回源服务独立出来,如果有配置更新,首先等待回源服务生效,然后再更新重定向服务。这个方案的缺点就是麻烦,得多部署一套服务,前端人员更新JS静态文件得等挺久,所以否掉了。

然后想到的一个方案是,重定向的时候在URL中标示出回源主机,举例来说,A返回重定向地址,回源请求来到Nginx,Nginx根据URL判断需要转发到A,而不是B,如此就不会出现上面提到的问题,另外即使某台服务宕机,另外一台也可以正常提供服务。

这块逻辑不应该耦合到原有的分发服务,所以就是想到了使用OpenResty解决:

http {...    upstream js_backend {        server 10.1.1.20:8080;        server 10.1.1.21:8080;        keepalive 64;    }...    server {        listen       80;...        location ^~ /20/ {            PRoxy_pass http://10.1.1.20:8080/;            proxy_http_version 1.1;            proxy_set_header Connection "";        }        location ^~ /21/ {            proxy_pass http://10.1.1.21:8080/;            proxy_http_version 1.1;            proxy_set_header Connection "";        }        location = /js {            proxy_pass http://js_backend;            proxy_http_version 1.1;            proxy_set_header Connection "";            header_filter_by_lua_block {                if ngx.status == 302 then                    local regex = "^([0-9]+).([0-9]+).([0-9]+).([0-9]+):([0-9]+)$"                    local m, err = ngx.re.match(ngx.var.upstream_addr, regex)                    if m then                        local loc = ngx.header["Location"]                        local s = loc:find("/", 9)                        ngx.header["Location"] = table.concat({loc:sub(1, s), m[4], "/", loc:sub(s+1, -1)})                    else                        ngx.log(ngx.ERR, err)                    end                end            }        }                ...            }}    

这里没有把upstream_addr完全拼进去,然后根据该段转发,主要是考虑到安全上的问题。

服务切了过来,一切正常,性能也没受到影响。(刚切过来时,回源请求比较多,受了点儿影响)

总的来说,对于OpenResty印象相当好,如果场景合适,以后会多多使用。

更新 0603

最近有一个后端tomcat挂掉了,然后看到error.log中大量的

2016/06/02 15:00:25 [error] 5128#0: *412855176 connect() failed (111: Connection refused) while connecting to upstream, client: 49.117.113.178, server: *.touclick.com, request: "GET /xxx HTTP/1.1", upstream: "http://10.47.64.40:8099/xxx", host: "js.touclick.com", referrer: "http://x"2016/06/02 15:00:25 [warn] 5128#0: *412855176 upstream server temporarily disabled while connecting to upstream, client: 49.117.113.178, server: *.touclick.com, request: "GET /xxx HTTP/1.1", upstream: "http://10.47.64.40:8099/xxx", host: "js.touclick.com", referrer: "http://x"2016/06/02 15:00:25 [error] 5128#0: *412855176 [lua] xxx_url_rewrite.lua:15: nil while reading response header from upstream, client: 49.117.113.178, server: *.touclick.com, request: "GET /xxx HTTP/1.1", upstream: "http://10.168.234.54:8099/xxx", host: "js.touclick.com", referrer: "http://x"

前两行容易理解,是由于后端tomcat挂掉了,但是第三行有点儿奇怪,意思是正则表达式无法匹配ngx.var.upstream_addr,然后回想起平时偶尔也会看到一两条,但是因为太偶然所以没继续关注。

ngx.var.upstream_addr打印出来发现是10.47.64.40:8099, 10.168.234.54:8099这个,查了一下文档:

$upstream_addr

keeps the IP address and port, or the path to the UNIX-domain socket of the upstream server. If several servers were contacted during request processing, their addresses are separated by commas, e.g. “192.168.1.1:80, 192.168.1.2:80, unix:/tmp/sock”. If an internal redirect from one server group to another happens, initiated by “X-Accel-Redirect” or error_page, then the server addresses from different groups are separated by colons, e.g. “192.168.1.1:80, 192.168.1.2:80, unix:/tmp/sock : 192.168.10.1:80, 192.168.10.2:80”.

啊…… 发现了一个bug……

修复如下:

if ngx.status == ngx.HTTP_MOVED_TEMPORARILY then    local regex = "^([0-9]+).([0-9]+).([0-9]+).([0-9]+):([0-9]+)$"    local upstream_addr = ngx.var.upstream_addr    local i, j = upstream_addr:find(", ")    while i do        upstream_addr = upstream_addr:sub(j+1, -1)        i, j = upstream_addr:find(", ")    end    local m, err = ngx.re.match(upstream_addr, regex, "o")    if m then        local loc = ngx.header["Location"]        local i, j = loc:find("://")        local s = loc:find("/", j+1)        ngx.header["Location"] = table.concat({ngx.var.scheme, loc:sub(i, s), m[4], "/", loc:sub(s+1, -1)})    else        ngx.log(ngx.WARN, "upstream_addr: ", ngx.var.upstream_addr)        ngx.log(ngx.ERR, err)    endend
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表