成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Nginx自定義模塊編寫:根據post參數路由到不同服務器

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

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

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

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

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

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

答案是可以的,這也是我之前寫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 這兩個非標準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模塊開發指南

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

  1. void ngx_http_foo_post_handler(ngx_http_request_t *r){ 
  2.     // 請求全部讀完后從這里入口, 可以產生響應 
  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結束 

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

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

  1. 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 的原型是這樣:

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

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

  1. r->connection->log 

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

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

二、lua解析器的加入

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

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

所以接下來我又寫了c調用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的很多函數都變了,比如lua_open廢棄,變成luaL_newstate等,不過總體來說還算沒浪費太多時間。

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

  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.     --到這里位置,就把所有的數據都拿到了 
  7.     --print (loc, pf, appid) 
  8.     -- 找是否在對應的url, loc中 
  9.     if not is_match_pf_and_loc(pf, loc) then 
  10.         return OUT_CODE 
  11.     end 
  12.     -- 找是否在對應的appid中 
  13.     if not is_match_appid(appid) then 
  14.         return OUT_CODE 
  15.     end 
  16.     return IN_CODE 
  17. end 

OK,結合了lua解析器之后,無論多復雜的調整,我們都基本可以做到只修改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.googlecode.com/svn/trunk/nginx_req_route

責任編輯:黃丹 來源: IT技術博客
相關推薦

2011-03-21 09:16:52

2010-05-18 17:07:29

IIS服務器

2011-04-06 15:05:58

nagios監控Linux

2009-08-01 12:00:15

ASP.NET服務器自ASP.NET服務器ASP.NET

2011-05-19 10:16:27

ASP.NET

2012-09-24 14:31:55

C#網絡協議C

2009-08-03 13:34:06

自定義C#控件

2021-03-16 10:39:29

SpringBoot參數解析器

2022-07-11 10:37:41

MapPart集合

2018-11-21 09:53:08

服務服務器分類

2021-05-28 08:58:41

Golang網卡metrics

2009-06-25 14:53:35

自定義UI組件JSF框架

2009-08-01 09:21:12

ASP.NET服務器自ASP.NET服務器控ASP.NET

2010-04-02 11:08:30

惠普服務器選購

2012-05-18 10:52:20

TitaniumAndroid模塊自定義View模塊

2020-11-19 10:50:43

ImportPython代碼

2009-12-17 15:42:25

Rails自定義Hel

2021-02-04 09:18:20

服務器認證自定義

2016-08-23 13:21:15

MVC路由視圖

2015-06-10 10:54:24

自定義路PHP
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产精品中文字幕一区二区三区 | 日本一区二区三区在线观看 | 99精品国产一区二区三区 | 亚洲精品电影网在线观看 | 麻豆av在线免费观看 | 在线中文视频 | a在线观看| 色综合一区二区三区 | 日韩在线观看一区 | 中文一级片 | 精品一区二区三区在线观看国产 | 日韩av一区二区在线观看 | 欧美高清视频一区 | 91社区在线观看高清 | 成人国产精品久久 | 久久精品国产亚洲a | 欧美v免费 | 国产成人精品一区二区三区 | 欧美一区二区三区在线播放 | 香蕉视频久久久 | 一二三区av | www.久| 日韩精品久久久久 | 99热精品在线 | 最新中文字幕在线 | 午夜在线观看免费 | 色吊丝2288sds中文字幕 | 久久久新视频 | 亚洲成人久久久 | 国产精品成人一区二区三区夜夜夜 | 精品国产一级片 | 精品福利一区二区三区 | 婷婷中文字幕 | 国产成人精品一区二区在线 | 国产精品毛片无码 | 午夜影院在线 | 特一级毛片 | 视频一区二区三区在线观看 | 精品国产欧美日韩不卡在线观看 | 日韩中文一区二区三区 | 欧美日韩国产高清视频 |