中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Nginx自定義模塊中根據post參數路由到不同服務器的示例分析

發布時間:2021-11-02 10:11:39 來源:億速云 閱讀:145 作者:柒染 欄目:系統運維

本篇文章給大家分享的是有關Nginx自定義模塊中根據post參數路由到不同服務器的示例分析,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

Nginx可以輕松實現根據不同的url 或者 get參數來轉發到不同的服務器,然而當我們需要根據http包體來進行請求路由時,Nginx默認的配置規則就捉襟見肘了,但是沒關系,Nginx提供了強大的自定義模塊功能,我們只要進行需要的擴展就行了。

我們來理一下思路,我們的需求是:

Nginx根據http包體的參數,來選擇合適的路由

在這之前,我們先來考慮另一個問題:

在Nginx默認配置的支持下,能否實現服務器間的跳轉呢?即類似于狀態機,從一個服務器執行OK后,跳轉到另一臺服務器,按照規則依次傳遞下去。

答案是可以的,這也是我之前寫bayonet之后,在nginx上特意嘗試的功能。

一個示例的配置如下:

server {     listen       8080;     server_name  localhost;     location / {         proxy_pass http://localhost:8888;         error_page 433 = @433;         error_page 434 = @434;     }     location @433 {         proxy_pass http://localhost:6788;     }     location @434 {         proxy_pass http://localhost:6789;     }     error_page   500 502 503 504  /50x.html;     location = /50x.html {         root   html;     } }

看明白了吧?我們使用了 433和434 這兩個非標準http協議的返回碼,所有請求進入時都默認進入 http://localhost:8888;,然后再根據返回碼是 433 還是 434 來選擇進入 http://localhost:6788 還是 http://localhost:6789。

OK,也許你已經猜到我將這個例子的用意了,是的,我們只要在我們的自定義模塊中,根據http的包體返回不同的返回碼,進而 proxy_pass 到不同的后端服務器即可。

好吧,接下來,我們正式進入nginx自定義模塊的編寫中來。

一. nginx 自定義模塊編寫 由于這也是我***次寫nginx模塊,所以也是參考了非常多文檔,我一一列在這里,所以詳細的入門就不說了,只說比較不太一樣的地方。 參考鏈接:

  1. nginx的helloworld模塊的helloworld

  2. nginx 一個例子模塊,簡單的將http請求的內容返輸出

  3. nginx 自定義協議 擴展模塊開發

  4. Emiller的Nginx模塊開發指南

而我們這個模塊一個***的特點就是,需要等包體整個接收完才能進行處理,所以有如下代碼:

void ngx_http_foo_post_handler(ngx_http_request_t *r){     // 請求全部讀完后從這里入口, 可以產生響應     ngx_http_request_body_t* rrb = r->request_body;       char* body = NULL;     int body_size = 0;       if (rb && rb->buf)     {         body = (char*)rb->buf->pos;         body_size = rb->buf->last - rb->buf->pos;     }       int result = get_route_id(r->connection->log,                                (int)r->method,                               (char*)r->uri.data,                               (char*)r->args.data,                               body,                               body_size                               );     if (result < 0)     {         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "get_route_id fail, result:%d", result);         result = DFT_ROUTE_ID;     }     ngx_http_finalize_request(r, result);   } static ngx_int_t ngx_http_req_route_handler(ngx_http_request_t *r) {     ngx_http_read_client_request_body(r, ngx_http_foo_post_handler);     return NGX_DONE; // 主handler結束 }

我們注冊了一個回調函數 ngx_http_foo_post_handler,當包體全部接受完成時就會調用。之后我們調用了get_route_id來獲取返回碼,然后通過 ngx_http_finalize_request(r, result); 來告訴nginx處理的結果。

這里有個小插曲,即get_route_id。我們來看一下它定義的原型:

extern int get_route_id(ngx_log_t *log, int method, char* uri, char* args, char* body, int body_size)

***個參數是 ngx_log_t *log,是為了方便在報錯的時候打印日志。然而在最開始的時候,get_route_id 的原型是這樣:

extern int get_route_id(ngx_http_request_t *r, int method, char* uri, char* args, char* body, int body_size);

結果在 get_route_id 函數內部,調用:

r->connection->log

的結果總是null,至今也不知道為什么。

OK,接下來我們只要在get_route_id中增加邏輯代碼,讀幾行配置,判斷一下就可以了~ 但是,我想要的遠不止如此。

二、lua解析器的加入

老博友應該都看過我之前寫的一篇博客: 代碼即數據,數據即代碼(1)-把難以變更的代碼變成易于變更的數據,而這一次的需求也非常符合使用腳本的原則:

只需要告訴我返回nginx哪個返回碼,具體怎么算出來的,再復雜,再多變,都放到腳本里面去。

所以接下來我又寫了c調用lua的代碼:

int get_route_id(ngx_log_t *log, int method, char* uri, char* args, char* body, int body_size) {     const char lua_funcname[] = "get_route_id";     lua_State *L = luaL_newstate();     luaL_openlibs(L);     if (luaL_loadfile(L, LUA_FILENAME) || lua_pcall(L, 0, 0, 0))     {         ngx_log_error(NGX_LOG_ERR, log, 0, "cannot run configuration file: %s", lua_tostring(L, -1));         lua_close(L);         return -1;     }      lua_getglobal(L, lua_funcname); /* function to be called */     lua_pushnumber(L, method);     lua_pushstring(L, uri);     lua_pushstring(L, args);     lua_pushlstring(L, body, body_size);     /* do the call (1 arguments, 1 result) */     if (lua_pcall(L, 4, 1, 0) != 0)     {         ngx_log_error(NGX_LOG_ERR, log, 0, "error running function %s: %s", lua_funcname, lua_tostring(L, -1));         lua_close(L);         return -2;     }     /* retrieve result */     if (!lua_isnumber(L, -1))     {         ngx_log_error(NGX_LOG_ERR, log, 0, "function %s must return a number", lua_funcname);         lua_close(L);         return -3;     }     int result = (int)lua_tonumber(L, -1);       lua_pop(L, 1); /* pop returned value */       lua_close(L);     return result; }

比較郁悶的是,lua 5.2的很多函數都變了,比如lua_open廢棄,變成luaL_newstate等,不過總體來說還算沒浪費太多時間。

接下來是req_route.lua的內容,我只截取入口函數如下:

function get_route_id(method, uri, args, body)     loc, pf ,appid = get_need_vals(method, uri, args, body)     if loc == nil or pf == nil or appid == nil then         return OUT_CODE     end     --到這里位置,就把所有的數據都拿到了     --print (loc, pf, appid)     -- 找是否在對應的url, loc中     if not is_match_pf_and_loc(pf, loc) then         return OUT_CODE     end     -- 找是否在對應的appid中     if not is_match_appid(appid) then         return OUT_CODE     end     return IN_CODE end

OK,結合了lua解析器之后,無論多復雜的調整,我們都基本可以做到只修改lua腳本而不需要重新修改、編譯nginx模塊代碼了。

接下來,就該是體驗我們的成果了。

三、Nginx配置

server {     listen       8080;     server_name  localhost;       location /req_route {         req_route;         error_page 433 = @433;         error_page 434 = @434;     }     location @433 {         proxy_pass http://localhost:6788;     }     location @434 {         proxy_pass http://localhost:6789;     }     error_page   500 502 503 504  /50x.html;     location = /50x.html {         root   html;     } }

OK,enjoy it!

以上就是Nginx自定義模塊中根據post參數路由到不同服務器的示例分析,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

巴南区| 隆安县| 宿州市| 宝兴县| 黔南| 大埔区| 若羌县| 隆子县| 通海县| 瑞金市| 马公市| 滕州市| 佛坪县| 高安市| 淳化县| 青冈县| 桑植县| 托克逊县| 永春县| 桂林市| 玛纳斯县| 斗六市| 吉林市| 临沂市| 玉山县| 乳山市| 沈丘县| 湘乡市| 江油市| 沅江市| 金溪县| 都昌县| 东光县| 香港| 策勒县| 堆龙德庆县| 抚顺县| 大厂| 始兴县| 黄骅市| 巴东县|