動態(tài)路由選路一(tengine+lua+redis)

需求

  • 根據(jù)提供的api路由規(guī)范灭美,確定后續(xù)嚴格遵循 /api/xxx 來請求志笼,通過固定匹配的FQDN導(dǎo)致來轉(zhuǎn)發(fā)
  • 根據(jù)cookie來判斷選擇對應(yīng)的FQDN
  • 當匹配cookie后,對應(yīng)的FQDN地址沒有上游server服務(wù)地址,則需要降級辽俗,轉(zhuǎn)發(fā)給默認的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;
}

思路

根據(jù)需求坐榆,可以使用openresty來實現(xiàn)拴魄,或者是使用基于nginx+resty_lua_module或者是ltengine+resty_lua_module來實現(xiàn),并且提前將對應(yīng)的路徑名存入redis席镀,來實現(xiàn)動態(tài)的獲取和轉(zhuǎn)發(fā)匹中,基于cookie可以拼接對應(yīng)的FQDN地址,檢測不存在的情況下豪诲,也可以通過自定義引入降級地址來轉(zhuǎn)發(fā)流量顶捷。

實現(xiàn)

Lua代碼

Lua腳本

-- 根據(jù)location第二段 /api/xxx ,來轉(zhuǎn)發(fā)到對應(yīng)名稱的后端FQDN

-- 分割字字符串,局部函數(shù)
local function split( str,reps )
    local resultStrList = {}
    string.gsub(str,'[^'..reps..']+',function ( w )
        table.insert(resultStrList,w)
    end)
    return resultStrList
end

-- 通過請求uri屎篱,提取接口字符串服赎,并從redis里獲取對應(yīng)的值
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取值狀態(tài)判斷
if res.status ~= 200 then
    ngx.log(ngx.ERR, "【redis server returned bad status】: ", res.status)
    ngx.exit(res.status)
end

-- redis取值內(nèi)容非空判斷
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

-- 檢查結(jié)果類型
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地址對應(yīng)的后端服務(wù)是否存在
  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 階段交播,進行路由的動態(tài)轉(zhuǎn)發(fā)重虑,nginx中的rdis配置的是internal,只有nginx內(nèi)部才能調(diào)用秦士,

server {
listen 8080;
# 內(nèi)部訪問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獲取對應(yīng)key的value嚎尤,來確定后續(xù)的轉(zhuǎn)發(fā)地址

  • redis中的值,可以動態(tài)的調(diào)整伍宦,初始芽死,根據(jù)nginx配置文件中已有路由條目,編寫好初始插入redis的數(shù)據(jù)

例如:

# 插入數(shù)據(jù) 到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

預(yù)期

Mac本地測試关贵,由于無法連接k8s網(wǎng)絡(luò),只能根據(jù)打印日志展示


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

# 國內(nèi)源
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"]
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末揖曾,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子亥啦,更是在濱河造成了極大的恐慌炭剪,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件翔脱,死亡現(xiàn)場離奇詭異奴拦,居然都是意外死亡,警方通過查閱死者的電腦和手機届吁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進店門错妖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來绿鸣,“玉大人,你說我怎么就攤上這事暂氯〕蹦#” “怎么了?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵痴施,是天一觀的道長擎厢。 經(jīng)常有香客問我,道長辣吃,這世上最難降的妖魔是什么锉矢? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮齿尽,結(jié)果婚禮上沽损,老公的妹妹穿的比我還像新娘。我一直安慰自己循头,他們只是感情好绵估,可當我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著卡骂,像睡著了一般国裳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上全跨,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天缝左,我揣著相機與錄音,去河邊找鬼浓若。 笑死渺杉,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的挪钓。 我是一名探鬼主播是越,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼碌上!你這毒婦竟也來了倚评?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤馏予,失蹤者是張志新(化名)和其女友劉穎天梧,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體霞丧,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡呢岗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片敷燎。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡暂筝,死狀恐怖箩言,靈堂內(nèi)的尸體忽然破棺而出硬贯,到底是詐尸還是另有隱情,我是刑警寧澤陨收,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布饭豹,位于F島的核電站,受9級特大地震影響务漩,放射性物質(zhì)發(fā)生泄漏拄衰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一饵骨、第九天 我趴在偏房一處隱蔽的房頂上張望翘悉。 院中可真熱鬧,春花似錦居触、人聲如沸妖混。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽制市。三九已至,卻和暖如春弊予,著一層夾襖步出監(jiān)牢的瞬間祥楣,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工汉柒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留误褪,地道東北人。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓碾褂,卻偏偏與公主長得像振坚,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子斋扰,可洞房花燭夜當晚...
    茶點故事閱讀 43,440評論 2 348

推薦閱讀更多精彩內(nèi)容