200字范文,内容丰富有趣,生活中的好帮手!
200字范文 > nginx 加路由时报错_Nginx自定义模块编写:根据post参数路由到不同服务器

nginx 加路由时报错_Nginx自定义模块编写:根据post参数路由到不同服务器

时间:2022-12-05 22:04:41

相关推荐

nginx 加路由时报错_Nginx自定义模块编写:根据post参数路由到不同服务器

Nginx可以轻松实现根据不同的url 或者 get参数来转发到不同的服务器,然而当我们需要根据http包体来进行请求路由时,Nginx默认的配置规则就捉襟见肘了,但是没关系,Nginx提供了强大的自定义模块功能,我们只要进行需要的扩展就行了。

我们来理一下思路,我们的需求是:

Nginx根据http包体的参数,来选择合适的路由

在这之前,我们先来考虑另一个问题:

在Nginx默认配置的支持下,能否实现服务器间的跳转呢?即类似于状态机,从一个服务器执行OK后,跳转到另一台服务器,按照规则依次传递下去。

答案是可以的,这也是我之前写bayonet之后,在nginx上特意尝试的功能。

一个示例的配置如下:

server{

listen8080;

server_namelocalhost;

location/{

proxy_passhttp://localhost:8888;

error_page433=@433;

error_page434=@434;

}

location@433{

proxy_passhttp://localhost:6788;

}

location@434{

proxy_passhttp://localhost:6789;

}

error_page500502503504/50x.html;

location=/50x.html{

roothtml;

}

}

看明白了吧?我们使用了 433和434 这两个非标准http协议的返回码,所有请求进入时都默认进入 http://localhost:8888;,然后再根据返回码是 433 还是 434 来选择进入 http://localhost:6788 还是 http://localhost:6789。

OK,也许你已经猜到我将这个例子的用意了,是的,我们只要在我们的自定义模块中,根据http的包体返回不同的返回码,进而 proxy_pass 到不同的后端服务器即可。

好吧,接下来,我们正式进入nginx自定义模块的编写中来。

一. nginx 自定义模块编写 由于这也是我***次写nginx模块,所以也是参考了非常多文档,我一一列在这里,所以详细的入门就不说了,只说比较不太一样的地方。 参考链接:

而我们这个模块一个***的特点就是,需要等包体整个接收完才能进行处理,所以有如下代码:

voidngx_http_foo_post_handler(ngx_http_request_t*r){

//请求全部读完后从这里入口,可以产生响应

ngx_http_request_body_t*rrb=r->request_body;

char*body=NULL;

intbody_size=0;

if(rb&&rb->buf)

{

body=(char*)rb->buf->pos;

body_size=rb->buf->last-rb->buf->pos;

}

intresult=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_idfail,result:%d",result);

result=DFT_ROUTE_ID;

}

ngx_http_finalize_request(r,result);

}

staticngx_int_tngx_http_req_route_handler(ngx_http_request_t*r)

{

ngx_http_read_client_request_body(r,ngx_http_foo_post_handler);

returnNGX_DONE;//主handler结束

}

我们注册了一个回调函数 ngx_http_foo_post_handler,当包体全部接受完成时就会调用。之后我们调用了get_route_id来获取返回码,然后通过 ngx_http_finalize_request(r, result);来告诉nginx处理的结果。

这里有个小插曲,即get_route_id。我们来看一下它定义的原型:

externintget_route_id(ngx_log_t*log,intmethod,char*uri,char*args,char*body,intbody_size)

***个参数是 ngx_log_t *log,是为了方便在报错的时候打印日志。然而在最开始的时候,get_route_id 的原型是这样:

externintget_route_id(ngx_http_request_t*r,intmethod,char*uri,char*args,char*body,intbody_size);

结果在 get_route_id 函数内部,调用:

r->connection->log

的结果总是null,至今也不知道为什么。

OK,接下来我们只要在get_route_id中增加逻辑代码,读几行配置,判断一下就可以了~ 但是,我想要的远不止如此。

二、lua解析器的加入

老博友应该都看过我之前写的一篇博客:代码即数据,数据即代码(1)-把难以变更的代码变成易于变更的数据,而这一次的需求也非常符合使用脚本的原则:

只需要告诉我返回nginx哪个返回码,具体怎么算出来的,再复杂,再多变,都放到脚本里面去。

所以接下来我又写了c调用lua的代码:

intget_route_id(ngx_log_t*log,intmethod,char*uri,char*args,char*body,intbody_size)

{

constcharlua_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,"cannotrunconfigurationfile:%s",lua_tostring(L,-1));

lua_close(L);

return-1;

}

lua_getglobal(L,lua_funcname);/*functiontobecalled*/

lua_pushnumber(L,method);

lua_pushstring(L,uri);

lua_pushstring(L,args);

lua_pushlstring(L,body,body_size);

/*dothecall(1arguments,1result)*/

if(lua_pcall(L,4,1,0)!=0)

{

ngx_log_error(NGX_LOG_ERR,log,0,"errorrunningfunction%s:%s",lua_funcname,lua_tostring(L,-1));

lua_close(L);

return-2;

}

/*retrieveresult*/

if(!lua_isnumber(L,-1))

{

ngx_log_error(NGX_LOG_ERR,log,0,"function%smustreturnanumber",lua_funcname);

lua_close(L);

return-3;

}

intresult=(int)lua_tonumber(L,-1);

lua_pop(L,1);/*popreturnedvalue*/

lua_close(L);

returnresult;

}

比较郁闷的是,lua 5.2的很多函数都变了,比如lua_open废弃,变成luaL_newstate等,不过总体来说还算没浪费太多时间。

接下来是req_route.lua的内容,我只截取入口函数如下:

functionget_route_id(method,uri,args,body)

loc,pf,appid=get_need_vals(method,uri,args,body)

ifloc==nilorpf==nilorappid==nilthen

returnOUT_CODE

end

--到这里位置,就把所有的数据都拿到了

--print(loc,pf,appid)

--找是否在对应的url,loc中

ifnotis_match_pf_and_loc(pf,loc)then

returnOUT_CODE

end

--找是否在对应的appid中

ifnotis_match_appid(appid)then

returnOUT_CODE

end

returnIN_CODE

end

OK,结合了lua解析器之后,无论多复杂的调整,我们都基本可以做到只修改lua脚本而不需要重新修改、编译nginx模块代码了。

接下来,就该是体验我们的成果了。

三、Nginx配置

server{

listen8080;

server_namelocalhost;

location/req_route{

req_route;

error_page433=@433;

error_page434=@434;

}

location@433{

proxy_passhttp://localhost:6788;

}

location@434{

proxy_passhttp://localhost:6789;

}

error_page500502503504/50x.html;

location=/50x.html{

roothtml;

}

}

OK,enjoy it!

***,放出代码如下:

【编辑推荐】

【责任编辑:黄丹 TEL:(010)68476606】

点赞 0

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。