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模块,所以也是参考了非常多文档,我一一列在这里,所以详细的入门就不说了,只说比较不太一样的地方。 参考链接:
nginx的helloworld模块的helloworld
nginx 一个例子模块,简单的将http请求的内容返输出
nginx 自定义协议 扩展模块开发
Emiller的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 or pf == nil or appid == 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!
最后,放出代码如下:
https://vimercode.googlecode.com/svn/trunk/nginx_req_route
转载于:https://blog.51cto.com/lucifer119/1406783
相关文章:
深度学习中的贝叶斯统计简介
贝叶斯用概率反映知识状态的确定性程度。数据集能够被直接观测到,因此不是随机的。另一方面,真实参数θ是未知或不确定的,因此可以表示成随机变量。在观察到数据前,我们将θ的已知知识表示成先验概率分布(prior probability distr…

少走弯路:强烈推荐的TensorFlow快速入门资料(可下载)
(图片付费下载自视觉中国)作者 | 黄海广来源 | 机器学习初学者(ID: ai-start-com)知识更新非常快,需要一直学习才能跟上时代进步,举个例子:吴恩达老师在深度学习课上讲的TensorFlow使用…

有状态bean与无状态bean
在学习bean的作用域的时候,了解了这个问题。 bean5种作用域:分别是:singleton、prototype、request、session、gloabal session 接下来就讲一下有状态bean与无状态bean: 有状态会话bean :每个用户有自己特有的一个实例…

从Developer Removed From Sale 回到可下载状态的方法
2019独角兽企业重金招聘Python工程师标准>>> 如果你不小心点了”Remove“ 按钮,App的状态会变成"Developer Removed From Sale ",这时,即使更新应用也无法改变这个状态。想要让App恢复可下载状态,你需要尝试…
朴素贝叶斯分类器简介及C++实现(性别分类)
贝叶斯分类器是一种基于贝叶斯定理的简单概率分类器。在机器学习中,朴素贝叶斯分类器是一系列以假设特征之间强(朴素)独立下运用贝叶斯定理为基础的简单概率分类器。朴素贝叶斯是文本分类的一种热门(基准)方法,文本分类是以词频为特征判断文件所属类别或…

你当年没玩好的《愤怒的小鸟》,AI现在也犯难了
(图片源自百度百科)作者 | Ekaterina Nikonova,Jakub Gemrot译者 | Tianyu出品 | AI科技大本营(ID:rgznai100)现在说起《愤怒的小鸟》游戏,要把人的回忆一下拉扯到差不多十年前了。它是一款当时一经推出就广…

msf反弹shell
今天回顾了一下msf反弹shell的操作,在这里做一下记录和分享。( ̄︶ ̄)↗ 反弹shell的两种方法 第一种Msfvenom实例: 1、msfconsole #启动msf 2、msfvenom -p php/meterpreter/reverse_tcp LHOST<Your IP Address> LPOR…

mysql 5.5半同步复制功能部署
安装、配置Semi-sync Replication在两台主机上安装好MySQL5.5,编译好的插件在目录CMAKE_INSTALL_PREFIX/lib/plugin下(默认是/usr/local/mysql/lib/plugin)。例如这里编译是指定CMAKE_INSTALL_PREFIX为/home/mysql/mysql,则有&…
Windows7/10上配置OpenCV3.3.0-Python3.6.2操作步骤
目前OpenCV无论是2.4.x还是最新的3.3.0版本,默认支持的都是Python 2.7版本。这里介绍下如何使OpenCV 3.3.0支持Python 3.6.2的操作步骤:1. 从 https://github.com/opencv/opencv/releases/tag/3.3.0 下载3.3.0.zip或opencv-3.3.0-vc14.exe,…

manage.py命令
一、manage.py命令选 manage.py是每个Django项目中自动生成的一个用于管理项目的脚本文件,需要通过python命令执行。manage.py接受的是Django提供的内置命令。 内置命令包含 checkdbshelldiffsettingsflushmakemigrationsmigraterunservershellstartappstartproject…

图灵奖得主Bengio再次警示:可解释因果关系是深度学习发展的当务之急
(图片付费下载自视觉中国)作者 | Will Knight译者 | Monanfei来源 | Wired出品 | AI科技大本营(ID:rgznai100)深度学习擅长在大量数据中寻找模式,但无法解释它们之间的关系。图灵奖获得者 Yoshua Bengio 希望改变这一状…

解决jQuery不同版同时引用的冲突
今天研发的同事在开发一个新jQuery插件时,遇到一个揪心的问题。平台以前使用的 jQuery版本是1.2.6,偶,天啊!这是古代的版本啊! 由于很多功能基于老版本,不能删除啊,同志们都懂的! 于…

TensorFlow中的计算图
作者 | stephenDC来源 | 大数据与人工智能(ID:ai-big-data)1 什么是计算图?一个机器学习任务的核心是模型的定义以及模型的参数求解方式,对这两者进行抽象之后,可以确定一个唯一的计算逻辑,将这个逻辑用图表…

java设计模式-适配器模式
模式导读: 每个人都有自己不同的需要,每个人都有自己能够接受的不同方式,就像是为满足现在快速度发展的社会,几乎人人离不开手机的时代,我们也许会碰到在外出行手机电量不足的情况,这个时候如果你在车站,你…
Ubuntu 14.04 64位上安装Valgrind 3.13.0 操作步骤
关于Valgrind的介绍和使用可以参考: http://blog.csdn.net/fengbingchun/article/details/50196189 在Ubuntu 14.04上可以通过以下命令直接安装Valgrind,直接通过命令安装的版本是3.10.1,如下图,有些较老,目前最新版本…

粗谈Android中的对齐
在谈这个之前先啰嗦几个概念。 基线:书写英语单词时为了规范书写会设有四条线,从上至下第三条就是基线。基线对齐主要是为了两个控件中显示的英文单词的基线对齐,如下所示: Start:在看API的时候经常会有Start对齐&…
OpenCV3.3中逻辑回归(Logistic Regression)使用举例
OpenCV3.3中给出了逻辑回归(logistic regression)的实现,即cv::ml::LogisticRegression类,类的声明在include/opencv2/ml.hpp文件中,实现在modules/ml/src/lr.cpp文件中,它既支持两分类,也支持多分类,其中:…

多数编程语言里的0.1+0.2≠0.3?
作者 | Parul Malhotra译者 | Raku出品 | AI科技大本营(ID:rgznai100)我们从小就被教导说0.10.20.3,但是在奇妙的计算机编程世界里面,事情变得不一样了。我最近在用JavaScript编程,正在阅读数据类型的时候,…

iOSSharing #9 | 2019-05-19
目录 1. setNeedsLayout、layoutIfNeeded与layoutSubviews区别? 2. UIView与CALayer的区别? 3. loadView什么时候被调用?它有什么作用?默认实现是怎么样的? 4. UIViewController的完整生命周期? 5. UIView动…

分表处理(三)
一、概述分表是个目前算是比较炒的比较流行的概念,特别是在大负载的情况下,分表是一个良好分散数据库压力的好方法。首先要了解为什么要分表,分表的好处是什么。我们先来大概了解以下一个数据库执行SQL的过程:接收到SQL –> 放…
逻辑回归(Logistic Regression)简介及C++实现
逻辑回归(Logistic Regression):该模型用于分类而非回归,可以使用logistic sigmoid函数( 可参考:http://blog.csdn.net/fengbingchun/article/details/73848734 )将线性函数的输出压缩进区间(0,1): p(y1| x;θ)σ(θTx).逻辑回归是…

CVPR 2019论文阅读:Libra R-CNN如何解决不平衡对检测性能的影响?
作者 | 路一直都在出品 | AI科技大本营(ID:rgznai100)Paper link:https://arxiv.org/pdf/1904.02701.pdfCode link:https://github.com/OceanPang/Libra_R-CNNAbstract在目标检测中,人们更关注的往往是模型结构&#x…

实现nginx上配置免费证书Let's Encrypt
Lets Encrypt 的免费证书有效期为三个月,不过可以免费续期,写一个脚本定期更新即可。 准备一台nginx 服务器 ,将以下三个附件上传到你的nginx服务器。 1、下载脚本文件,wget https://raw.githubusercontent.com/xdtianyu/scripts/…

深入解析Windows操作系统笔记——CH1概念和术语
1.概念和工具 本章主要介绍Windows操作系统的关键概念和术语 1.概念和工具... 1 1.1操作系统版本... 1 1.2基础概念和术语... 2 1.2.1Windows API2 1.2.2 服务、函数和例程... 3 1.2.3 进程、线程和作业... 4 1.2.3.1 进程... 4 1.2.3.2 线程... 4 1.2.3.3 虚拟地址描述符... 4…
C++/C++11中std::exception的使用
std::exception:标准异常类的基类,其类的声明在头文件<exception>中。所有标准库的异常类均继承于此类,因此通过引用类型可以捕获所有标准异常。 std::exception类定义了无参构造函数、拷贝构造函数、拷贝赋值运算符、一个虚析构函数和…

技术不错的程序员,为何面试却“屡战屡败”
为何很多有不少编程经验,技术能力不错的程序员,去心仪公司面试时却总是失败?至于失败的原因,可能很多人都没意识到过。01想要通关面试,千万别让数据结构拖了后腿很多公司,比如 BAT、Google、Facebook&#…

FastJson 转换 javaBean 时 null 值被忽略都问题
[toc] 问题 当 JavaeBean 中某个属性值为 null 时,转换为 JSONObject 对象或者 json 字符串时,该属性值被忽略。如何让不管值是否为 null,转化后该属性还存在,只是值为 null。 情况演示 class St {private String sid;private Str…

来玩Play框架07 静态文件
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明。谢谢! Play框架的主要功能是提供动态响应的内容。但一个网络项目中必然有大量的静态内容,比如图片、Javascript文件、CSS文件等。我下面介绍…
C++/C++11中std::runtime_error的使用
std::runtime_error:运行时错误异常类,只有在运行时才能检测到的错误,继承于std::exception,它的声明在头文件<stdexcept>中。std::runtime_error也用作几个运行时错误异常的基类,包括std::range_error(生成的结…

估值被砍700亿美元后,Waymo发重磅公开信:即将推出全自动驾驶打车服务
(图片源自 Waymo 官网)编译 | 夕颜出品 | AI科技大本营(ID:rgznai100)近日,据 Reddit 盛传的一封电子邮件副本显示,Alphabet 旗下的自动驾驶汽车公司 Waymo 已经向其自动驾驶服务的用户发送了一封电子邮件&…