什么是路由
这里所说的「路由」不是用在网络中的概念,而是在 web 开发中,访问路径以及具体的内容映射。比如,访问 /a
映射到某个具体的页面,这个就称为一个路由。动态路由,顾名思义就是动态地添加这种映射关系。
OpenResty 中定义路由
对于 OpenResty (Nginx) 来说,得益于它强劲的性能,常常用来当作反向代理网关。通过它强大的 location
配置将不同的 /$request_uri
转发到不同的后端服务中,这样的转发关系也就是我们这里要说到的「路由」:
...
localtion = /a {
...
proxy_pass http://$server_a;
}
location = /b {
....
proxy_pass http://$server_b;
}
但是这些关系必须写死在配置文件中,当有后端有服务变更就需要手动地修改配置文件并 reload
使配置生效。这样的操作在今天以容器作为服务的载体下就显得异常的繁琐了,容器的生命周期通常来说是短暂的,一旦容器发生变动就需要去手动修改配置。
通过 Lua + Redis 实现动态路由
应用场景
该方案是将原来定义 upstream 中的 server_ip 存放在 redis 中,通过 lua 去读取后直接 proxy_pass
,这样做其实是损失了 Nginx 中 upstream 中的负载均衡算法,心跳机制,所有应用场景存在一定的局限性,就是 proxy_pass
的 server_ip 是一个 LB 的地址,这个 LB 上实现了原来由 Nginx upstream 中实现的负载均衡和心跳机制。有个好消息就是这样的方案就非常适合现在的 Kubernetes 架构了,Kubernetes 中的 Service 对象产生的 cluster_ip 正是一个 LB 的 IP。
- 使用 ngx_redis2 模块来读取 redis 实现读取 redis 的接口,并在 location 中配置
internal
保护这个接口只运行内部调用。
location = /redis {
internal;
set_unescape_uri $key $arg_key;
redis2_query get $key;
redis2_pass 192.168.4.182:6379;
}
- 使用 ngx.location.capture 来调用内部接口,它可以发起非阻塞的内部请求访问目标 location。
location = /app1 {
resolver 114.114.114.114;
set $target '';
default_type "text/html";
access_by_lua_block {
local rds_key = "app1"
# 从 redis 中获取 key 为 app1 对应的 server_ip
local res = ngx.location.capture('/get_redis', { args = {key = rds_key}})
local parser = require("redis.parser")
local server, typ = parser.parse_reply(res.body)
if typ ~= parser.BULK_REPLY or not server then
ngx.log(ngx.ERR, "bad redis response: ", res.body)
ngx.exit(500)
end
ngx.var.target = server
}
proxy_pass http://$target;
}
关于性能
该方案每次请求都会去请求一次 redis网上有人使用本地缓存 + redis 缓存来提升性能,具体看这里 。
网友评论