乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      Nginx自定義模塊編寫:根據(jù)post參數(shù)路由到不同服務(wù)器

       集微筆記 2014-05-05

      Nginx可以輕松實現(xiàn)根據(jù)不同的url 或者 get參數(shù)來轉(zhuǎn)發(fā)到不同的服務(wù)器,然而當(dāng)我們需要根據(jù)http包體來進(jìn)行請求路由時,Nginx默認(rèn)的配置規(guī)則就捉襟見肘了,但是沒關(guān)系,Nginx提供了強大的自定義模塊功能,我們只要進(jìn)行需要的擴展就行了。

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

      Nginx根據(jù)http包體的參數(shù),來選擇合適的路由

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

      在Nginx默認(rèn)配置的支持下,能否實現(xiàn)服務(wù)器間的跳轉(zhuǎn)呢?即類似于狀態(tài)機,從一個服務(wù)器執(zhí)行OK后,跳轉(zhuǎn)到另一臺服務(wù)器,按照規(guī)則依次傳遞下去。

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

      一個示例的配置如下:

      1. server { 
      2.     listen       8080; 
      3.     server_name  localhost; 
      4.     location / { 
      5.         proxy_pass http://localhost:8888; 
      6.         error_page 433 = @433; 
      7.         error_page 434 = @434; 
      8.     } 
      9.     location @433 { 
      10.         proxy_pass http://localhost:6788; 
      11.     } 
      12.     location @434 { 
      13.         proxy_pass http://localhost:6789; 
      14.     } 
      15.     error_page   500 502 503 504  /50x.html; 
      16.     location = /50x.html { 
      17.         root   html; 
      18.     } 

      看明白了吧?我們使用了 433和434 這兩個非標(biāo)準(zhǔn)http協(xié)議的返回碼,所有請求進(jìn)入時都默認(rèn)進(jìn)入 http://localhost:8888;,然后再根據(jù)返回碼是 433 還是 434 來選擇進(jìn)入 http://localhost:6788 還是 http://localhost:6789。

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

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

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

      1. nginx的helloworld模塊的helloworld
      2. nginx 一個例子模塊,簡單的將http請求的內(nèi)容返輸出
      3. nginx 自定義協(xié)議 擴展模塊開發(fā)
      4. Emiller的Nginx模塊開發(fā)指南

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

      1. void ngx_http_foo_post_handler(ngx_http_request_t *r){ 
      2.     // 請求全部讀完后從這里入口, 可以產(chǎn)生響應(yīng) 
      3.     ngx_http_request_body_t* rrb = r->request_body; 
      4.   
      5.     char* body = NULL; 
      6.     int body_size = 0; 
      7.   
      8.     if (rb && rb->buf) 
      9.     { 
      10.         body = (char*)rb->buf->pos; 
      11.         body_size = rb->buf->last - rb->buf->pos; 
      12.     } 
      13.   
      14.     int result = get_route_id(r->connection->log,  
      15.                               (int)r->method, 
      16.                               (char*)r->uri.data, 
      17.                               (char*)r->args.data, 
      18.                               body, 
      19.                               body_size 
      20.                               ); 
      21.     if (result < 0) 
      22.     { 
      23.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "get_route_id fail, result:%d", result); 
      24.         result = DFT_ROUTE_ID; 
      25.     } 
      26.     ngx_http_finalize_request(r, result); 
      27.   
      28. static ngx_int_t ngx_http_req_route_handler(ngx_http_request_t *r) 
      29.     ngx_http_read_client_request_body(r, ngx_http_foo_post_handler); 
      30.     return NGX_DONE; // 主handler結(jié)束 

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

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

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

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

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

      結(jié)果在 get_route_id 函數(shù)內(nèi)部,調(diào)用:

      1. r->connection->log 

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

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

      二、lua解析器的加入

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

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

      所以接下來我又寫了c調(diào)用lua的代碼:

      1. int get_route_id(ngx_log_t *log, int method, char* uri, char* args, char* body, int body_size) 
      2.     const char lua_funcname[] = "get_route_id"; 
      3.     lua_State *L = luaL_newstate(); 
      4.     luaL_openlibs(L); 
      5.     if (luaL_loadfile(L, LUA_FILENAME) || lua_pcall(L, 0, 0, 0)) 
      6.     { 
      7.         ngx_log_error(NGX_LOG_ERR, log, 0, "cannot run configuration file: %s", lua_tostring(L, -1)); 
      8.         lua_close(L); 
      9.         return -1; 
      10.     }  
      11.     lua_getglobal(L, lua_funcname); /* function to be called */ 
      12.     lua_pushnumber(L, method); 
      13.     lua_pushstring(L, uri); 
      14.     lua_pushstring(L, args); 
      15.     lua_pushlstring(L, body, body_size); 
      16.     /* do the call (1 arguments, 1 result) */ 
      17.     if (lua_pcall(L, 4, 1, 0) != 0) 
      18.     { 
      19.         ngx_log_error(NGX_LOG_ERR, log, 0, "error running function %s: %s", lua_funcname, lua_tostring(L, -1)); 
      20.         lua_close(L); 
      21.         return -2; 
      22.     } 
      23.     /* retrieve result */ 
      24.     if (!lua_isnumber(L, -1)) 
      25.     { 
      26.         ngx_log_error(NGX_LOG_ERR, log, 0, "function %s must return a number", lua_funcname); 
      27.         lua_close(L); 
      28.         return -3; 
      29.     } 
      30.     int result = (int)lua_tonumber(L, -1); 
      31.   
      32.     lua_pop(L, 1); /* pop returned value */ 
      33.   
      34.     lua_close(L); 
      35.     return result; 

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

      接下來是req_route.lua的內(nèi)容,我只截取入口函數(shù)如下:

      1. function get_route_id(method, uri, args, body) 
      2.     loc, pf ,appid = get_need_vals(method, uri, args, body) 
      3.     if loc == nil or pf == nil or appid == nil then 
      4.         return OUT_CODE 
      5.     end 
      6.     --到這里位置,就把所有的數(shù)據(jù)都拿到了 
      7.     --print (loc, pf, appid) 
      8.     -- 找是否在對應(yīng)的url, loc中 
      9.     if not is_match_pf_and_loc(pf, loc) then 
      10.         return OUT_CODE 
      11.     end 
      12.     -- 找是否在對應(yīng)的appid中 
      13.     if not is_match_appid(appid) then 
      14.         return OUT_CODE 
      15.     end 
      16.     return IN_CODE 
      17. end 

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

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

      三、Nginx配置

      1. server { 
      2.     listen       8080; 
      3.     server_name  localhost; 
      4.   
      5.     location /req_route { 
      6.         req_route; 
      7.         error_page 433 = @433; 
      8.         error_page 434 = @434; 
      9.     } 
      10.     location @433 { 
      11.         proxy_pass http://localhost:6788; 
      12.     } 
      13.     location @434 { 
      14.         proxy_pass http://localhost:6789; 
      15.     } 
      16.     error_page   500 502 503 504  /50x.html; 
      17.     location = /50x.html { 
      18.         root   html; 
      19.     } 

      OK,enjoy it!

      最后,放出代碼如下:

      https://vimercode./svn/trunk/nginx_req_route


        本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多