nginx 加路由时报错_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模塊,所以也是參考了非常多文檔,我一一列在這里,所以詳細的入門就不說了,只說比較不太一樣的地方。 參考鏈接:
而我們這個模塊一個***的特點就是,需要等包體整個接收完才能進行處理,所以有如下代碼:
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?orpf==?nil?orappid==?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!
***,放出代碼如下:
【編輯推薦】
【責任編輯:黃丹 TEL:(010)68476606】
點贊 0
總結
以上是生活随笔為你收集整理的nginx 加路由时报错_Nginx自定义模块编写:根据post参数路由到不同服务器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 5999元起!华为WATCH Ultim
- 下一篇: centos7启动dhcp失败_4:Ng