nginx
nginx是俄罗斯人写的轻量级http服务器,Nginx 以事件驱动的方式编写,有非常好的性能,同时也是一个非常高效的反向代理、负载均衡。
Nginx 稳定性高,模块库丰富,配置灵活,系统资源的消耗低。响应静态页面的速度非常快
nginx 做什么
- 处理静态文件
- 反向代理,负载均衡和容错
- 大并发
- 易配置,易拓展
nginx 处理过程
nginx 是异步非阻塞的方式处理请求。采用epoll事件循环,多个独立worker处理请求,并不是并发,避免加锁和上下文切换带来的性能问题。
具体请求的处理
- 解析配置文件, 得到需要监听的端口与 ip 地址,然后在 Nginx 的 master 进程里面,先初始化好这个监控的 socket
- 然后再 fork 出多个子进程出来,然后子进程会竞争 accept 新的连接
- 与客户端三次握手得到这个建立好的连接的 socket,然后创建 Nginx 对连接的封装
- Nginx 或客户端来主动关掉连接
nginx的配置
分为几个模块:
- main: Nginx 在运行时与具体业务功能(比如http服务或者email服务代理)无关的一些参数,比如工作进程数,运行的身份等。
- http: 与提供 http 服务相关的一些配置参数。例如:是否使用 keepalive 啊,是否使用gzip进行压缩等。
- server: http 服务上支持若干虚拟主机。每个虚拟主机一个对应的 server 配置项,配置项里面包含该虚拟主机相关的配置。
- location: http 服务中,某些特定的URL对应的一系列配置项。
默认情况下,这个配置文件通常命名为 nginx.conf 并且会放置在 /usr/local/nginx/conf,/etc/nginx,或者 /usr/local/etc/nginx
示例配置
user nobody;
worker_processes 1;
error_log logs/error.log info;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
gzip on;
server {
listen 80;
server_name localhost;
access_log /var/log/nginx/host.access.log main;
location / {
index index.html;
root /usr/local/openresty/nginx/html;
}
}
}
nginx 的使用
开启nginx
nginx -c /usr/local/nginx/conf/nginx.conf
nginx -s signal
signal 可以为下列命令之一:
- stop — 直接关闭 nginx
- quit — 会在处理完当前正在的请求后退出,也叫优雅关闭
- reload — 重新加载配置文件,相当于重启 // 滚动升级
- reopen — 重新打开日志文件
nginx with lua -- openresty
OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。
通过融合lua,可以将 Nginx 有效地变成一个强大的web服务器!
环境搭建
直接搭建openresty, nginx + lua 套餐
brew tap homebrew/nginx
brew install homebrew/nginx/openresty
如果一切顺利,OpenResty 应该已经安装好了。
为了方便,这边直接用docker装OpenResty:
新建一个Dockerfile,写入:
#Dockerfile
FROM openresty/openresty:trusty
RUN apt-get update && apt-get install -y vim
可以直接装openresty的,但是容器的bash里面没有vim,在里面改代码很麻烦,所以就自己构建了一个image。
然后构建image
docker build -t openresty .
docker container run -itd -p 80:80 --name test openresty
构建一个名叫test的容器。
ok,现在已经跑起来了,访问http://localhost,已经出现了openresty的欢迎页
运行
docker ps -a
可以看到:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
06c9a63607eb openresty "/usr/local/openrest…" 4 hours ago Up 25 minutes 0.0.0.0:80->80/tcp test
已经起了服务。
我们进入容器看一下:
docker exec -it test bash
里面就是nginx的目录。
我们的配置在
vi usr/local/openresty/nginx/conf/nginx.conf
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}
其中引入了/etc/nginx/conf.d/*.conf;我们的server配置就在这里。
现在该这个etc/nginx/conf.d/default.conf就可以愉快的进行nginx配置了。
location / {
root /usr/local/openresty/nginx/html;
index index.html index.htm;
}
我们访问的主页目录就在root指定下的index.html,root是指定根目录,index指定默认访问文件。
如何结合lua
lua提供了好多个指令,比如:
- content_by_lua_file 直接加lua file的路径,接受请求,并处理响应。
- lua_package_path 设置用lua代码写的扩展库路径,lua文件里require要用到。
set_by_lua_file $var
- rewrite_by_lua_file lua文件的重定向操作
- access_by_lua_file lua文件的访问控制
- header_filter_by_lua_file 设置header 和 cookie
- init_by_lua_file ginx Master进程加载配置时执行;通常用于初始化全局配置/预加载Lua模块
...
lua常用的方法和常量:
ngx.arg[index] #ngx指令参数,当这个变量在set_by_lua或者set_by_lua_file内使用的时候是只读的,指的是在配置指令输入的参数
ngx.var.varname #读写NGINX变量的值,最好在lua脚本里缓存变量值,避免在当前请求的生命周期内内存的泄漏
ngx.config.ngx_lua_version #当前ngx_lua模块版本号
ngx.config.nginx_version #nginx版本
ngx.worker.pid #当前worker进程的PID
...
print() #与 ngx.print()方法有区别,print() 相当于ngx.log()
ngx.ctx #这是一个lua的table,用于保存ngx上下文的变量,在整个请求的生命周期内都有效,详细参考官方
ngx.location.capture() #发出一个子请求
ngx.location.capture_multi() #发出多个子请求
ngx.status #读或者写当前请求的相应状态. 必须在输出相应头之前被调用
ngx.header.HEADER #访问或设置http header头信息
ngx.req.set_uri() #设置当前请求的URI
ngx.set_uri_args(args) #根据args参数重新定义当前请求的URI参数
ngx.req.get_uri_args() #返回一个lua table,包含当前请求的全部的URL参数
ngx.req.get_post_args() #返回一个LUA TABLE,包括所有当前请求的POST参数
ngx.req.get_headers() #返回一个包含当前请求头信息的lua table
ngx.req.set_header() #设置当前请求头header某字段值.当前请求的子请求不会受到影响
ngx.req.read_body() #在不阻塞ngnix其他事件的情况下同步读取客户端的body信息
ngx.time() #返回当前时间戳
ngx.re.match(subject,regex,options,ctx) #ngx正则表达式匹配
...
开始openresty
在etc/nginx/conf.d/default.conf的location下面加一句
location /test {
default_type text/html;
content_by_lua_block {
ngx.say("HelloWorld")
}
}
重启nginx nginx -s reload
访问http://localhost/test可以看到我们写的helloworld。
再比如
location = /sum {
# 只允许内部调用
internal;
# 这里做了一个求和运算只是一个例子,可以在这里完成一些数据库、
# 缓存服务器的操作,达到基础模块和业务逻辑分离目的
content_by_lua_block {
local args = ngx.req.get_uri_args()
ngx.say(tonumber(args.a) + tonumber(args.b))
}
}
location = /app/test {
content_by_lua_block {
local res = ngx.location.capture(
"/sum", {args={a=3, b=8}}
)
ngx.say("status:", res.status, " response:", res.body)
}
}
请求内部接口,返回结果。
比如:
lua可以帮我们做跳转。重写url等等。
location = /stream {
rewrite_by_lua_block {
return ngx.redirect('http://open.toutiao.com');
}
}
比如
location /print_param {
default_type text/html;
content_by_lua_block {
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
}
}
访问http://localhost/print_param?a=1&b=2&c=3
可以看到[GET ] key:b v:2 [GET ] key:a v:1 [GET ] key:c v:3
location /res {
default_type text/html;
local table = {
"hello, ",
{"world: ", true, " or ", false,
{": ", nil}}
}
ngx.print(table)
}
ngx.print突出碎片化字符串。hello, world: true or false:
location /res {
content_by_lua_block {
ngx.header['Content-Type'] = 'text/json; charset=UTF-8'
local str = '{"a":1,"b":"ss","c":{"c1":1,"c2":2},"d":[10,11],"1":100}'
ngx.say(str)
}
}
返回json,lua好像没有类似js的json对象。。