2015年10月22日

Nginx+lua


本文介绍nginx+lua

nginx相关资料

一. 日志

1. 日志配置

语法: access_log path [format [buffer=size [flush=time]]];
access_log path format gzip[=level] [buffer=size] [flush=time];
access_log syslog:server=address[,parameter=value] [format];
access_log off;
默认值: access_log logs/access.log combined;
配置段: http, server, location, if in location, limit_except
gzip压缩等级。
buffer设置内存缓存区大小。
flush保存在缓存区中的最长时间。
不记录日志:access_log off;
使用默认combined格式记录日志:access_log logs/access.log 或 access_log logs/access.log combined;
语法: error_log file | stderr | syslog:server=address[,parameter=value] [debug | info | notice | warn | error | crit | alert | emerg];
默认值: error_log logs/error.log error;
配置段: main, http, server, location
配置错误日志。

stderr (0)>= emerg(1) >= alert(2) >= crit(3) >= err(4)>= warn(5) >= notice(6) >= info(7) >= debug(8)
debug级别最低,stderr级别最高;圆括号中的数据是对应日志等级的值。
语法: log_format name string …;
默认值: log_format combined “…”;
配置段: http
log_format  main  '$server_name $remote_addr - $remote_user [$time_local] "$request" '
                        '$status $uptream_status $body_bytes_sent "$http_referer" '
                        '"$http_user_agent" "$http_x_forwarded_for" '
                        '$ssl_protocol $ssl_cipher $upstream_addr $request_time $upstream_response_time';

$server_name:虚拟主机名称。
$remote_addr:远程客户端的IP地址。
-:空白,用一个“-”占位符替代,历史原因导致还存在。
$remote_user:远程客户端用户名称,用于记录浏览者进行身份验证时提供的名字,如登录百度的用户名scq2099yt,如果没有登录就是空白。
[$time_local]:访问的时间与时区,比如18/Jul/2012:17:00:01 +0800,时间信息最后的"+0800"表示服务器所处时区位于UTC之后的8小时。
$request:请求的URI和HTTP协议,这是整个PV日志记录中最有用的信息,记录服务器收到一个什么样的请求
$status:记录请求返回的http状态码,比如成功是200。
$uptream_status:upstream状态,比如成功是200.
$body_bytes_sent:发送给客户端的文件主体内容的大小,比如899,可以将日志每条记录中的这个值累加起来以粗略估计服务器吞吐量。
$http_referer:记录从哪个页面链接访问过来的。 
$http_user_agent:客户端浏览器信息
$http_x_forwarded_for:客户端的真实ip,通常web服务器放在反向代理的后面,这样就不能获取到客户的IP地址了,通过$remote_add拿到的IP地址是反向代理服务器的iP地址。反向代理服务器在转发请求的http头信息中,可以增加x_forwarded_for信息,用以记录原有客户端的IP地址和原来客户端的请求的服务器地址。
$ssl_protocol:SSL协议版本,比如TLSv1。
$ssl_cipher:交换数据中的算法,比如RC4-SHA。 
$upstream_addr:upstream的地址,即真正提供服务的主机地址。 
$request_time:整个请求的总时间。 
$upstream_response_time:请求过程中,upstream的响应时间。

2. 自动切割

#!/bin/bash #设置日志文件存放目录 logs_path="/home/eop/openresty/nginx/logs/" #设置pid文件 pid_path="/home/eop/openresty/nginx/logs/nginx.pid"

#重命名日志文件 mv ${logs_path}access.log ${logs_path}access_$(date -d "yesterday" +"%Y%m%d").log

#向nginx主进程发信号重新打开日志 kill -USR1 cat ${pid_path} ~~~

0 0 * * * bash /home/eop/openresty/nginx/nginx_log.sh

/home/eop/openresty/nginx/logs/*.log {
        daily
        missingok
        rotate 52
        compress
        delaycompress
        notifempty
        create 640 nginx adm
        sharedscripts
        postrotate
                [ -f /home/eop/openresty/nginx/logs/nginx.pid ] && kill -USR1 `cat /home/eop/openresty/nginx/logs/nginx.pid`
        endscript
}

配置说明: 
daily: 日志文件每天进行滚动 
missingok: 如果找不到这个log档案,就忽略过去 
rotate: 保留最进52次滚动的日志 
compress: 通过gzip压缩转储以后的日志 
delaycompress: 和compress一起使用时,转储的日志文件到下一次转储时才压缩
notifempty: 如果是空文件的话,不转储 
create mode owner group:转储文件,使用指定的文件模式创建新的日志文件 
sharedscripts: 运行postrotate脚本(该脚本作用为让nginx重新生成日志文件) 
postrotate/endscript: 在转储以后需要执行的命令可以放入这个对,这两个关键字必须单独成行 

3. 日志采集

nginx在运行过程中会产生大量日志,为了保证nginx服务器运行正常,并且便于对日志进行进一步分析,有必要将nginx日志收集到专门的日志服务器。

二. 状态

1. 配置

location /ngx_status {
    stub_status on;
    access_log off;
}

2. 信息

Active connections: 2 
server accepts handled requests
 11 11 19 
Reading: 0 Writing: 1 Waiting: 1 

active connections – 活跃的连接数量
server accepts handled requests — 总共处理了11989个连接 , 成功创建11989次握手, 总共处理了11991个请求
reading — 读取客户端的连接数.
writing — 响应数据到客户端的数量
waiting — 开启 keep-alive 的情况下,这个值等于 active – (reading+writing), 意思就是 Nginx 已经处理完正在等候下一次请求指令的驻留连接.

三. https

1. 生成证书

cd /home/eop/openresty/nginx/conf
openssl genrsa -des3 -out server.key 1024
openssl req -new -key server.key -out server.csr
openssl rsa -in server.key -out server_nopwd.key
openssl x509 -req -days 365 -in server.csr -signkey server_nopwd.key -out server.crt

2. 配置


server {
    listen 443;
    ssl on;
    ssl_certificate  /home/eop/openresty/nginx/conf/server.crt;
    ssl_certificate_key  /home/eop/openresty/nginx/conf/server_nopwd.key;
}

四. httpclient

实现构造http请求并向外部服务器发送。

1. ngx_lua + capture + proxy

local res = ngx.location.capture('/proxy/https/10.1.29.82:8443/test', {
    method = ngx.HTTP_GET,
    body = body,
    args = {hello = 'world'}
});
location /proxy {
    internal;
    rewrite ^/proxy/(https?)/([^/]+)/(.*) /$3 break;
    proxy_pass $1://$2;
}

2. lua-resty-http

https://github.com/pintsized/lua-resty-http

将包中lib/resty下的文件放入到openresty/lualib/resty目录下

local http = require "resty.http"
local httpc = http.new()
local res, err = httpc:request_uri("http://10.1.63.77/lua1", {
    method = "POST",
    body = "a=1&b=2",
    headers = {
        ["Content-Type"] = "application/x-www-form-urlencoded",
    }
})

if not res then
    ngx.say("failed to request: ", err)
    return
end

-- In this simple form, there is no manual connection step, so the body is read
-- all in one go, including any trailers, and the connection closed or keptalive
-- for you.

ngx.status = res.status

for k,v in pairs(res.headers) do
    ngx.say(k..": "..v)
end

ngx.say(res.body)

五. 访问Redis

1. Openresty访问Redis有两种方式:

1. 调用lua-resty-redis接口进行访问
    https://github.com/openresty/lua-resty-redis
    每次访问时建立一个连接,访问完毕后关闭连接(或超时后自动关闭),不建议使用这种方式
2. 使用ngx.location.capture调用redis2-nginx-module进行访问
    https://github.com/openresty/redis2-nginx-module
    如果redis2_pass直接指向redis服务器,会和上一种方式一样,每调用一次ngx.location.capture就会创建一个新的连接,因此也不建议使用这种方式
3. 使用ngx.location.capture + redis2_pass + upstream的方式
    该方法可以参照https://github.com/openresty/redis2-nginx-module#connection-pool

2. 配置参数:

redis2_query
redis2_raw_query
redis2_raw_queries
redis2_pass

3. 示例:

nginx配置

http {
    ……

    upstream redisbackend {
        server 127.0.0.1:6379;

        keepalive 2;
    }

    server {
        listen       80;
        server_name  localhost;

        location = /redis {
            internal;
            redis2_raw_query $echo_request_body;
            redis2_pass redisbackend;
        }

        location /luatest {
            lua_code_cache off;
            default_type 'text/plain';
            content_by_lua_file html/luatest/test.lua;
        }
    }
}

lua脚本 ~~~ local parser = require "redis.parser"

a= {body = parser.build_query({"set", "foo", "hello world"})}

local res = ngx.location.capture("/redis?", a)

if res.status ~= 200 or not res.body then ngx.log(ngx.ERR, "failed to query redis") ngx.exit(500) end

local reply, type = parser.parse_reply(res.body) ngx.say(reply..'\t'..parser.typename(type))


## 六. 访问MySQL

### 1. 访问方式

Openresty访问MySQL有两种方式:

调用lua-resty-mysql接口进行访问:https://github.com/openresty/lua-resty-mysql 使用ngx.location.capture调用drizzle-nginx-module进行访问:https://github.com/openresty/drizzle-nginx-module ~~~

2. 配置说明

drizzle_server
drizzle_server <host>[:<port>] user=<user> [password=<pass>] dbname=<database> [protocol=<protocol>] [charset=<charset>]
在upstream中配置,用于mysql数据库连接创建
 user:用户名
 password:用户密码
 dbname:数据库
 protocol:使用何种协议,可以选择drizzle或者mysql,默认为drizzle
 charset:字符集,如utf8,与mysql数据库的默认字符集相同

drizzle_keepalive
drizzle_keepalive max=<size> mode=<mode> [overflow=<action>]
在upstream中配置,用于连接保持
 max:当前连接池的大小,默认为0,0表示禁止连接池,这样不会创建连接,不要将max配置为0
 mode:可以配置为single和multi,默认为single。当为single时,连接池不会区分当前upstream block下不同类别的drizzle server;当为multi时,连接池会重用相同名字和端口的连接,对不同的数据库和用户会忽略。
 overflow:指示当连接池满的情况下,如果有新连接需要建立,如何处理。有两个值可以选择,reject和ignore:reject会拒绝当前的请求,并回送503;ignore会继续创建一个新的数据库连接

drizzle_query
drizzle_query <sql>
发起一个数据库查询请求

drizzle_pass
drizzle_pass <remote>
指定drizzle的upstream

drizzle_buffer_size
drizzle_buffer_size <size>
指定drizzle返回缓冲区大小,默认为4k/8k

3. 示例

nginx配置 ~~~ http { ……

upstream mysqlbackend {
    drizzle_server 127.0.0.1:3306 dbname=mytest
       user=root protocol=mysql;
    drizzle_keepalive max=100 mode=single overflow=reject;
}

server {
    listen       80;
    server_name  localhost;

    location / {
        root   html;
        index  index.html index.htm;
    }

    location = /mysql {
        internal;
        drizzle_pass mysqlbackend;
        drizzle_module_header off;
        drizzle_query $query_string;
        rds_json on;
    }

    location /luatest {
        lua_code_cache off;
        default_type 'text/plain';
        content_by_lua_file html/luatest/test.lua;
    }
}

} ~~~

lua脚本 ~~~ local res = ngx.location.capture("/mysql?".."select userid, username from tb1") ngx.say(res.body)

res = ngx.location.capture("/mysql?".."select userid, username from tb2") ngx.say(res.body)

res = ngx.location.capture("/mysql?".."insert into tb1 (username) values (\"Test\")") ngx.say(res.body)

res = ngx.location.capture("/mysql?".."insert into tb2 (username) values (\"Test\")") ngx.say(res.body) ~~~

七. 限速

限制客户端的访问请求数和并发连接数。这在实际应用中非常重要!

1. 限制访问请求数

使用ngx_http_limit_req_module模块可以限制访问请求数。

limit_req_zone
语法: limit_req_zone $variable zone=name:size rate=rate;
默认值: none
配置段: http
设置一块共享内存限制域用来保存键值的状态参数。 特别是保存了当前超出请求的数量。 键的值就是指定的变量(空值不会被计算)。如
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;

说明:区域名称为one,大小为10m,平均处理的请求频率不能超过每秒一次。
键值是客户端IP。
使用$binary_remote_addr变量, 可以将每条状态记录的大小减少到64个字节,这样1M的内存可以保存大约1万6千个64字节的记录。
如果限制域的存储空间耗尽了,对于后续所有请求,服务器都会返回 503 (Service Temporarily Unavailable)错误。
速度可以设置为每秒处理请求数和每分钟处理请求数,其值必须是整数,所以如果你需要指定每秒处理少于1个的请求,2秒处理一个请求,可以使用 “30r/m”。

limit_req_log_level
语法: limit_req_log_level info | notice | warn | error;
默认值: limit_req_log_level error;
配置段: http, server, location
设置你所希望的日志级别,当服务器因为频率过高拒绝或者延迟处理请求时可以记下相应级别的日志。 延迟记录的日志级别比拒绝的低一个级别;比如, 如果设置“limit_req_log_level notice”, 延迟的日志就是info级别。

limit_req_status
语法: limit_req_status code;
默认值: limit_req_status 503;
配置段: http, server, location
该指令在1.3.15版本引入。设置拒绝请求的响应状态码。

limit_req
语法: limit_req zone=name [burst=number] [nodelay];
默认值: —
配置段: http, server, location
设置对应的共享内存限制域和允许被处理的最大请求数阈值。 如果请求的频率超过了限制域配置的值,请求处理会被延迟,所以所有的请求都是以定义的频率被处理的。 超过频率限制的请求会被延迟,直到被延迟的请求数超过了定义的阈值,这时,这个请求会被终止,并返回503 (Service Temporarily Unavailable) 错误。

示例

http {
    limit_req_zone $binary_remote_addr zone=eop:10m rate=20r/s;

    server {
        location  ^~ /download/ {
            #限制每ip每秒不超过20个请求,漏桶数burst为5
                    #brust的意思就是,如果第1秒、2,3,4秒请求为19个,
                    #第5秒的请求为25个是被允许的。
                    #但是如果你第1秒就25个请求,第2秒超过20的请求返回503错误。
                    #nodelay,如果不设置该选项,严格使用平均速率限制请求数,
                    #第1秒25个请求时,5个请求放到第2秒执行,
                    #设置nodelay,25个请求将在第1秒执行。  
            limit_req zone=eop burst=5;
            alias /data/www.eop.com/download/;
        }
    }
}

2. 限制并发连接数

使用ngx_http_limit_conn_module模块可以限制并发连接数。

limit_conn_zone
语法: limit_conn_zone $variable zone=name:size;
默认值: none
配置段: http
该指令描述会话状态存储区域。键的状态中保存了当前连接数,键的值可以是特定变量的任何非空值(空值将不会被考虑)。$variable定义键,zone=name定义区域名称,后面的limit_conn指令会用到的。size定义各个键共享内存空间大小。如:
limit_conn_zone $binary_remote_addr zone=addr:10m;
注释:客户端的IP地址作为键。注意,这里使用的是$binary_remote_addr变量,而不是$remote_addr变量。
$remote_addr变量的长度为7字节到15字节,而存储状态在32位平台中占用32字节或64字节,在64位平台中占用64字节。
$binary_remote_addr变量的长度是固定的4字节,存储状态在32位平台中占用32字节或64字节,在64位平台中占用64字节。
1M共享空间可以保存3.2万个32位的状态,1.6万个64位的状态。
如果共享内存空间被耗尽,服务器将会对后续所有的请求返回 503 (Service Temporarily Unavailable) 错误。
limit_zone 指令和limit_conn_zone指令同等意思,已经被弃用,就不再做说明了。

limit_conn_log_level
语法:limit_conn_log_level info | notice | warn | error
默认值:error
配置段:http, server, location
当达到最大限制连接数后,记录日志的等级。
limit_conn
语法:limit_conn zone_name number
默认值:none
配置段:http, server, location
指定每个给定键值的最大同时连接数,当超过这个数字时被返回503 (Service Temporarily Unavailable)错误。如:

limit_conn_zone $binary_remote_addr zone=addr:10m;
server {
    location /www.ttlsa.com/ {
        limit_conn addr 1;
    }
}
同一IP同一时间只允许有一个连接。
当多个 limit_conn 指令被配置时,所有的连接数限制都会生效。比如,下面配置不仅会限制单一IP来源的连接数,同时也会限制单一虚拟服务器的总连接数:

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;
}
[warning]limit_conn指令可以从上级继承下来。[/warning]

limit_conn_status
语法: limit_conn_status code;
默认值: limit_conn_status 503;
配置段: http, server, location
该指定在1.3.15版本引入的。指定当超过限制时,返回的状态码。默认是503。
limit_rate
语法:limit_rate rate
默认值:0
配置段:http, server, location, if in location
对每个连接的速率限制。参数rate的单位是字节/秒,设置为0将关闭限速。 按连接限速而不是按IP限制,因此如果某个客户端同时开启了两个连接,那么客户端的整体速率是这条指令设置值的2倍。

配置示例

http {
    geo $whiteiplist  {
        default 1;
        218.249.60.68 0;
    }
    map $whiteiplist $limit {
        1 $binary_remote_addr;
        0 "";
    }
    #limit_conn_zone $limit zone=limit:10m;
    #limit_req_zone $binary_remote_addr zone=limit_req:10m rate=3r/m;
    limit_req_zone $limit zone=limit_req:10m rate=3r/m;
    limit_req zone=limit_req burst=1 nodelay;
    limit_req_status 503;
    limit_req_log_level error;

    server {
    }
}

3. 结合白名单进行限速

通过结合nginx的geo和map,实现对来自ip白名单以外的请求进行限制。

http {
 geo $whiteiplist  {
 default 1;
 127.0.0.1 0;
 10.0.0.0/8 0;
 121.207.242.0/24 0;
 }

 map $whiteiplist  $limit {
 1 $binary_remote_addr;
 0 "";
 }

 limit_conn_zone $limit zone=limit:10m;

 server {
        listen       8080;
        server_name  test.ttlsa.com;

        location ^~ /ttlsa.com/ {
                limit_conn limit 4;
                limit_rate 200k;
                alias /data/www.ttlsa.com/data/download/;
        }
 }
}

八. 性能调优

1. nginx配置优化

worker_processes 8;

worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;

worker_rlimit_nofile 102400;

events {
    use epoll;
    worker_connections 102400;
}

http {
    upstream redisbackend {
        server 127.0.0.1:6379;
        keepalive 1000;
    }

    server {
        listen 8080 reuseport;
    }
}

2. 内核网络参数优化

编辑/etc/sysctl.conf文件

net.ipv4.ip_forward = 0
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.default.accept_source_route = 0
kernel.sysrq = 0
kernel.core_uses_pid = 1
net.ipv4.tcp_syncookies = 1
kernel.msgmnb = 65536
kernel.msgmax = 65536
kernel.shmmax = 68719476736
kernel.shmall = 4294967296
net.ipv4.tcp_max_tw_buckets = 6000
net.ipv4.tcp_sack = 1
net.ipv4.tcp_window_scaling = 1
net.ipv4.tcp_rmem = 4096 87380 4194304
net.ipv4.tcp_wmem = 4096 16384 4194304
net.core.wmem_default = 8388608
net.core.rmem_default = 8388608
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.netdev_max_backlog = 262144
net.core.somaxconn = 65535
net.ipv4.tcp_max_orphans = 3276800
net.ipv4.tcp_max_syn_backlog = 262144
net.ipv4.tcp_timestamps = 0
net.ipv4.tcp_synack_retries = 1
net.ipv4.tcp_syn_retries = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_mem = 94500000 915000000 927000000
net.ipv4.tcp_fin_timeout = 1
net.ipv4.tcp_keepalive_time = 30
net.ipv4.ip_local_port_range = 1024 65000

执行/sbin/sysctl -p生效

3. 内核ulimit参数优化

/etc/security/limits.conf最后增加

* soft nofile 65535
* hard nofile 65535
* soft nproc 65535
* hard nproc 65535

4. mysql优化

set global max_connections=1000;

5. Redis优化

maxclients 10000

6. 内核参数优化脚本

#! /bin/bash

sysctlfile=/etc/sysctl.conf
limitsfile=/etc/security/limits.conf
user=www
SYSCTLTMPFILE=./sysctl.conf.tmp

sysctlitems="net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_max_tw_buckets = 5000
net.ipv4.tcp_keepalive_time = 1200
net.ipv4.ip_local_port_range = 10000 65000
net.ipv4.tcp_max_syn_backlog = 8192
net.core.netdev_max_backlog = 32768
net.core.somaxconn = 65535
net.core.wmem_default = 8388608
net.core.rmem_default = 8388608
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_timestamps = 0
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 2
net.ipv4.tcp_wmem = 8192 436600 873200
net.ipv4.tcp_rmem  = 32768 436600 873200
net.ipv4.tcp_mem = 94500000 91500000 92700000
net.ipv4.tcp_max_orphans = 3276800
net.ipv4.tcp_fin_timeout = 30"

sysctlconf()
{
    TMPFILE=.tmp
    srchstr=$1
    rplcstr=$@
    tmp=`sed -n "/$srchstr .*/"p $SYSCTLTMPFILE`
    if [ -n "$tmp" ];
    then
        sed "s/$srchstr .*/$rplcstr/g" $SYSCTLTMPFILE > $TMPFILE
        mv $TMPFILE $SYSCTLTMPFILE
    else
        echo "$rplcstr" >> $SYSCTLTMPFILE
    fi
}

limitsconf()
{
    TMPFILE=.tmp

    tmp=`sed -n "/^$user soft .*/"p $limitsfile`
    if [ -n "$tmp" ];
    then
        sed "s/^$user soft .*/$user soft nofile 102400/g" $limitsfile > $TMPFILE
        mv $TMPFILE $limitsfile
    else
        echo "$user soft nofile 102400" >> $limitsfile
    fi

    tmp=`sed -n "/^$user hard .*/"p $limitsfile`
    if [ -n "$tmp" ];
    then
        sed "s/^$user hard .*/$user hard nofile 102400/g" $limitsfile > $TMPFILE
        mv $TMPFILE $limitsfile
    else
        echo "$user hard nofile 102400" >> $limitsfile
    fi
}

cp $sysctlfile $SYSCTLTMPFILE
IFS="
"
for item in $sysctlitems
do
    IFS=" "
    sysctlconf $item
    IFS="
"
done
IFS=" "

mv $SYSCTLTMPFILE $sysctlfile
rm -f $SYSCTLTMPFILE
sysctl -p

limitsconf

九. 压力测试

1. ab

sudo ln -s /usr/local/apr/lib/pkgconfig/apr-1.pc /usr/local/lib/pkgconfig/apr-1.pc

ab -n 100 -c 100 -p sub.json -T 'application/json' 'http://10.1.63.78:8080/Subscription/normalcall/v1/events'
ab -n 100 -c 100 -p unsub.json -T 'application/json' 'http://10.1.63.78:8080/Unsubscription/normalcall/v1/events'
ab -n 100 -c 100 'http://10.1.63.78:8080/test/test.html'

2. siege

siege -r 100 -c 100 -b 'http://10.1.63.78:8080/Subscription/normalcall/v1/events POST < ./sub.json'
siege -r 100 -c 100 -b 'http://10.1.63.78:8080/test/test.html'

3. 结果

环境: ~~~ CPU: Intel(R) Xeon(R) CPU E5-2609 v2 @ 2.50GHz (8核) MEM: 32G ~~~

部署: ~~~ nginx、redis部署在同一台机器 mysql部署在一台机器 ~~~

结果: ~~~ ab测试,2000并发 单个请求平均处理时间1.04ms nginx启动的8个worker的cpu占用率接近100% 主机cpu占用率100%,其中用户态10%,内核态90% ~~~

十. 配置为服务

创建/etc/systemd/system/nginx.service文件

[Unit]
Description=Nginx Web server
BindTo=network.target
Requires=php-fpm.service

[Install]
WantedBy=multi-user.target

[Service]
User=nginx
Group=nginx
Type=forking
PIDFile=/home/nginx/nginx/logs/nginx.pid
Environment=PATH=/home/nginx/nginx:/usr/bin:/bin
ExecStart=/home/nginx/nginx/sbin/nginx -p /home/nginx/nginx
ExecStop=ExecStop=/bin/kill -s QUIT $MAINPID
ExecReload=/bin/kill -s HUP $MAINPID
PrivateTmp=true
#Restart=on-failure

十一. 使用线程实现并发处理

创建/etc/systemd/system/nginx.service文件

        location /sleep {
            default_type text/plain;
            lua_need_request_body on;
            content_by_lua '
                ngx.sleep(2)
                ngx.exit(ngx.HTTP_OK)
            ';
        }

        location /testthread {
            default_type text/plain;
            lua_need_request_body on;
            content_by_lua '
                local function fetch(uri)
                    local http=require "resty.http"
                    local httpc=http.new()
                    local thirdpartyRes, thirdpartyErr = httpc:request_uri(
                        uri,
                        {
                            method="GET",
                        }
                    )
                    return thirdpartyRes
                end
                begintime = ngx.now()
                local threads = {}
                for i=1,12000 do
                    threads[i]=ngx.thread.spawn(fetch, "http://127.0.0.1:8080/sleep")
                end

                local snum1=0;
                local snum2=0;
                local enum=0;
                for i=1,#threads do
                    local ok, res = ngx.thread.wait(threads[i])
                    if ok then
                        if res == nil then
                            --ngx.say("thread(" .. i .. ") faild")
                            snum2=snum2+1
                        else
                            --ngx.say("thread(" .. i .. "): " .. res.status)
                            snum1=snum1+1
                        end
                    else
                        enum=enum+1
                        --ngx.say("thread(" .. i .. ") faild")
                    end
                end
                ngx.update_time()
                endtime=ngx.now()
                ngx.say("begintime:" .. begintime .. " endtime:" .. endtime .. " cost:" .. endtime-begintime)
                ngx.say("snum1:" .. snum1 .. " snum2:" .. snum2 .. " enum:" .. enum)

                --begintime = ngx.now()
                --for i=1,#threads do
                --    fetch("http://127.0.0.1:8080/sleep")
                --end
                --endtime=ngx.now()
                --ngx.say("begintime:" .. begintime .. " endtime:" .. endtime .. " cost:" .. endtime-begintime)
            ';
        }

十二. 基本认证

1. 生成密码文件

printf "eop:$(openssl passwd -crypt EBupt!@#123)\n" >> htpasswd

2. 配置

server{
       server_name  www.ttlsa.com ttlsa.com;

        index index.html index.php;
        root /data/site/www.ttlsa.com;       

        location /
        {
                auth_basic "nginx basic http test for ttlsa.com";
                auth_basic_user_file conf/htpasswd; 
                autoindex on;
        }
}

3. nginxauth.sh

#!/bin/bash

passwd=$(mkpasswd -l 8)

echo "eop:$(openssl passwd -crypt $passwd)" > /home/www/nginx/conf/htpasswd
service nginx restart

mailx -s "ebopencloud login info" liujian@ebupt.com <<EOF
user:eop
password:$passwd
EOF

十三. 安全加固

1. iptables

iptables -I INPUT -p tcp --dport 22 -j ACCEPT
iptables -I INPUT -p tcp --dport 18000 -j ACCEPT
iptables -I INPUT -p tcp --dport 80 -j ACCEPT
iptables -I INPUT -p tcp --dport 443 -j ACCEPT
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 8080
iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-ports 8443
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -s 127.0.0.1 -d 127.0.0.1 -j ACCEPT
iptables -P INPUT DROP
service iptables save