nginx本身是个极出色的http服务器,除此之外还可以跟应用服务器(比如api)结合,将独立业务(比如认证)分离出来,让应用服务器变的更灵活更强大。本文将以实际例子展现nginx的扩展性能力。
0. 需求:实现电影列表的接口
服务端程序员写了个接口,以提供其它人调用,接口要求以json格式返回一个电影列表数据。他用了世界上最好的语言开发,代码如下:
-- movie.php --
$rows = array(
array(‘id‘ => 1, ‘title‘ => ‘Reggae‘),
array(‘id‘ => 4, ‘title‘ => ‘Indie‘),
array(‘id‘ => 5, ‘title‘ => ‘Rap‘),
array(‘id‘ => 6, ‘title‘ => ‘Cowbell‘)
);
$json = json_encode($rows);
echo $json;
?>
他很开心的提交了任务,但没过多久真正要花时间的考验来了。PM要求他给接口认证,不想让它成为公共接口。
1. 什么是认证
他是个google stackoverflow粉,几番翻墙后,决定以这种方式实现:让访问电影接口前先拿到一个token作为凭据,然后以此token访问接口。所以他把问题简化为两件事:
a)提供生成token的访问
b)在movie.php接口前作access token的验证
看着对接的程序员在那悠哉着等着他实现完提供接口,把接口格式定完先抛给他:
a)生成access_token
http://192.168.1.102/token?appid=some_id&secret=some_secret
b)给电影接口加个参数access_token
http://192.168.1.102/movie.php?access_token=some_token
并且多唠叨了几句:
* 你丫的把appid和secret的值保存好,不能暴露。
* access_token是有过期时间的。
2. 灵活的程序员
首先,他考虑到access_token需要存储、查找,越高效越好,用mysql+memcached or redis缓存吗?这次用点不一样的,快速高效的,于是他选了handlersocket的方式。handlersocket是mysql的一个插件,简单讲就是让操作mysql直通存储层。https://github.com/DeNA/HandlerSocket-Plugin-for-MySQL
其次,他开始不现实的默念:如果有个人帮我把认证处理了,我专心写api就可以,不要让我的代码看到一丝认证的影子。思维总是一张纸而已,分分钟他开始尝试了这个想法。
* 先建oauth_access_token表
CREATE TABLE oauth_access_token (
id int(10) NOT NULL AUTO_INCREMENT,
access_token varchar(255) DEFAULT NULL,
expires_in int(10) NOT NULL,
last_used_time int(10) NOT NULL,
PRIMARY KEY (id),
KEY ACCESS_TOKEN (access_token)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
* 给api加上认证功能,不用改代码哦,直接在nginx配置里指定
upstream hsock_rsrv {
server 192.168.100.133:9998;
keepalive 1024;
}
upstream hsock_wsrv {
server 192.168.100.133:9999;
keepalive 1024;
}
server {
listen 80;
location / {
root html/api;
index index.php;
}
oauth_db some_db;
oauth_table oauth_access_token;
# 生成access_token
location /token {
oauth_token hsock_wsrv; # 指定handlersocket的写服务器,对应上面的upstream
oauth_appid some_appid;
oauth_secret some_secret;
oauth_expires_in 2h;
}
location /check {
oauth_check hsock_rsrv; # 仅供api认证调用,返回200才是正常。外部不会访问
}
location ~ \.php$ {
oauth_request /check; # 让api具备认证功能,对应上面的 location /check
# 以下就是你的api配置了,以php为例
root html;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
3. 缺少了什么
细心的你可能发现了,上面有几个指定是nginx不支持的,都是以oauth_开头:oauth_token, oauth_appid, oauth_secret, oauth_expires_in, oauth_check, oauth_request。oauth本身是个协议,有多种方式,从复杂到简单,这里的api认证只是其中一个简单的方式,以oauth这个命名有点哗众取宠,但是挺直观,所以还是用它作为模块的命名。到此,开始安装模块:
> git clone [email protected]:hongzhidao/nginx-http-oauth-module.git
> cd /work/nginx-1.8.0 && ./configure --add-module=/work/nginx-http-oauth-module && make && make install
源码下载:http://nglua.com/download/nginx-http-oauth-module.tar.gz
4. 集思广义
nginx具备很强的扩展能力,如果您的程序是构建在http之上,有些公共的模块可以交给nginx去处理,让程序本身只关注业务部分。github上已经有很多优秀的nginx模块。在nginx方面,我有不少有意思的想法和代码,期待交流。