Nginx是一个高性能的Web 服务器,同时是一个高效的反向代理服务器,它仍是一个IMAP/POP3/SMTP 代理服务器。javascript
因为Nginx采用的是事件驱动的架构,可以处理并发百万级别的tcp链接,高度的模块化设计和自由的BSD许可,使得Nginx有着很是丰富的第三方模块。好比Openresty、API网关Kong。php
BSD开源协议是一个给予使用者很大自由的协议。基本上使用者能够”随心所欲”,能够自由的使用,修改源代码,也能够将修改后的代码做为开源或者专有软件再发布。css
Centos系统安装,请参考这里http://www.linuxidc.com/Linux/2016-09/134907.htm。先复制粘贴下它的文章。html
安装 nginx 须要先将官网下载的源码进行编译,编译依赖 gcc 环境,若是没有 gcc 环境,则须要安装:java
yum install gcc-c++node
PCRE(Perl Compatible Regular Expressions) 是一个Perl库,包括 perl 兼容的正则表达式库。nginx 的 http 模块使用 pcre 来解析正则表达式,因此须要在 linux 上安装 pcre 库,pcre-devel 是使用 pcre 开发的一个二次开发库。nginx也须要此库。命令:mysql
yum install -y pcre pcre-devellinux
zlib 库提供了不少种压缩和解压缩的方式, nginx 使用 zlib 对 http 包的内容进行 gzip ,因此须要在 Centos 上安装 zlib 库。nginx
yum install -y zlib zlib-develc++
OpenSSL 是一个强大的安全套接字层密码库,囊括主要的密码算法、经常使用的密钥和证书封装管理功能及 SSL 协议,并提供丰富的应用程序供测试或其它目的使用。 nginx 不只支持 http 协议,还支持 https(即在ssl协议上传输http),因此须要在 Centos 安装 OpenSSL 库。
yum install -y openssl openssl-devel
1.直接下载.tar.gz安装包,地址:https://nginx.org/en/download.html
2.使用wget命令下载(推荐)。
wget -c https://nginx.org/download/nginx-1.10.1.tar.gz
依然是直接命令:
tar -zxvf nginx-1.10.1.tar.gz cd nginx-1.10.1
其实在 nginx-1.10.1 版本中你就不须要去配置相关东西,默认就能够了。固然,若是你要本身配置目录也是能够的。 使用默认配置
./configure
make make install
查找安装路径:
whereis nginx
Nginx的模块从结构上分为核心模块、基础模块和第三方模块:
Nginx的高并发得益于其采用了epoll模型,与传统的服务器程序架构不一样,epoll是linux内核2.6之后才出现的。Nginx采用epoll模型,异步非阻塞,而Apache采用的是select模型。
nginx 环境变量配置:
export PATH=$PATH:/usr/servers/nginx/sbin
cat /var/run/nginx.pid
或者 nginx -s reload一般状况下,Nginx的配置在Ngix的安装目录下的/conf/config.default 文件里,基本配置以下:
worker_process # 表示工做进程的数量,通常设置为cpu的核数 worker_connections # 表示每一个工做进程的最大链接数 server{} # 块定义了虚拟主机 listen # 监听端口 server_name # 监听域名 location {} # 是用来为匹配的 URI 进行配置,URI 即语法中的“/uri/” location /{} # 匹配任何查询,由于全部请求都以 / 开头 root # 指定对应uri的资源查找路径,这里html为相对路径,完整路径为 # /opt/nginx-1.7.7/html/ index # 指定首页index文件的名称,能够配置多个,以空格分开。若有多 # 个,按配置顺序查找。
location 经常使用配置以下:
模式 | 含义 |
---|---|
location = /uri | = 表示精确匹配,只有彻底匹配上才能生效 |
location ^~ /uri | ^~ 开头对URL路径进行前缀匹配,而且在正则以前。 |
location ~ pattern | 开头表示区分大小写的正则匹配 |
location ~* pattern | 开头表示不区分大小写的正则匹配 |
location /uri | 不带任何修饰符,也表示前缀匹配,可是在正则匹配以后 |
location / | 通用匹配,任何未匹配到其它location的请求都会匹配到,至关于switch中的default |
Nginx的经常使用配置很是多,如下内容摘自于布尔教育课件,仅供参考:
#定义Nginx运行的用户和用户组 user www www; #启动进程,一般设置成和cpu的数量相等 worker_processes 8; worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000; #为每一个进程分配cpu,上例中将8个进程分配到8个cpu,固然能够写多个,或者将一个进程分配到多个cpu。 worker_rlimit_nofile 102400; #这个指令是指当一个nginx进程打开的最多文件描述符数目,理论值应该是最多打 #开文件数(ulimit -n)与nginx进程数相除,可是nginx分配请求并非那么均匀 #,因此最好与ulimit -n的值保持一致。 #全局错误日志及PID文件 error_log /usr/local/nginx/logs/error.log; #错误日志定义等级,[ debug | info | notice | warn | error | crit ] pid /usr/local/nginx/nginx.pid; #一个nginx进程打开的最多文件描述符数目,理论值应该是最多打开文件数(系统的值ulimit -n)与nginx进程数相除,可是nginx分配请求并不均匀. #因此建议与ulimit -n的值保持一致。 worker_rlimit_nofile 65535; #工做模式及链接数上限 events { use epoll; #epoll是多路复用IO(I/O Multiplexing)中的一种方式,可是仅用于linux2.6以上内核,能够大大提升nginx的性能 worker_connections 102400; #单个后台worker process进程的最大并发连接数 (最大链接数=链接数*进程数) multi_accept on; #尽量多的接受请求 } #设定http服务器,利用它的反向代理功能提供负载均衡支持 http { #设定mime类型,类型由mime.type文件定义 include mime.types; default_type application/octet-stream; #设定日志格式 access_log /usr/local/nginx/log/nginx/access.log; sendfile on; #sendfile 指令指定 nginx 是否调用 sendfile 函数(zero copy 方式)来输出文件,对于普通应用必须设为 on #若是用来进行下载等应用磁盘IO重负载应用,可设置为 off,以平衡磁盘与网络I/O处理速度,下降系统的uptime. #autoindex on; #开启目录列表访问,合适下载服务器,默认关闭。 tcp_nopush on; #防止网络阻塞 keepalive_timeout 60; #keepalive超时时间,客户端到服务器端的链接持续有效时间,当出现对服务器的后,继请求时,keepalive-timeout功能可避免创建或从新创建链接。 tcp_nodelay on; #提升数据的实时响应性 #开启gzip压缩 gzip on; gzip_min_length 1k; gzip_buffers 4 16k; gzip_http_version 1.1; gzip_comp_level 2; #压缩级别大小,最大为9,值越小,压缩后比例越小,CPU处理更快。 #值越大,消耗CPU比较高。 gzip_types text/plain application/x-javascript text/css application/xml; gzip_vary on; client_max_body_size 10m; #容许客户端请求的最大单文件字节数 client_body_buffer_size 128k; #缓冲区代理缓冲用户端请求的最大字节数, proxy_connect_timeout 90; #nginx跟后端服务器链接超时时间(代理链接超时) proxy_send_timeout 90; #后端服务器数据回传时间(代理发送超时) proxy_read_timeout 90; #链接成功后,后端服务器响应时间(代理接收超时) proxy_buffer_size 4k; #设置代理服务器(nginx)保存用户头信息的缓冲区大小 proxy_buffers 4 32k; #proxy_buffers缓冲区,网页平均在32k如下的话,这样设置 proxy_busy_buffers_size 64k; #高负荷下缓冲大小(proxy_buffers*2) #设定请求缓冲 large_client_header_buffers 4 4k; client_header_buffer_size 4k; #客户端请求头部的缓冲区大小,这个能够根据你的系统分页大小来设置,通常一个请求的头部大小不会超过1k #不过因为通常系统分页都要大于1k,因此这里设置为分页大小。分页大小能够用命令getconf PAGESIZE取得。 open_file_cache max=102400 inactive=20s; #这个将为打开文件指定缓存,默认是没有启用的,max指定缓存数量,建议和打开文件数一致,inactive是指通过多长时间文件没被请求后删除缓存。 open_file_cache_valid 30s; #这个是指多长时间检查一次缓存的有效信息。 open_file_cache_min_uses 1; #open_file_cache指令中的inactive参数时间内文件的最少使用次数,若是超过这个数字,文件描述符一直是在缓存中打开的,如上例,若是有一个文件在inactive #包含其它配置文件,如自定义的虚拟主机 include vhosts.conf; }
配置详解2以下:
#这里为后端服务器wugk应用集群配置,根据后端实际状况修改便可,tdt_wugk为负载均衡名称,能够任意指定 #但必须跟vhosts.conf虚拟主机的pass段一致,不然不能转发后端的请求。weight配置权重,在fail_timeout内检查max_fails次数,失败则剔除均衡。 upstream tdt_wugk { server 127.0.0.1:8080 weight=1 max_fails=2 fail_timeout=30s; server 127.0.0.1:8081 weight=1 max_fails=2 fail_timeout=30s; } #虚拟主机配置 server { #侦听80端口 listen 80; #定义使用www.wuguangke.cn访问 server_name www.wuguangke.cn; #设定本虚拟主机的访问日志 access_log logs/access.log main; root /data/webapps/wugk; #定义服务器的默认网站根目录位置 index index.php index.html index.htm; #定义首页索引文件的名称 #默认请求 location ~ /{ root /data/www/wugk; #定义服务器的默认网站根目录位置 index index.php index.html index.htm; #定义首页索引文件的名称 #如下是一些反向代理的配置. proxy_next_upstream http_502 http_504 error timeout invalid_header; #若是后端的服务器返回50二、50四、执行超时等错误,自动将请求转发到upstream负载均衡池中的另外一台服务器,实现故障转移。 proxy_redirect off; #后端的Web服务器能够经过X-Forwarded-For获取用户真实IP proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://tdt_wugk; #请求转向后端定义的均衡模块 } # 定义错误提示页面 error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } #配置Nginx动静分离,定义的静态页面直接从Nginx发布目录读取。 location ~ .*\.(html|htm|gif|jpg|jpeg|bmp|png|ico|txt|js|css)$ { root /data/www/wugk; #expires定义用户浏览器缓存的时间为3天,若是静态页面不常更新,能够设置更长,这样能够节省带宽和缓解服务器的压力。 expires 3d; } #PHP脚本请求所有转发到 FastCGI处理. 使用FastCGI默认配置. location ~ \.php$ { root /root; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME /data/www/wugk$fastcgi_script_name; include fastcgi_params; } #设定查看Nginx状态的地址 location /NginxStatus { stub_status on; } } }
名称 | 说明 |
---|---|
$arg_name | 请求中的name参数 |
$args | 请求中的参数 |
$binary_remote_addr | 远程地址的二进制表示 |
$body_bytes_sent | 已发送的消息体字节数 |
$content_length HTTP | 请求信息里的”Content-Length” |
$content_type | 请求信息里的”Content-Type” |
$document_root | 针对当前请求的根路径设置值 |
\(document_uri | 与\)uri相同; 好比 /test2/test.php | |
$host | 请求信息中的”Host”,若是请求中没有Host行,则等于设置的服务器名 |
$hostname | 机器名使用 gethostname系统调用的值 |
$http_cookie | cookie 信息 |
$http_referer | 引用地址 |
$http_user_agent | 客户端代理信息 |
$http_via | 最后一个访问服务器的Ip地址。 |
$http_x_forwarded_for | 至关于网络访问路径 |
$is_args | 若是请求行带有参数,返回“?”,不然返回空字符串 |
$limit_rate | 对链接速率的限制 |
$nginx_version | 当前运行的nginx版本号 |
$pid worker | 进程的PID |
\(query_string | 与\)args相同 | |
\(realpath_root | 按root指令或alias指令算出的当前请求的绝对路径。其中的符号连接都会解析成真是文件路径,使用 Nginx 内置绑定变量 | | 207\)remote_addr | 客户端IP地址 |
$remote_port | 客户端端口号 |
$remote_user | 客户端用户名,认证用 |
$request | 用户请求 |
$request_body | 这个变量(0.7.58+) 包含请求的主要信息。在使用proxy_pass或fastcgi_pass指令的location中比较有意义 |
$request_body_file | 客户端请求主体信息的临时文件名 |
$request_completion | 若是请求成功,设为”OK”;若是请求未完成或者不是一系列请求中最后一部分则设为空 |
$request_filename | 当前请求的文件路径名,好比/opt/nginx/www/test.php |
$request_method | 请求的方法,好比”GET”、”POST”等 |
$request_uri | 请求的URI,带参数 |
$scheme | 所用的协议,好比http或者是https |
$server_addr | 服务器地址,若是没有用listen指明服务器地址,使用这个变量将发起一次系统调用以取得地址(形成资源浪费) |
$server_name | 请求到达的服务器名 |
$server_port | 请求到达的服务器端口号 |
$server_protocol | 请求的协议版本,”HTTP/1.0”或”HTTP/1.1” |
$uri | 请求的URI,可能和最初的值有不一样,好比通过重定向之类的 |
Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。
Lua 是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个研究小组,由Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo所组成并于1993年开发。 —摘抄 http://www.runoob.com/lua/lua-tutorial.html
注意: 在上一篇文章中,OpenResty已经有了Lua的环境,这里安装的是单独的Lua环境,用于学习和开发Lua。大多数的电脑是Windowds版本的电脑,Windows版本下载地址http://luaforge.net/projects/luaforwindows/。
Linux和Mac电脑下载地址:http://luajit.org/download.html,安装命令以下:
wget http://luajit.org/download/LuaJIT-2.1.0-beta1.tar.gz tar -xvf LuaJIT-2.1.0-beta1.tar.gz cd LuaJIT-2.1.0-beta1 make sudo make install
使用IDEA开发的同窗,能够经过安装插件的形式来集成Lua的环境,插件名为EmmyLua,安装插件后,在Idea的右侧栏就会出现Lua的图标,点击图标,就会出现运行Lua代码的窗口。建议使用该插件,能够免去安装Lua环境的麻烦。
安装好环境后,我采用EmmyLua插件的形式,对Lua的入门语法进行一个简单的讲解。 打开EmmyLua的终端,在终端上输入:
print("hi you")
按ctrl+enter,终端显示:
hi you
lua的基本数据类型有nil、string、boolean、number、function类型。
nil相似于Java中的null ,表示空值。变量第一次赋值为nil。
local num print(num) num=100 print(num)
终端输出:
nil
100
Number 类型用于表示实数,和 Java里面的 double 类型很相似。可使用数学函数 math.floor(向下取整) 和 math.ceil(向上取整) 进行取整操做。
local order = 3.99 local score = 98.01 print(math.floor(order)) print(math.ceil(score))
输出:
3
99
Lua 中有三种方式表示字符串: 一、使用一对匹配的单引号。例:’hello’。 二、使用一对匹配的双引号。例:”abclua 3.字符串还能够用一种长括号(即[[ ]]) 括起来的方式定义
ocal str1 = 'hello world' local str2 = "hello lua" local str3 = [["add\name",'hello']] local str4 = [=[string have a [[]].]=] print(str1) -->output:hello world print(str2) -->output:hello lua print(str3) -->output:"add\name",'hello' print(str4) --
Table 类型实现了一种抽象的“关联数组”。“关联数组”是一种具备特殊索引方式的数组,索引一般是字符串(string) 或者 number 类型,但也能够是除 nil 之外的任意类型的值。
local corp = { web = "www.google.com", --索引为字符串,key = "web", -- value = "www.google.com" telephone = "12345678", --索引为字符串 staff = {"Jack", "Scott", "Gary"}, --索引为字符串,值也是一个表 100876, --至关于 [1] = 100876,此时索引为数字 -- key = 1, value = 100876 100191, --至关于 [2] = 100191,此时索引为数字 [10] = 360, --直接把数字索引给出 ["city"] = "Beijing" --索引为字符串 } print(corp.web) -->output:www.google.com print(corp["telephone"]) -->output:12345678 print(corp[2]) -->output:100191 print(corp["city"]) -->output:"Beijing" print(corp.staff[1]) -->output:Jack print(corp[10]) -->output:36
在 Lua 中,函数 也是一种数据类型,函数能够存储在变量中,能够经过参数传递给其余函 数,还能够做为其余函数的返回值。
local function foo() print("in the function") --dosomething() local x = 10 local y = 20 return x + y end local a = foo --把函数赋给变量 print(a()) --output: in the function 30
~= 不等于
逻辑运算符 | 说明 |
---|---|
and | 逻辑与 |
or | 逻辑或 |
not | 逻辑非 |
local c = nil local d = 0 local e = 100 print(c and d) -->打印 nil print(c and e) -->打印 nil print(d and e) -->打印 100 print(c or d) -->打印 0 print(c or e) -->打印 100 print(not c) -->打印 true print(not d) --> 打印 false
在 Lua 中链接两个字符串,可使用操做符“..”(两个点).
print("Hello " .. "World") -->打印 Hello World print(0 .. 1) -->打印 01
单个 if 分支 型
x = 10 if x > 0 then print("x is a positive number") end
两个分支 if-else 型
x = 10 if x > 0 then print("x is a positive number") else print("x is a non-positive number") end
多个分支 if-elseif-else 型:
score = 90 if score == 100 then print("Very good!Your score is 100") elseif score >= 60 then print("Congratulations, you have passed it,your score greater or equal to 60") --此处能够添加多个elseif else print("Sorry, you do not pass the exam! ") end
Lua 提供了一组传统的、小巧的控制结构,包括用于条件判断的 if 用于迭代的 while、repeat 和 for,本章节主要介绍 for 的使用.
for 语句有两种形式:数字 for(numeric for) 和范型 for(generic for) 。 数字型 for 的语法以下:
for var = begin, finish, step do --body end
实例1:
for i = 1, 5 do print(i) end -- output: 1 2 3 4 5
实例2:
for i = 1, 10, 2 do print(i) end -- output: 1 3 5 7 9
泛型 for 循环经过一个迭代器(iterator) 函数来遍历全部值:
-- 打印数组a的全部值 local a = {"a", "b", "c", "d"} for i, v in ipairs(a) do print("index:", i, " value:", v) end -- output: index: 1 value: a index: 2 value: b index: 3 value: c index: 4 value: d
lua的入门就到这里,由于lua语法虽少,但细节有不少,不可能花不少时间去研究这个。入个门,遇到问题再去查资料就好了。另外须要说明的是本文大部份内容为复制粘贴于OPenResty 最佳实践,感谢原做者的开源电子书,让我获益匪浅。更多内容请参考:
lua入门教程:http://www.runoob.com/lua/lua-tutorial.html
OPenResty 最佳实践: https://moonbingbing.gitbooks.io/openresty-best-practices/content/index.html
个人服务器为一台全新的centos 7的服务器,因此从头安装openresty,并记录了安装过程当中出现的问题,以及解决办法。
cd /usr mkdir servers mkdir downloads yum install libreadline-dev libncurses5-dev libpcre3-dev libssl-dev perl cd /usr/servers wget https://openresty.org/download/openresty-1.11.2.4.tar.gz tar -zxvf openresty-1.11.2.4.tar.gz cd /usr/servers/bunble/LuaJIT-2.1-20170405 安装Lua make clean && make && make install
安装过程当中出现如下的错误:
gcc: Command not found
yum -y install gcc automake autoconf libtool make
make clean && make && make install ln -sf luajit-2.1.0-alpha /usr/local/bin/luajit
cd /usr/servers/ngx_openresty–1.11.2.4/bundle
wget https://github.com/FRiCKLE/ngx_cache_purge/archive/2.3.tar.gz
tar -xvf 2.3.tar.gz
cd /usr/servers/ngx_openresty-1.11.2.4/bundle
wget https://github.com/yaoweibin/nginx_upstream_check_module/archive/v0.3.0.tar.gz
tar -xvf v0.3.0.tar.gz
cd /usr/servers/ngx_openresty-1.11.2.4 ./configure --prefix=/usr/servers --with-http_realip_module --with-pcre --with-luajit --add-module=./bundle/ngx_cache_purge-2.3/ --add-module=./bundle/nginx_upstream_check_module-0.3.0/ -j2
提示错误,安装pcre库
yum install -y pcre pcre-devel
<1> gcc 安装 安装 nginx 须要先将官网下载的源码进行编译,编译依赖 gcc 环境,若是没有 gcc 环境,则须要安装:
yum install gcc-c++
<2> PCRE pcre-devel 安装
PCRE(Perl Compatible Regular Expressions) 是一个Perl库,包括 perl 兼容的正则表达式库。nginx 的 http 模块使用 pcre 来解析正则表达式,因此须要在 linux 上安装 pcre 库,pcre-devel 是使用 pcre 开发的一个二次开发库。nginx也须要此库。命令:
yum install -y pcre pcre-devel
<3> zlib 安装 zlib 库提供了不少种压缩和解压缩的方式, nginx 使用 zlib 对 http 包的内容进行 gzip ,因此须要在 Centos 上安装 zlib 库。
yum install -y zlib zlib-devel
<4> OpenSSL 安装 OpenSSL 是一个强大的安全套接字层密码库,囊括主要的密码算法、经常使用的密钥和证书封装管理功能及 SSL 协议,并提供丰富的应用程序供测试或其它目的使用。 nginx 不只支持 http 协议,还支持 https(即在ssl协议上传输http),因此须要在 Centos 安装 OpenSSL 库。
yum install -y openssl openssl-devel
<5>.从新安装OpenResty
cd /usr/servers/ngx_openresty-1.11.2.4 ./configure --prefix=/usr/servers --with-http_realip_module --with-pcre --with-luajit --add-module=./bundle/ngx_cache_purge-2.3/ --add-module=./bundle/nginx_upstream_check_module-0.3.0/ -j2 make && make install
<6>.启动Nginx
/usr/servers/nginx/sbin/nginx
浏览器访问http://116.196.177.123:
Welcome to OpenResty! If you see this page, the OpenResty web platform is successfully installed and working. Further configuration is required. For online documentation and support please refer to openresty.org. Thank you for flying OpenResty.
安装成功了。
vim /usr/servers/nginx/conf/nginx.conf
错误提示没有安装vim
yum -y install vim*
一、在http部分添加以下配置
lua模块路径,多个之间”;”分隔,其中”;;”表示默认搜索路径,默认到/usr/servers/nginx下找
lua_package_path “/usr/servers/lualib/?.lua;;”; #lua 模块
lua_package_cpath “/usr/servers/lualib/?.so;;”; #c模块
二、在nginx.conf中的http部分添加include lua.conf包含此文件片断 Java代码 收藏代码 include lua.conf;
在/usr/server/nginx/conf下
vim lua.conf
#lua.conf server { listen 80; server_name _; location /lua { default_type 'text/html'; content_by_lua 'ngx.say("hello world")'; } }
vim /etc/profile
JAVA_HOME=/usr/local/jdk/jdk1.8.0_144 JRE_HOME=$JAVA_HOME/jre PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin CLASSPATH=:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib/dt.jar export JAVA_HOME JRE_HOME PATH CLASSPATH export PATH=$PATH:/usr/servers/nginx/sbin
source /etc/profile
测试:
nginx -t
nginx: the configuration file /usr/servers/nginx/conf/nginx.conf syntax is ok nginx: configuration file /usr/servers/nginx/conf/nginx.conf test is successful
nginx -s reload
浏览器访问http://116.196.177.123/lua ,浏览器显示:
hello world
mkdir /usr/example cp -r /usr/servers/lualib/ /usr/example/ mkdir /usr/example/lua
cd /usr/example vim example.conf
server { listen 80; server_name _; location /lua { default_type 'text/html'; lua_code_cache off; content_by_lua_file /usr/example/lua/test.lua; } }
vim /usr/example/lua/test.lua
ngx.say("hello world");
cd /usr/servers/nginx/conf/
vim nginx.conf
http模块:
http { include mime.types; default_type application/octet-stream; lua_package_path "/usr/example/lualib/?.lua;;"; #lua 模块 lua_package_cpath "/usr/example/lualib/?.so;;"; #c模块 include /usr/example/example.conf; .... .... }
nginx -t
nginx: [alert] lua_code_cache is off; this will hurt performance in /usr/example/example.conf:7 nginx: the configuration file /usr/servers/nginx/conf/nginx.conf syntax is ok nginx: configuration file /usr/servers/nginx/conf/nginx.conf test is successful
nginx -s reload
浏览器访问http://116.196.177.123/lua ,
hello world
导出history的全部命令:
在你的帐户目录下 输入命令 ls -a 找到 .bash_history 这个就是记录命令文件。 输入命令: cat .bash_history >> history.txt
http://www.linuxidc.com/Linux/2016-09/134907.htm
http://jinnianshilongnian.iteye.com/blog/2186270
https://openresty.org/en/
这篇文章主要讲解OpenResty常见的api。
vim /usr/example/example.conf
location /lua_var { default_type 'text/plain'; content_by_lua_block { ngx.say(ngx.var.arg_a) } }
从新加载nginx配置文件: nginx -s reload
在浏览器上访问http://116.196.177.123/lua_var?a=323,浏览器显示:
323
在上述代码中,涉及到了2个api, 一是ngx.say(直接返回请求结果);二是ngx.var,它是获取请求的参数,好比本例子上的?a=323,获取以后,直接输出为请求结果。
vim /usr/example/example.conf
location /lua_request{ default_type 'text/html'; lua_code_cache off; content_by_lua_file /usr/example/lua/lua_request.lua; }
vim /usr/example/lua/lua_request.lua ,添加一下代码:
local arg = ngx.req.get_uri_args() for k,v in pairs(arg) do ngx.say("[GET ] key:", k, " v:", v) end ngx.req.read_body() -- 解析 body 参数以前必定要先读取 body local arg = ngx.req.get_post_args() for k,v in pairs(arg) do ngx.say("[POST] key:", k, " v:", v) end
在上述例子中有如下的api:
使用curl模拟请求:
curl ‘http://116.196.177.123/lua_request?a=323&b=ss’ -d ‘c=12w&d=2se3’
返回的结果:
[GET ] key:b v:ss [GET ] key:a v:323 [POST] key:d v:2se3 [POST] key:c v:12w
vim /usr/example/lua/lua_request.lua ,在原有的代码基础上,再添加一下代码:
local headers = ngx.req.get_headers() ngx.say("headers begin", "<br/>") ngx.say("Host : ", headers["Host"], "<br/>") ngx.say("user-agent : ", headers["user-agent"], "<br/>") ngx.say("user-agent : ", headers.user_agent, "<br/>") for k,v in pairs(headers) do if type(v) == "table" then ngx.say(k, " : ", table.concat(v, ","), "<br/>") else ngx.say(k, " : ", v, "<br/>") end end
从新加载nginx -s reload
使用curl模拟请求:
curl ‘http://116.196.177.123/lua_request?a=323&b=ss’ -d ‘c=12w&d=2se3’
[GET ] key:b v:ss [GET ] key:a v:323 [POST] key:d v:2se3 [POST] key:c v:12w headers begin<br/> Host : 116.196.77.157<br/> user-agent : curl/7.53.0<br/> user-agent : curl/7.53.0<br/> host : 116.196.77.157<br/> content-type : application/x-www-form-urlencoded<br/> accept : */*<br/> content-length : 12<br/> user-agent : curl/7.53.0<br/>
vim /usr/example/lua/lua_request.lua ,在原有的代码基础上,再添加一下代码:
ngx.say("ngx.req.http_version : ", ngx.req.http_version(), "<br/>") --请求方法 ngx.say("ngx.req.get_method : ", ngx.req.get_method(), "<br/>") --原始的请求头内容 ngx.say("ngx.req.raw_header : ", ngx.req.raw_header(), "<br/>") --请求的body内容体 ngx.say("ngx.req.get_body_data() : ", ngx.req.get_body_data(), "<br/>") ngx.say("<br/>")
从新加载nginx -s reload
使用curl模拟请求:
curl ‘http://116.196.177.123/lua_request?a=323&b=ss’ -d ‘c=12w&d=2se3’
//.... ngx.req.http_version : 1.1<br/> ngx.req.get_method : POST<br/> ngx.req.raw_header : POST /lua_request?a=323&b=ss HTTP/1.1 Host: 116.196.77.157 User-Agent: curl/7.53.0 Accept: */* Content-Length: 12
vim /usr/example/example.conf,添加一个location,代码以下:
location /lua_response{ default_type 'text/html'; lua_code_cache off; content_by_lua_file /usr/example/lua/lua_response.lua ; }
vim /usr/example/lua/lua_response.lua 添加一下代码:
ngx.header.a="1" ngx.header.b={"a","b"} ngx.say("hello","</br>") ngx.print("sss") return ngx.exit(200)
上述代码中有如下api:
使用curl模拟请求, curl ‘http://116.196.177.123/lua_response’ ,获取的响应体以下:
hello sss
在配置文件vim /usr/example/example.conf 加上如下代码:
location /lua_log{ default_type 'text/html'; lua_code_cache off; content_by_lua_file /usr/example/lua/lua_log.lua; }
vim /usr/example/lua/lua_log.lua ,加上如下代码:
local log="i'm log" local num =10 ngx.log(ngx.ERR, "log",log) ngx.log(ngx.INFO,"num:" ,num)
从新加载配置文件nginx -s reload
curl ‘http://116.196.177.123/lua_log’
打开nginx 的logs目录下的error.log 文件:
tail -fn 1000 /usr/servers/nginx/logs/error.log
能够看到在日志文件中已经输出了日志,这种日志主要用于记录和测试。
日志级别:
vim /usr/example/example.conf 添加如下代码:
location /lua_sum{ # 只容许内部调用 internal; # 这里作了一个求和运算只是一个例子,能够在这里完成一些数据库、 # 缓存服务器的操做,达到基础模块和业务逻辑分离目的 content_by_lua_block { local args = ngx.req.get_uri_args() ngx.say(tonumber(args.a) + tonumber(args.b)) } }
internal 关键字,表示只容许内部调用。使用curl模拟请求,请求命令以下:
$ curl ‘http://116.196.177.123/lua_sum?a=1&b=2’
因为该loction是一个内部调用的,外部不能返回,最终返回的结果为404,以下:
<html> <head><title>404 Not Found</title></head> <body bgcolor="white"> <center><h1>404 Not Found</h1></center> <hr><center>openresty/1.11.2.4</center> </body> </html>
vim /usr/example/example.conf 添加如下代码:
location = /lua_sum_test { content_by_lua_block { local res = ngx.location.capture("/lua_sum", {args={a=3, b=8}}) ngx.say("status:", res.status, " response:", res.body) } }
上述的代码经过ngx.location.capture去调用内部的location,并得到返回结果,最终将结果输出,采用curl模拟请求:
$ curl ‘http://116.196.177.123/lua_sum_test’
返回结果以下:
status:200 response:11
vim /usr
location /lua_redirect{ default_type 'text/html'; content_by_lua_file /usr/example/lua/lua_redirect.lua; } ngx.redirect("http://www.fangzhipeng.com", 302)
http://116.196.177.123/lua_redirect
vim /usr/servers/nginx/cong/nginx.conf
在http模块加上如下:
lua_shared_dict shared_data 1m; location /lua_shared_dict{ default_type 'text/html'; content_by_lua_file /usr/example/lua/lua_shared_dict.lua; } local shared_data = ngx.shared.shared_data local i = shared_data:get("i") if not i then i = 1 shared_data:set("i",i) end i = shared_data:incr("i",1) ngx.say("i:",i)
屡次访问 http://116.196.177.123/lua_shared_dict,浏览器打印:
i:1 i:2 i:3 i:4 i:5
如下内容来自于《openresty 最佳实践》
如上图所示,openresty的执行阶段分为
这样咱们就能够根据咱们的须要,在不一样的阶段直接完成大部分典型处理了。
执行阶段概念:
Openresty没有提供默认的Http客户端,须要下载第三方的http客户端。
下载lua-resty-http到lualib目录下,使用如下的命令下载:
cd /usr/example/lualib/resty/ wget https://raw.githubusercontent.com/pintsized/lua-resty-http/master/lib/resty/http_headers.lua wget https://raw.githubusercontent.com/pintsized/lua-resty-http/master/lib/resty/http.lua
lua-resty-http模块的地址为https://github.com/pintsized/lua-resty-http
安装成功后,经过require(“resty.http”)引入 lua_http模块,它有如下的api方法:
vim /usr/example/lua/test_http.lua,写如下代码:
local http = require("resty.http") local httpc = http.new() local resp, err = httpc:request_uri("http://s.taobao.com", { method = "GET", path = "/search?q=hello", headers = { ["User-Agent"] = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.111 Safari/537.36" } }) if not resp then ngx.say("request error :", err) return end ngx.status = resp.status for k, v in pairs(resp.headers) do if k ~= "Transfer-Encoding" and k ~= "Connection" then ngx.header[k] = v end end ngx.say(resp.body) httpc:close()
vim /usr/example/example.conf 加上如下的配置:
location /lua_http { default_type 'text/html'; lua_code_cache on; content_by_lua_file /usr/example/lua/test_http.lua; }
在Nginx的配置文件nginx.conf的http部分,加上如下dns解析:
vim /usr/servers/nginx/conf/nginx.conf
resolver 8.8.8.8;
浏览器访问:http://116.196.177.123/lua_http,浏览器会显示淘宝的搜索页。
Json是一种常见的数据交换格式,经常使用于http通讯协议和其余数据传输领域。在openresty默认内嵌了lua_cjson模块,用来序列化数据。
lua_cjson模块的地址:https://www.kyne.com.au/~mark/software/lua-cjson-manual.html
它经常使用的API以下:
vim /usr/example/lua/test_cjson.lua,添加如下内容:
local cjson = require("cjson") local obj = { id = 1, name = "zhangsan", age = nil, is_male = false, hobby = {"film", "music", "read"} } local str = cjson.encode(obj) ngx.say(str, "<br/>") str = '{"hobby":["film","music","read"],"is_male":false,"name":"zhangsan","id":1,"age":null}' local obj = cjson.decode(str) ngx.say(obj.age, "<br/>") ngx.say(obj.age == nil, "<br/>") ngx.say(obj.age == cjson.null, "<br/>") ngx.say(obj.hobby[1], "<br/>")
vim /usr/example/example.conf添加如下内容:
location ~ /lua_cjson { default_type 'text/html'; lua_code_cache on; content_by_lua_file /usr/example/lua/test_cjson.lua; }
在浏览器上访问http://116.196.177.123/lua_cjson,浏览器显示如下内容:
{"hobby":["film","music","read"],"is_male":false,"name":"zhangsan","id":1} null false true film
Centos系统下安装mysql,先下载mysql-community-release-el7-5.noarch.rpm,而后经过yum安装,安装过程一直肯定【Y】便可。
cd /usr/downloads/ wget http://dev.mysql.com/get/mysql-community-release-el7-5.noarch.rpm rpm -ivh mysql-community-release-el7-5.noarch.rpm yum install mysql-community-server
安装成功后,重启mysql,并进入mysql数据库,给root用户设置一个密码,密码为“123”。
service mysqld restart mysql -u root -p set password for root@localhost = password('123');
lua-resty-mysql模块的官方文档地址: https://github.com/openresty/lua-resty-mysql
lua-resty-mysql - Lua MySQL client driver for ngx_lua based on the cosocket API
lua-resty-mysql模块是基于cosocket API 为ngx_lua提供的一个Lua MySQL客户端。它保证了100%非阻塞。
vim /usr/example/lua/test_mysql.lua,添加如下的代码:
local function close_db(db) if not db then return end db:close() end local mysql = require("resty.mysql") local db, err = mysql:new() if not db then ngx.say("new mysql error : ", err) return end db:set_timeout(1000) local props = { host = "127.0.0.1", port = 3306, database = "mysql", user = "root", password = "123" } local res, err, errno, sqlstate = db:connect(props) if not res then ngx.say("connect to mysql error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate) return close_db(db) end local drop_table_sql = "drop table if exists test" res, err, errno, sqlstate = db:query(drop_table_sql) if not res then ngx.say("drop table error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate) return close_db(db) end local create_table_sql = "create table test(id int primary key auto_increment, ch varchar(100))" res, err, errno, sqlstate = db:query(create_table_sql) if not res then ngx.say("create table error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate) return close_db(db) end local insert_sql = "insert into test (ch) values('hello')" res, err, errno, sqlstate = db:query(insert_sql) if not res then ngx.say("insert error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate) return close_db(db) end res, err, errno, sqlstate = db:query(insert_sql) ngx.say("insert rows : ", res.affected_rows, " , id : ", res.insert_id, "<br/>") local update_sql = "update test set ch = 'hello2' where id =" .. res.insert_id res, err, errno, sqlstate = db:query(update_sql) if not res then ngx.say("update error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate) return close_db(db) end ngx.say("update rows : ", res.affected_rows, "<br/>") local select_sql = "select id, ch from test" res, err, errno, sqlstate = db:query(select_sql) if not res then ngx.say("select error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate) return close_db(db) end for i, row in ipairs(res) do for name, value in pairs(row) do ngx.say("select row ", i, " : ", name, " = ", value, "<br/>") end end ngx.say("<br/>") local ch_param = ngx.req.get_uri_args()["ch"] or '' local query_sql = "select id, ch from test where ch = " .. ngx.quote_sql_str(ch_param) res, err, errno, sqlstate = db:query(query_sql) if not res then ngx.say("select error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate) return close_db(db) end for i, row in ipairs(res) do for name, value in pairs(row) do ngx.say("select row ", i, " : ", name, " = ", value, "<br/>") end end local delete_sql = "delete from test" res, err, errno, sqlstate = db:query(delete_sql) if not res then ngx.say("delete error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate) return close_db(db) end ngx.say("delete rows : ", res.affected_rows, "<br/>") close_db(db)
在上面的代码中,展现了基本的创表、插入数据、修改数据、查询数据、删除数据的一些功能。
其中用到的lua-resty-mysql的一些API方法:
lua-resty-mysql的一些关键的API方法,见https://github.com/openresty/lua-resty-mysql#table-of-contents
vim /usr/example/example.conf 在配置文件配置:
location /lua_mysql { default_type 'text/html'; lua_code_cache on; content_by_lua_file /usr/example/lua/test_mysql.lua; }
浏览器访问http://116.196.177.123/lua_mysql,浏览器显示如下的内容:
insert rows : 1 , id : 2 update rows : 1 select row 1 : ch = hello select row 1 : id = 1 select row 2 : ch = hello2 select row 2 : id = 2 delete rows : 2
在实际的开发过程当中,不可能把全部的lua代码写在一个lua文件中,一般的作法将特定功能的放在一个lua文件中,即用lua模块开发。在lualib目录下,默认有如下的lua模块。
lualib/ ├── cjson.so ├── ngx │ ├── balancer.lua │ ├── ocsp.lua │ ├── re.lua │ ├── semaphore.lua │ ├── ssl │ │ └── session.lua │ └── ssl.lua ├── rds │ └── parser.so ├── redis │ └── parser.so └── resty ├── aes.lua ├── core │ ├── base64.lua │ ├── base.lua │ ├── ctx.lua │ ├── exit.lua │ ├── hash.lua │ ├── misc.lua │ ├── regex.lua │ ├── request.lua │ ├── response.lua │ ├── shdict.lua │ ├── time.lua │ ├── uri.lua │ ├── var.lua │ └── worker.lua ├── core.lua ├── dns │ └── resolver.lua ├── limit │ ├── conn.lua │ ├── req.lua │ └── traffic.lua ├── lock.lua ├── lrucache │ └── pureffi.lua ├── lrucache.lua ├── md5.lua ├── memcached.lua ├── mysql.lua ├── random.lua ├── redis.lua ├── sha1.lua ├── sha224.lua ├── sha256.lua ├── sha384.lua ├── sha512.lua ├── sha.lua ├── string.lua ├── upload.lua ├── upstream │ └── healthcheck.lua └── websocket ├── client.lua ├── protocol.lua └── server.lua
在使用这些模块以前,须要在nginx的配置文件nginx.conf中的http模块加上如下的配置:
lua_package_path "/usr/example/lualib/?.lua;;"; #lua 模块 lua_package_cpath "/usr/example/lualib/?.so;;"; #c模块
如今来简单的开发一个lua模块:
vim /usr/example/lualib/module1.lua
在module1.lua文件加上如下的代码:
local count = 0 local function hello() count = count + 1 ngx.say("count : ", count) end local _M = { hello = hello } return _M
开发时将全部数据作成局部变量/局部函数;经过 _M导出要暴露的函数,实现模块化封装。
在/usr/example/lua目录下建立一个test_module_1.lua 文件,在该文件中引用上面的module1.lua文件。
vim /usr/example/lua/test_module_1.lua
加上如下代码:
local module1 = require("module1") module1.hello()
经过require(“模块名”)来加载模块,若是是多级目录,则须要经过require(“目录1.目录2.模块名”)加载。
在/user/example/example.conf中加上如下的配置:
location /lua_module_1 { default_type 'text/html'; lua_code_cache on; content_by_lua_file /usr/example/lua/test_module_1.lua; }
屡次在浏览器上访问:http://116.196.177.123/lua_module_1,浏览器显示:
count : 1 count : 2 count : 3 ...
linux下安装: cd /usr/servers
$ wget http://download.redis.io/releases/redis-3.2.6.tar.gz $ tar xzf redis-3.2.6.tar.gz $ cd redis-3.2.6 $ make
启动redis:
nohup /usr/servers/redis-3.2.6/src/redis-server /usr/servers/redis-3.2.6/redis.conf &
查看是否启动:
ps -ef |grep redis
终端显示:
root 20985 14268 0 18:49 pts/0 00:00:00 /usr/servers/redis-3.2.6/src/redis-server 127.0.0.1:6379
可见redis已经启动。
lua_resty_redis模块地址:https://github.com/openresty/lua-resty-redis
lua-resty-redis - Lua redis client driver for the ngx_lua based on the cosocket API
lua_resty_redis 它是一个基于cosocket API的为ngx_lua模块提供Lua redis客户端的驱动。
建立一个test_redis_basic.lua文件
vim /usr/example/lua/test_redis_basic.lua
local function close_redis(red) if not red then return end local pool_max_idle_time = 10000 --毫秒 local pool_size = 100 --链接池大小 local ok, err = red:set_keepalive(pool_max_idle_time, pool_size) if not ok then ngx.say("set keepalive error : ", err) end end local redis = require("resty.redis") local red = redis:new() red:set_timeout(1000) local ip = "127.0.0.1" local port = 6379 local ok, err = red:connect(ip, port) if not ok then ngx.say("connect to redis error : ", err) return close_redis(red) end ok, err = red:set("msg", "hello world") if not ok then ngx.say("set msg error : ", err) return close_redis(red) end local resp, err = red:get("msg") if not resp then ngx.say("get msg error : ", err) return close_redis(red) end if resp == ngx.null then resp = '' end ngx.say("msg : ", resp) close_redis(red)
上面的代码很简单,经过链接池链接Redis,链接上redis后,经过set一对键值对(msg,helloword)到redis中,而后get(msg),并经过ngx.say()返回给浏览器。
vim /usr/example/example.conf,添加如下的配置代码:
location /lua_redis_basic { default_type 'text/html'; lua_code_cache on; content_by_lua_file /usr/example/lua/test_redis_basic.lua; }
浏览器访问:http://116.196.177.123/lua_redis_basic
浏览器显示:
msg : hello world
lua_resty_redis支持全部的redis指令,自己Redis就支持lua语言操做。因此lua_resty_redis模块可以提升全部的redis操做的功能。
在不少时候,Redis是设置了口令的,链接时,若是须要验证口令,须要添加 local res, err = red:auth(“foobared”),示例代码以下:
local redis = require "resty.redis" local red = redis:new() red:set_timeout(1000) -- 1 sec local ok, err = red:connect("127.0.0.1", 6379) if not ok then ngx.say("failed to connect: ", err) return end local res, err = red:auth("foobared") if not res then ngx.say("failed to authenticate: ", err) return end
更多请关注的官方文档https://github.com/openresty/lua-resty-redis 和开涛的博客http://jinnianshilongnian.iteye.com/blog/2187328
RBAC(Role-Based Access Control,基于角色的访问控制),用户基于角色的访问权限控制。简单地说,一个用户拥有若干角色,每个角色拥有若干权限。这样,就构形成“用户-角色-权限”的受权模型。在这种模型中,用户与角色之间,角色与权限之间,通常都是多对多的关系。如图所示:
在本案例中,采用的就是这种权限设计的方式。具体的sql语句脚本以下:
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT , `name` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL , PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARACTER SET=latin1 COLLATE=latin1_swedish_ci AUTO_INCREMENT=2 ROW_FORMAT=COMPACT ; CREATE TABLE role( `id` int(11) NOT NULL AUTO_INCREMENT , `name` varchar(255) CHARACTER SET latin5 NULL DEFAULT NULL , PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARACTER SET=latin1 COLLATE=latin1_swedish_ci AUTO_INCREMENT=2 ROW_FORMAT=COMPACT ; CREATE TABLE permission( `id` int(11) NOT NULL AUTO_INCREMENT , `permission` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL , PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARACTER SET=latin1 COLLATE=latin1_swedish_ci AUTO_INCREMENT=3 ROW_FORMAT=COMPACT ; CREATE TABLE user_role( `id` int(11) NOT NULL AUTO_INCREMENT , `user_id` int(11) NULL DEFAULT NULL , `role_id` int(11) NULL DEFAULT NULL , PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARACTER SET=latin1 COLLATE=latin1_swedish_ci AUTO_INCREMENT=2 ROW_FORMAT=COMPACT ; CREATE TABLE role_permission( `id` int(11) NOT NULL AUTO_INCREMENT , `role_id` int(11) NULL DEFAULT NULL , `permission_id` int(11) NULL DEFAULT NULL , PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARACTER SET=latin1 COLLATE=latin1_swedish_ci AUTO_INCREMENT=3 ROW_FORMAT=COMPACT ;
初始化如下的sql脚本,即给用户id为1的用户关联角色,角色并关联权限:
INSERT INTO `permission` VALUES ('1', '/user/orgs'); INSERT INTO `role` VALUES ('1', 'user'); INSERT INTO `role_permission` VALUES ('1', '1', '1'); INSERT INTO `user` VALUES ('1', 'forezp'); INSERT INTO `user_role` VALUES ('1', '1', '1');
在本案例中,须要根据user表中的Id获取该Id对应的权限。首先根据userId获取该用户对应的角色,再根据根据该角色获取相应的权限,每每一个用户具备多个角色,而角色又有多个权限。好比查询userId为1 的用户的权限的sql语句以下:
SELECT a.id,a.permission from permission a ,role_permission b,role c,user_role d,user e WHERE a.id=b.permission_id and c.id=b.role_id and d.role_id=c.id and d.user_id=e.id and e.id=1"
在Openresty中怎么链接数据库,怎么查询sql语句,在以前的文章已将讲述过了。根据用户id获取用户的权限的功能是一个使用率极高的功能,因此考虑将这个功能模块化。
vim /usr/example/lualib/sql_tool.lua ,编辑加入如下的代码:
local mysql = require("resty.mysql") local function close_db(db) if not db then return end db:close() end local function select_user_permission(user_id) local db, err = mysql:new() if not db then ngx.say("new mysql error : ", err) return end db:set_timeout(1000) local props = { host = "127.0.0.1", port = 3306, database = "test", user = "root", password = "123" } local res, err, errno, sqlstate = db:connect(props) if not res then ngx.say("connect to mysql error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate) close_db(db) end local select_sql = "SELECT a.id,a.permission from permission a ,role_permission b,role c,user_role d,user e WHERE a.id=b.permission_id and c.id=b.role_id and d.role_id=c.id and d.user_id=e.id and e.id="..user_id res, err, errno, sqlstate = db:query(select_sql) if not res then ngx.say("select error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate) return close_db(db) end local permissions={} for i, row in ipairs(res) do for name, value in pairs(row) do if name == "permission" then table.insert(permissions, 1, value) end end end return permissions end local _M = { select_user_permission= select_user_permission } return _M
在上面的代码中,有一个select_user_permission(user_id)方法,该方法根据用户名获取该用户的权限。查出来存在一个table 类型的 local permissions={}中。
vim /usr/example/example.conf 加上如下的代码:
location ~ /sql_tool{ default_type 'text/html'; content_by_lua_file /usr/example/lua/test_sql_tool.lua; }
在浏览器上访问http://116.196.177.123/sql_tool,浏览器显示以下的内容:
/user/orgs
在以前的文章讲述了如何使用Openresty链接redis,并操做redis。 这小节将讲述如何使用openresty链接redis,并写几个方法,用于存储用户的token等,并将这些信息模块化,主要有如下几个方法:
vim /usr/example/lualib/tokentool.lua 编辑一下内容:
module("tokentool", package.seeall) local redis = require "resty.redis" local str = require "resty.string" local cjson = require("cjson") local redis_host = "127.0.0.1" local redis_port = 6379 local function close_redis(red) if not red then return end local pool_max_idle_time = 10000 --毫秒 local pool_size = 100 --链接池大小 local ok, err = red:set_keepalive(pool_max_idle_time, pool_size) if not ok then ngx.say("set keepalive error : ", err) end end local function connect() local red = redis:new() red:set_timeout(1000) local ok, err = red:connect(redis_host, redis_port) if not ok then return false end --local res, err = red:auth("xiaoantimes") --if not res then -- ngx.say("failed to authenticate: ", err) -- return false --end --ok, err = red:select(1) --if not ok then -- return false --end return red end function has_token(token) local red = connect() if red == false then return false end local res, err = red:get(token) if not res then return false end close_redis(red) return true end function set_permissions(user_id,permissions) if (permissions==null) or( permissions==ngx.null) then return false end local str = cjson.encode(permissions) ngx.log(ngx.ERR,"set redis p:"..str) local red=connect() if red== false then return false end local ok, err = red:set(user_id,str) if not ok then return false end return true end function get_permissions(user_id) local red=connect() if red== false then return false end local res, err = red:get(user_id) if (not res) or (res == ngx.null) then return end ngx.log(ngx.ERR,"get redis p:"..res); local permissions=cjson.decode(res) return permissions end function get_user_id(token) local red = connect() local resp, err = red:get(token) if not resp then ngx.say("get msg error : ", err) return close_redis(red) end close_redis(red) return resp end
vim /usr/example/lua/test_token_tool.lua,加上如下的内容:
local tokentool= require "tokentool" local ret = tokentool.has_token("msg") ngx.log(ngx.ERR,ret) if ret == true then ngx.say("ok") else ngx.say("oops,error") end
在/usr/example/example.conf加上如下的内容:
location ~ /token_tool{ default_type 'text/html'; lua_code_cache on; content_by_lua_file /usr/example/lua/test_token_tool.lua; }
打开浏览器访问http://116.196.177.123/token_tool,浏览器显示:
ok
采用openresty 开发出的api网关有不少,好比比较流行的kong、orange等。这些API 网关经过提供插件的形式,提供了很是多的功能。这些组件化的功能每每可以知足大部分的需求,若是要想达到特定场景的需求,可能须要二次开发,好比RBAC权限系统。本小节经过整合前面的知识点,来构建一个RBAC权限认证系统。
本小节采用了如下的技术栈:
验证流程图以下所示:
vim /usr/example/example.conf ,加上如下的配置:
location / { default_type "text/html"; access_by_lua_file /usr/example/lua/api_access.lua; content_by_lua_file /usr/example/lua/api_content.lua; }
以上的配置表示,要不符合已有location路径的全部请求,将走这个location为/ 的路径。符合这个location的请求将进入 access_by_lua_file和 content_by_lua_file的模块判断。
vim /usr/example/lua/access_by_lua_file ,加上如下代码:
local tokentool = require "tokentool" local mysqltool = require "mysqltool" function is_include(value, tab) for k,v in ipairs(tab) do if v == value then return true end end return false end local white_uri={"/user/login","/user/validate"} --local user_id = ngx.req.get_uri_args()["userId"] --获取header的token值 local headers = ngx.req.get_headers() local token=headers["token"] local url=ngx.var.uri if ( not token) or (token==null) or (token ==ngx.null) then if is_include(url,white_uri)then else return ngx.exit(401) end else ngx.log(ngx.ERR,"token:"..token) local user_id=tokentool.get_user_id(token) if (not user_id) or( user_id ==null) or ( user_id == ngx.null) then return ngx.exit(401) end ngx.log(ngx.ERR,"user_id"..user_id) local permissions={} permissions =tokentool.get_permissions(user_id) if(not permissions)or(permissions==null)or( permissions ==ngx.null) then permissions= mysqltool.select_user_permission(user_id) if permissions and permissions ~= ngx.null then tokentool.set_permissions(user_id,permissions) end end if(not permissions)or(permissions==null)or( permissions ==ngx.null) then return ngx.exit(401) end local is_contain_permission = is_include(url,permissions) if is_contain_permission == true then -- ngx.say("congratuation! you have pass the api gateway") else return ngx.exit(401) end end
在上述代码中:
若是全部的判断经过,则该用户请求的具备权限访问,则进入content_by_lua_file模块,直接在这个模块给请求返回“congratulations! you have passed the api gateway”。
vim /usr/example/lua/api_content.lua ,添加如下内容:
ngx.say("congratulations!"," you have passed ","the api gateway") ----200状态码退出 return ngx.exit(200)
打开浏览器访问http://116.196.177.123/user/login,浏览器显示:
congratulations! you have passed the api gateway
/user/login这个url 在白名单的范围内,因此它是能够经过权限验证的。
打开浏览器访问http://116.196.177.123/user/sss,显示如下内容:
401 Authorization Required
openresty/1.11.2.4
在redis中添加一对key-value,key为token_forezp,value为1,即token_forezp对应的用户的id为1.
/usr/servers/redis-3.2.6 src/redis-cli set token_forezp 1
初始化如下的sql脚本,即给用户id为1的用户关联角色,角色并关联权限:
INSERT INTO `permission` VALUES ('1', '/user/orgs'); INSERT INTO `role` VALUES ('1', 'user'); INSERT INTO `role_permission` VALUES ('1', '1', '1'); INSERT INTO `user` VALUES ('1', 'forezp'); INSERT INTO `user_role` VALUES ('1', '1', '1');
用postman请求,在请求头中加入token,值为token_forezp,请求结果以下: