nginx 加路由时报错_Nginx自定义模块编写:根据post参数路由到不同服务器
Nginx可以輕松實(shí)現(xiàn)根據(jù)不同的url 或者 get參數(shù)來(lái)轉(zhuǎn)發(fā)到不同的服務(wù)器,然而當(dāng)我們需要根據(jù)http包體來(lái)進(jìn)行請(qǐng)求路由時(shí),Nginx默認(rèn)的配置規(guī)則就捉襟見(jiàn)肘了,但是沒(méi)關(guān)系,Nginx提供了強(qiáng)大的自定義模塊功能,我們只要進(jìn)行需要的擴(kuò)展就行了。
我們來(lái)理一下思路,我們的需求是:
Nginx根據(jù)http包體的參數(shù),來(lái)選擇合適的路由
在這之前,我們先來(lái)考慮另一個(gè)問(wèn)題:
在Nginx默認(rèn)配置的支持下,能否實(shí)現(xiàn)服務(wù)器間的跳轉(zhuǎn)呢?即類(lèi)似于狀態(tài)機(jī),從一個(gè)服務(wù)器執(zhí)行OK后,跳轉(zhuǎn)到另一臺(tái)服務(wù)器,按照規(guī)則依次傳遞下去。
答案是可以的,這也是我之前寫(xiě)bayonet之后,在nginx上特意嘗試的功能。
一個(gè)示例的配置如下:
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 這兩個(gè)非標(biāo)準(zhǔn)http協(xié)議的返回碼,所有請(qǐng)求進(jìn)入時(shí)都默認(rèn)進(jìn)入 http://localhost:8888;,然后再根據(jù)返回碼是 433 還是 434 來(lái)選擇進(jìn)入 http://localhost:6788 還是 http://localhost:6789。
OK,也許你已經(jīng)猜到我將這個(gè)例子的用意了,是的,我們只要在我們的自定義模塊中,根據(jù)http的包體返回不同的返回碼,進(jìn)而 proxy_pass 到不同的后端服務(wù)器即可。
好吧,接下來(lái),我們正式進(jìn)入nginx自定義模塊的編寫(xiě)中來(lái)。
一. nginx 自定義模塊編寫(xiě) 由于這也是我***次寫(xiě)nginx模塊,所以也是參考了非常多文檔,我一一列在這里,所以詳細(xì)的入門(mén)就不說(shuō)了,只說(shuō)比較不太一樣的地方。 參考鏈接:
而我們這個(gè)模塊一個(gè)***的特點(diǎn)就是,需要等包體整個(gè)接收完才能進(jìn)行處理,所以有如下代碼:
void?ngx_http_foo_post_handler(ngx_http_request_t?*r){
//?請(qǐng)求全部讀完后從這里入口,?可以產(chǎn)生響應(yīng)
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結(jié)束
}
我們注冊(cè)了一個(gè)回調(diào)函數(shù) ngx_http_foo_post_handler,當(dāng)包體全部接受完成時(shí)就會(huì)調(diào)用。之后我們調(diào)用了get_route_id來(lái)獲取返回碼,然后通過(guò) ngx_http_finalize_request(r, result); 來(lái)告訴nginx處理的結(jié)果。
這里有個(gè)小插曲,即get_route_id。我們來(lái)看一下它定義的原型:
extern?int?get_route_id(ngx_log_t?*log,?int?method,?char*?uri,?char*?args,?char*?body,?int?body_size)
***個(gè)參數(shù)是 ngx_log_t *log,是為了方便在報(bào)錯(cuò)的時(shí)候打印日志。然而在最開(kāi)始的時(shí)候,get_route_id 的原型是這樣:
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)用:
r->connection->log
的結(jié)果總是null,至今也不知道為什么。
OK,接下來(lái)我們只要在get_route_id中增加邏輯代碼,讀幾行配置,判斷一下就可以了~ 但是,我想要的遠(yuǎn)不止如此。
二、lua解析器的加入
老博友應(yīng)該都看過(guò)我之前寫(xiě)的一篇博客:?代碼即數(shù)據(jù),數(shù)據(jù)即代碼(1)-把難以變更的代碼變成易于變更的數(shù)據(jù),而這一次的需求也非常符合使用腳本的原則:
只需要告訴我返回nginx哪個(gè)返回碼,具體怎么算出來(lái)的,再?gòu)?fù)雜,再多變,都放到腳本里面去。
所以接下來(lái)我又寫(xiě)了c調(diào)用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的很多函數(shù)都變了,比如lua_open廢棄,變成luaL_newstate等,不過(guò)總體來(lái)說(shuō)還算沒(méi)浪費(fèi)太多時(shí)間。
接下來(lái)是req_route.lua的內(nèi)容,我只截取入口函數(shù)如下:
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
--到這里位置,就把所有的數(shù)據(jù)都拿到了
--print?(loc,?pf,?appid)
--?找是否在對(duì)應(yīng)的url,?loc中
if?not?is_match_pf_and_loc(pf,?loc)?then
return?OUT_CODE
end
--?找是否在對(duì)應(yīng)的appid中
if?not?is_match_appid(appid)?then
return?OUT_CODE
end
return?IN_CODE
end
OK,結(jié)合了lua解析器之后,無(wú)論多復(fù)雜的調(diào)整,我們都基本可以做到只修改lua腳本而不需要重新修改、編譯nginx模塊代碼了。
接下來(lái),就該是體驗(yàn)我們的成果了。
三、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!
***,放出代碼如下:
【編輯推薦】
【責(zé)任編輯:黃丹 TEL:(010)68476606】
點(diǎn)贊 0
總結(jié)
以上是生活随笔為你收集整理的nginx 加路由时报错_Nginx自定义模块编写:根据post参数路由到不同服务器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 5999元起!华为WATCH Ultim
- 下一篇: 世界上首枚3D打印火箭未能进入轨道 官方