nginx
计算机
正向代理:国内访问国外就需要代理服务器 隐藏客户端真实ip
反向代理:一个接待对应多个服务器 隐藏服务端真实ip 负载均衡 nginx
配置文件解读
location
模式 | 含义 | 例子 |
---|---|---|
=/uri | 精确匹配 | |
^~/uri | 前缀匹配 | |
~pattern | 开头区分大小写 | |
~*pattern | 开头不区分大小写 | |
/uri | ||
/ | default |
# 负载均衡
upstream myserver {
<!-- 权重 -->
ip_hash;
server ip:8081 weight=10;
server ip2:8081 backup;
}
ip_hash 根据请求ip hash 访问固定一个客户端
least_conn 最少链接数
fair; 按招后台响应时间分配
backup 预留备份服务器
max_fails 请求失败次数
fail_timeout 请求失败达到次数后, 暂停服务时间
# 代理
location / {
proxy_pass http://myserver
proxy_set_header host $host;
proxy_set_header X-Fowarded-For $remote_addr;
}
location /hello/ {
proxy_pass http://server1;
}
proxy_pass http://server1 会访问到 http://server1/hello/
proxy_pass http://server2/ 会访问到 http://server2
# 限流
# 令牌桶 允许突发流量
limit_req_zone 限制单位时间内的请求数, 漏桶算法 不允许突发流量
http {
limit_req_zone 192.168.1.1 zone=myLimit:10m rate=5r/s;
}
# 限制 1s 内访问 5 次
# 突发流量限制访问频率 limit_req zone=myLimit burst=20 nodelay; 特事特办
server {
listen 80;
server_name server.com;
location / {
limit_req zone=myLimit;
rewrite / http://www.baidu.com permanent;
}
}
# 控制冰法连接数
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn_zone $server_name zone=perserver:10m;
server {
...
limit_conn perip 10;
limit_conn perserver 100;
}
limit_conn perip 10 作用的key 是 $binary_remote_addr,表示限制单个IP同时最多能持有10个连接。
limit_conn perserver 100 作用的key是 $server_name,表示虚拟主机(server) 同时能处理并发连接的总数
# 正常限流设置
limit_req_zone $limit_key zone=myRateLimit:10m rate=10r/s;
server {
...
limit_conn perip 10;
limit_conn perserver 100;
}
limit_req_conn 限制同一时间连接数,即并发限制
为什么要用单线程?
采用单线程来异步非阻塞处理请求(管理员可以配置Nginx主进程的工作进程的数量),不会为每个请求分配cpu和内存资源,节省了大量资源,同时也减少了大量的CPU的上下文切换,所以才使得Nginx支持更高的并发。
简单过程: 主程序 Master process 启动后,通过一个 for 循环来 接收 和 处理外部信号; 主进程通过 fork() 函数产生 worker 子进程 ,每个子进程执行一个for循环来实现Nginx服务器对事件的接收和处理 。 Master 接收 Worker 处理 详细过程:
- Nginx 在启动后,会有一个 master进程和多个相互独立的 worker 进程。
- master 接收来自外界的信号,先建立好需要 listen 的 socket(listenfd) 之后,然后再 fork 出多个 worker 进程,然后向各worker进程发送信号,每个进程都有可能来处理这个连接。
- 所有 worker 进程的 listenfd 会在新连接到来时变得可读 ,为保证只有一个进程处理该连接,所有 worker 进程在注册 listenfd 读事件前抢占 accept_mutex ,抢到互斥锁的那个进程注册 listenfd 读事件 ,在读事件里调用 accept 接受该连接。
- 当一个 worker 进程在 accept 这个连接之后,就开始读取请求、解析请求、处理请求,产生数据后,再返回给客户端 ,最后才断开连接。
为什么说,Nginx 基于事件模型:
每进来一个 request ,会有一个 worker 进程去处理。但不是全程的处理,处理到什么程度呢?处理到可能发生阻塞的地方,比如向上游(后端)服务器转发 request ,并等待请求返回。那么,这个处理的 worker 不会这么傻等着,他会在发送完请求后,注册一个事件:如果 upstream 返回了,告诉我一声,我再接着干。于是他就休息去了。此时,如果再有 request 进来,他就可以很快再按这种方式处理。而一旦上游服务器返回了,就会触发这个事件,worker 才会来接手,这个 request 才会接着往下走。 使用进程的好处:各个进程之间相互独立,不需要加锁
由于 web server 的工作性质决定了每个 request 的大部份生命都是在网络传输中,实际上花费在 server 机器上的时间片不多。这是几个进程就解决高并发的秘密所在。即: webserver 刚好属于网络 IO 密集型应用,不算是计算密集型。
异步,非阻塞,使用 epoll ,和大量细节处的优化。也正是 Nginx 之所以然的技术基石。
nginx命令行
nginx -s stop :快速关闭Nginx,可能不保存相关信息,并迅速终止web服务。
nginx -s quit :平稳关闭Nginx,保存相关信息,有安排的结束web服务。
nginx -s reload :因改变了Nginx相关配置,需要重新加载配置而重载。
nginx -s reopen :重新打开日志文件。
nginx -c filename :为 Nginx 指定一个配置文件,来代替缺省的。
nginx -t :不运行,而仅仅测试配置文件。nginx 将检查配置文件的语法的正确性,并尝试打开配置文件中所引用到的文件。
nginx -v:显示 nginx 的版本。
nginx -V:显示 nginx 的版本,编译器版本和配置参数。
ps -ef | grep nginx 查看运行
网站有多个webapp的配置
http {
#此处省略一些基本配置
upstream product_server{
server www.mxz.com:8081;
}
upstream admin_server{
server www.mxz.com:8082;
}
upstream finance_server{
server www.mxz.com:8083;
}
server {
#此处省略一些基本配置
#默认指向product的server
location / {
proxy_pass http://product_server;
}
location /product/{
proxy_pass http://product_server;
}
location /admin/ {
proxy_pass http://admin_server;
}
location /finance/ {
proxy_pass http://finance_server;
}
}
}
获取uri参数
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
}
curl ‘127.0.0.1:6699/a?a=1&b=2%26’ -d ‘c=3&d=4%26’
传递请求uri参数
nginx 设置错误级别
error_log logs/error.log error; # 日志级别
ngx.log(ngx.ERR, "num:", num)
nginx 黑名单
#使用access阶段完成准入阶段处理
access_by_lua_block {
ngx.log(ngx.ERR, "common", ngx.var.remote_addr)
local black_ips = {["127.0.0.1"]=true}
local ip = ngx.var.remote_addr
if true == black_ips[ip] then
ngx.exit(ngx.HTTP_FORBIDDEN)
end
ngx.var.limit_rate = 1000
}
参数变量
$args:这个变量等于请求行中的参数,同$query_string。 $is_args: 如果已经设置$args,则该变量的值为”?”,否则为""。 $content_length: 请求头中的Content-length字段。 $content_type: 请求头中的Content-Type字段。 $document_uri: 与$uri相同。 $document_root: 当前请求在root指令中指定的值。 $host: 请求主机头字段,否则为服务器名称。 服务端地址 $http_user_agent: 客户端agent信息。 $http_cookie: 客户端cookie信息。 $limit_rate: 这个变量可以限制连接速率。 $request_method: 客户端请求的动作,通常为GET或POST。 $remote_addr: 客户端的IP地址。 $remote_port: 客户端的端口。 $remote_user: 已经经过Auth Basic Module验证的用户名。 $request_body_file`: 客户端请求主体的临时文件名。 $request_uri: 请求的URI,带参数 $request_filename: 当前请求的文件路径,由root或alias指令与URI请求生成。 $scheme: 所用的协议,比如http或者是https,比如rewrite ^(.+)$ $scheme://example.com$1 redirect;。 $server_protocol: 请求使用的协议,通常是HTTP/1.0或HTTP/1.1。 $server_addr: 服务器地址,在完成一次系统调用后可以确定这个值。 $server_name: 服务器名称。 $server_port: 请求到达服务器的端口号。 $request_uri: 包含请求参数的原始URI,不包含主机名,如:/foo/bar.php?arg=baz,它无法修改。 $uri: 不带请求参数的当前URI,$uri不包含主机名,如/foo/bar.html可能和最初的值有不同,比如经过重定向之类的。它可以通过内部重定向,或者使用index指令进行修改。不包括协议和主机名,例如/foo/bar.html。 $arg_mxz 取query值