美文网首页
动态路由选路一(tengine+lua+redis)

动态路由选路一(tengine+lua+redis)

作者: 猪蹄胖 | 来源:发表于2023-08-31 14:29 被阅读0次

    需求

    • 根据提供的api路由规范,确定后续严格遵循 /api/xxx 来请求,通过固定匹配的FQDN导致来转发
    • 根据cookie来判断选择对应的FQDN
    • 当匹配cookie后,对应的FQDN地址没有上游server服务地址,则需要降级,转发给默认的FQDN
    location /api/bpm/ {
        set $backend http://dmo-sso.default.svc.cluster.local;
        if ($cookie_userTag ~ ^beta){
           set $backend http://dmo-sso-beta.default.svc.cluster.local;
        }
        proxy_pass $backend;
    }
    

    思路

    根据需求,可以使用openresty来实现,或者是使用基于nginx+resty_lua_module或者是ltengine+resty_lua_module来实现,并且提前将对应的路径名存入redis,来实现动态的获取和转发,基于cookie可以拼接对应的FQDN地址,检测不存在的情况下,也可以通过自定义引入降级地址来转发流量。

    实现

    Lua代码

    Lua脚本

    -- 根据location第二段 /api/xxx ,来转发到对应名称的后端FQDN
    
    -- 分割字字符串,局部函数
    local function split( str,reps )
        local resultStrList = {}
        string.gsub(str,'[^'..reps..']+',function ( w )
            table.insert(resultStrList,w)
        end)
        return resultStrList
    end
    
    -- 通过请求uri,提取接口字符串,并从redis里获取对应的值
    local uri_data = ngx.var.uri
    local key = split(ngx.var.host, '.')[1]
    local field = split(uri_data, '/')[2]
    local db = 0
    local res = ngx.location.capture("/redis_hget", { args = { key = key , field = field , db = db } })
    
    -- redis取值状态判断
    if res.status ~= 200 then
        ngx.log(ngx.ERR, "【redis server returned bad status】: ", res.status)
        ngx.exit(res.status)
    end
    
    -- redis取值内容非空判断
    if not res.body then
        ngx.log(ngx.ERR, "【redis returned empty body】")
        ngx.exit(500)
    end
    
    -- redis解析,多值返回 (redis无密码)
    local parser = require "redis.parser"
    local results = parser.parse_replies(res.body, 2)
    for i, result in ipairs(results) do
      if i == 2 then
        server = result[1]
        typ = result[2]
      end
    end
    
    -- 检查结果类型
    if typ ~= parser.BULK_REPLY or not server then
        ngx.log(ngx.ERR, "【bad redis response】: ", res.body)
        ngx.exit(500)
    end
    
    -- 自定义拼接FQDN
    local default_fqdn = ".default.svc.cluster.local"
    local target = server .. default_fqdn
    
    -- 降级默认地址
    local degrade_target = "dmo-apaas-gateway.default.svc.cluster.local"
    
    -- cookie_userTag 检测
    local upstream = require "ngx.upstream"
    local get_servers = upstream.get_servers
    -- 获取单个指定的userTag
    local userTag = ngx.var.cookie_userTag
    
    -- 判断非空和空值
    if( userTag ~= nil ) and ( userTag ~= "" ) then
      ngx.log(ngx.NOTICE, "【userTag】: ", userTag)
      cookie_target = server .. "-" .. userTag .. default_fqdn
      ngx.log(ngx.NOTICE, "【cookie_target】: ", cookie_target)
      local servers, err = get_servers(cookie_target)
      -- 判断FQDN地址对应的后端服务是否存在
      if( servers ~= nil ) then
        ngx.var.target = cookie_target
      else
        ngx.log(ngx.ERR, "【failed to get servers in upstream】: ", err)
        -- 降级
        ngx.var.target = degrade_target
      end
    else
      ngx.var.target = target
    end
    
    ngx.log(ngx.NOTICE, "【target】: ", ngx.var.target)
    

    nginx配置

    access_by_lua 阶段,进行路由的动态转发,nginx中的rdis配置的是internal,只有nginx内部才能调用,

    server {
    listen 8080;
    # 内部访问redis
    location = /redis_hget {
     internal;
     set_unescape_uri $key $arg_key;
     set_unescape_uri $field $arg_field;
     set_unescape_uri $db $arg_db;
     redis2_query select $db;
     redis2_query hget $key $field;
     redis2_pass redis-idgenerator.default.svc.cluster.local:16379;
    }
    # 接口
    location ^~ /api/ {
      set $target '';
      access_by_lua_file lua/test.lua;
      proxy_pass http://$target;
     }
    }
    

    redis

    • 从redis获取对应key的value,来确定后续的转发地址

    • redis中的值,可以动态的调整,初始,根据nginx配置文件中已有路由条目,编写好初始插入redis的数据

    例如:

    # 插入数据 到redis的 db0
     cat dynamic_api_hash.txt | redis-cli -h redis-idgenerator.default.svc.cluster.local -p 16379 -n 0
    

    redis里面的键值对可以使用hmset来批量插入

    # cat dynamic_api_key_value.txt
    HMSET  passport1  i18n  dmo-lego-i18n-rest  eadmin  dmo-lego-org-framework-rest  kakashi  dmo-lego-kakashi
    

    键值对也可以单个插入,方便修改和查询(也可以使用HSET)

    ECHO =========app=========== 
    HMSET  app  standardOpen  dmo-lego-standard-openapi
    HMSET  app  config  dmo-lego-config
    HMSET  app  develop  dmo-lego-develop-rest
    HMSET  app  resource  dmo-lego-resource-rest
    HMSET  app  shareGroup  dmo-lego-club
    

    预期

    Mac本地测试,由于无法连接k8s网络,只能根据打印日志展示


    image.png image.png

    匹配cookie和降级


    image.png

    编译

    • tengine + resty_lua_module
    • 下载需要使用到的依赖包代码
    • 编写 makefile (使用buildx,支持arm和amd x86)
    • 编写 dockerfile
    all: build push
    
    build:
        docker buildx build --platform linux/arm64,linux/amd64  -t registry.bizsaas.net/tengine_lua:2.3.2.3 -f Dockerfile.buildx .  --push
    
    .PHONY: all build
    
    FROM alpine:latest
    MAINTAINER Browser <yunjie.xiao@clickpaas.com>
    
    # 版本号
    ENV     VER_TENGINE                     2.3.2
    ENV     VER_LUAJIT2                     2.1-20220411
    ENV     VER_NGX_DEVEL_KIT               0.3.1
    ENV     VER_ECHO_NGINX_MODULE           0.62
    ENV     VER_LUA_NGINX_MODULE            0.10.14
    ENV     VER_LUA_REDIS_PARSER            0.13
    ENV     VER_LUA_RESTY_CORE              0.1.23
    ENV     VER_LUA_RESTY_REDIS             0.29
    ENV     VER_LUA_UPSTREAM_NGINX_MODULE   0.07
    ENV     VER_REDIS2_NGINX_MODULE         0.15
    ENV     VER_SET_MISC_NGINX_MODULE       0.33
    
    # 国内源
    RUN echo "https://mirrors.ustc.edu.cn/alpine/v3.9/main" > /etc/apk/repositories \
            && echo "https://mirrors.ustc.edu.cn/alpine/v3.9/community" >> /etc/apk/repositories
    
    # 依赖
    RUN apk update
    RUN apk add --no-cache --virtual .build-deps \
        gcc \
        libc-dev \
        make \
        openssl \
        openssl-dev \
        pcre \
        pcre-dev \
        zlib-dev \
        linux-headers \
        curl \
        gnupg \
        libxslt-dev \
        gd-dev \
        geoip-dev \
        perl-dev
    
    # 编译
    COPY app /app
    RUN chown root.root -R /app && cd /app  \
        && tar zxvf tengine-${VER_TENGINE}.tar.gz \
        && tar zxvf v${VER_LUAJIT2}.tar.gz \
        && tar zxvf v${VER_NGX_DEVEL_KIT}.tar.gz \
        && tar zxvf v${VER_ECHO_NGINX_MODULE}.tar.gz \
        && tar zxvf v${VER_LUA_NGINX_MODULE}.tar.gz \
        && tar zxvf v${VER_LUA_REDIS_PARSER}.tar.gz \
        && tar zxvf v${VER_LUA_RESTY_CORE}.tar.gz  \
        && tar zxvf v${VER_LUA_RESTY_REDIS}.tar.gz \
        && tar zxvf v${VER_LUA_UPSTREAM_NGINX_MODULE}.tar.gz \
        && tar zxvf v${VER_REDIS2_NGINX_MODULE}.tar.gz \
        && tar zxvf v${VER_SET_MISC_NGINX_MODULE}.tar.gz \
        && cd luajit2-${VER_LUAJIT2} && make install PREFIX=/usr/local/luajit && cd - \
        && export LUAJIT_LIB=/usr/local/luajit/lib \
        && export LUAJIT_INC=/usr/local/luajit/include/luajit-2.1 \
        && cd lua-resty-core-${VER_LUA_RESTY_CORE} && make install PREFIX=/usr/local && cd - \
        && cd lua-resty-redis-${VER_LUA_RESTY_REDIS} && make install PREFIX=/usr/local && cd - \
        && cd lua-redis-parser-${VER_LUA_REDIS_PARSER} && make LUA_INCLUDE_DIR=/usr/local/luajit/include/luajit-2.1 && make install && cd - \
        && cd tengine-${VER_TENGINE} \
        && ./configure \
        --prefix=/etc/nginx \
        --with-pcre \
        --sbin-path=/usr/sbin/nginx \
        --conf-path=/etc/nginx/nginx.conf \
        --error-log-path=/var/log/nginx/error.log \
        --http-log-path=/var/log/nginx/access.log \
        --pid-path=/var/run/nginx.pid \
        --lock-path=/var/run/nginx.lock \
        --http-client-body-temp-path=/var/cache/nginx/client_temp \
        --http-proxy-temp-path=/var/cache/nginx/proxy_temp \
        --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \
        --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \
        --http-scgi-temp-path=/var/cache/nginx/scgi_temp \
        --user=root \
        --group=root \
        --with-http_ssl_module \
        --with-http_realip_module \
        --with-http_addition_module \
        --with-http_sub_module \
        --with-http_dav_module \
        --with-http_flv_module \
        --with-http_mp4_module \
        --with-http_gunzip_module \
        --with-http_gzip_static_module \
        --with-http_random_index_module \
        --with-http_secure_link_module \
        --with-http_stub_status_module \
        --with-http_auth_request_module \
        --with-http_slice_module \
        --with-mail \
        --with-mail_ssl_module \
        --with-file-aio \
        --with-ipv6 \
        --with-stream \
        --with-stream_ssl_module \
        --with-ld-opt="-Wl,-rpath,/usr/local/luajit/lib" \
        --add-module=../ngx_devel_kit-${VER_NGX_DEVEL_KIT} \
        --add-module=../echo-nginx-module-${VER_ECHO_NGINX_MODULE} \
        --add-module=../lua-nginx-module-${VER_LUA_NGINX_MODULE} \
        --add-module=../redis2-nginx-module-${VER_REDIS2_NGINX_MODULE} \
        --add-module=../set-misc-nginx-module-${VER_SET_MISC_NGINX_MODULE} \
        --add-module=../lua-upstream-nginx-module-${VER_LUA_UPSTREAM_NGINX_MODULE} \
        && make -j2 \
        && make install
    
    # nginx 配置
    RUN cp -rf /app/nginx.conf /etc/nginx/nginx.conf \
        && cp -rf /app/*.html /etc/nginx/html \
        && mkdir -p /var/log/nginx \
        && mkdir -p /var/cache/nginx \
        && touch /var/log/nginx/access.log \
        && touch /var/log/nginx/error.log \
        && ln -sf /dev/stdout /var/log/nginx/access.log \
        && ln -sf /dev/stderr /var/log/nginx/error.log
    
    # nginx-controller
    RUN cp -rf /app/Shanghai /etc/localtime \
        && tar zxvf /app/nginx-controller.tar.gz -C / \
        && chown root.root /nginx-controller \
        && echo "nohup ./nginx-controller -log_dir=/var/log/nginx &" >> /run.sh \
        && echo "nginx -g 'daemon off;'" >> /run.sh \
        && chmod a+x /run.sh \
        && rm -rf /app
    
    WORKDIR /
    
    EXPOSE 80 443
    
    CMD ["/bin/sh","-c","/run.sh"]
    

    相关文章

      网友评论

          本文标题:动态路由选路一(tengine+lua+redis)

          本文链接:https://www.haomeiwen.com/subject/ffjzmdtx.html