OpenResty实现动态路由,


什么是路由

这里所说的「路由」不是用在网络中的概念,而是在 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。

lua+redis实现动态路由.png
location = /redis {
    internal;
    set_unescape_uri $key $arg_key;
    redis2_query get $key;
    redis2_pass 192.168.4.182:6379;
}
  1. 使用 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 缓存来提升性能,具体看这里 。

相关内容

    暂无相关文章