nginx 场景业务汇总 (中)
本文链接:http://www.cnblogs.com/zhenghongxin/p/8906225.html,如果可以,请阅读上篇 《nginx场景业务汇总(初)》
(十三)负载均衡
- 轮询
http {
upstream myapp1 {
server srv1.example.com;
server srv2.example.com;
server srv3.example.com;
}
server {
listen 80;
location / {
proxy_pass http://myapp1;
}
}
}
公平调度原则轮询,类似于rabbitMq的调度规则。依次将请求分发到srv1,srv3,srv3,。
- 最少连接
upstream myapp1 {
least_conn; //最少连接
server srv1.example.com;
server srv2.example.com;
server srv3.example.com;
}
哪个服务器负载低,就分发到此服务器
- ip_hash (会话持久性)
upstream myapp1 {
ip_hash; //ip_hash
server srv1.example.com;
server srv2.example.com;
server srv3.example.com;
}
这是部分解决session共享问题的方法。让同个ip的访问始终分发于某个服务器上,保持会话的一致性。
- 加权
利用权重来促使nginx多分发到某些性能高效的服务器。
upstream myapp1 {
server srv1.example.com weight=3;
server srv2.example.com;
server srv3.example.com;
}
也就是说,如果有五个请求,有3个请求被分发到srv1,一个请求到srv2,一个请求到srv3
其他参数
(十四)HTTPS
为了网站后台更加安全,之前我们做过让后台需要证书登录,但做的是用openssl生成的免费证书。
# 创建服务器私钥,命令会让你输入一个口令:
openssl genrsa -des3 -out server.key 1024
# 创建签名请求的证书(CSR):
openssl req -new -key server.key -out server.csr
# 加载SSL支持的Nginx并使用上述私钥时除去必须的口令:
cp server.key server.key.org
openssl rsa -in server.key.org -out server.key
# 标记证书使用上述私钥和CSR:
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
注意day时间为一年过期
nginx配置:
ssl on;
ssl_certificate /etc/nginx/ssl_key/example.crt;
ssl_certificate_key /etc/nginx/ssl_key/example.key;
(十五)openresty 与 lua (ngx_lua)
我们可以看网络上对它的描述:OpenResty 是一个强大的 Web 应用服务器,Web 开发人员可以使用 Lua 脚本语言调动 Nginx 支持的各种 C 以及 Lua 模块,更主要的是在性能方面,OpenResty可以 快速构造出足以胜任 10K 以上并发连接响应的超高性能 Web 应用系统。所以对于一些高性能的服务来说,可以直接使用 OpenResty 访问 Mysql或Redis等,而不需要通过第三方语言(PHP、Python、Ruby)等来访问数据库再返回,这大大提高了应用的性能。确实,该模块非常强大,在很多业务场景都可以用,因为是nginx本身的模块,比起nginx转发给php处理快多了。该模块可以处理的业务:
- 秒杀场景 - openresty 是可以直接连接redis,这会产生更快的速度。判断库存是否存在,在秒杀这种高并发的业务下极为适合
- web保护层 - 在连接php应用前,做一些可以不用php处理的过滤,校验等
- 统计 - 不放在php层,而是放在nginx模块完成
- 图片与音频 - 要具体业务分析,我们的业务图片用到此模
- 限流
- 缓存
- 降级
- 灰度发布等等很多
使用ngx_lua,先安装luaJIT:
# cd /usr/local/src
# wget http://luajit.org/download/LuaJIT-2.0.2.tar.gz
# tar -xzvf LuaJIT-2.0.2.tar.gz
# cd LuaJIT-2.0.2
# make
出现如下内容表示编译成功
OK Successfully built LuaJIT
make[1]: Leaving directory `/usr/local/src/LuaJIT-2.0.2/src'
==== Successfully built LuaJIT 2.0.2 ====
# make install
出现如下内容,表示安装成功
==== Successfully installed LuaJIT 2.0.2 to /usr/local ====
下载nginx_lua 模块
# cd /usr/local/src
# wget https://github.com/chaoslawful/lua-nginx-module/archive/v0.8.6.tar.gz
# tar -xzvf v0.8.6
进入nginx源码,进行模块添加编译:
./configure --user=www --group=www --prefix=/phpstudy/server/nginx --with-http_ssl_module --with-http_sub_module --with-http_stub_status_module --with-pcre --with-http_secure_link_module
--add-module=/data/lua-nginx-module-0.8.6
接着make (不要使用make install)
可以使用命令查看之前所添加的模块:
[root@VM_71_225_centos data]# nginx -V nginx version: nginx/1.4.6 built by gcc 4.8.5 20150623 (Red Hat 4.8.5-16) (GCC) TLS SNI support enabled configure arguments: --user=www --group=www --prefix=/phpstudy/server/nginx --with-http_ssl_module --with-http_sub_module --with-http_stub_status_module --with-pcre
--with-http_secure_link_module
--add-module=/data/lua-nginx-module-0.8.6
完成之后停止nginx,备份nginx,再把当前生成的nginx覆盖进去,重启nginx
cp /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx_bk
nginx -s stop
cp objs/nginx /usr/local/nginx/sbin/nginx
nginx
查看当前模块是否已经有lua
可能遇到的错误:
# /usr/local/nginx-1.4.2/sbin/nginx -v
./objs/nginx: error while loading shared libraries: libluajit-5.1.so.2: cannot open shared object file: No such file or directory
解决方法:
# ln -s /usr/local/lib/libluajit-5.1.so.2 /lib64/libluajit-5.1.so.2
使用:
location /hello {
default_type 'text/plain';
content_by_lua 'ngx.say("hello, lua")';
}
openresty 请自行查看其它教程
(十六)基于上述的灰度发布
即让一部分用户继续用产品特性A,一部分用户开始用产品特性B,如果用户对B没有什么反对意见,那么逐步扩大范围,把所有用户都迁移到B上面来。可以保证软件的稳定性。比如我们要上一个新的接口,可以通过在业务Nginx通过Lua写复杂的业务规则实现不同的人看到不同的版本。
以下是一个简单的示例,根据用户IP来体验不同的版本:
nginx.conf配置:
location / { default_type "text/html"; content_by_lua_file /opt/app/lua/dep.lua; #add_after_body "$http_x_forwarded_for"; } location @server{ proxy_pass http://127.0.0.1:9090; } location @server_test{ proxy_pass http://127.0.0.1:8080; }
dep.lua:
clientIP = ngx.req.get_headers()["X-Real-IP"] //获取用户的真实IP
if clientIP == nil then
clientIP = ngx.req.get_headers()["x_forwarded_for"]
end
if clientIP == nil then
clientIP = ngx.var.remote_addr //尝试用remote_addr来获取用户ip
end
local memcached = require "resty.memcached" //从memcache中获得允许查看新版本的Ip列表
local memc, err = memcached:new()
if not memc then
ngx.say("failed to instantiate memc: ", err)
return
end
local ok, err = memc:connect("127.0.0.1", 11211)
if not ok then
ngx.say("failed to connect: ", err)
return
end
local res, flags, err = memc:get(clientIP)
ngx.say("value key: ",res,clientIP)
if err then
ngx.say("failed to get clientIP ", err)
return
end
if res == "1" then
ngx.exec("@server_test") //转发到server_test 服
return
end
ngx.exec("@server") //否则转发到server正式服
(十七)gzip压缩
用于对输出到客户端的内容进行压缩,以减小传输文件体积,减少对网络带宽的占用。在Web应用中通常启用gzip压缩,用来缩短响应时间,提升用户体验。
# 对static目录下js、css、jpg、jpeg、png、gif后缀的文件启用gzip压缩功能
location ~ /static/(.+)\.(js|css|jpg|jpeg|png|gif) {
gzip on; # 启用gzip压缩,默认是off,不启用
# 对js、css、jpg、png、gif格式的文件启用gzip压缩功能
gzip_types application/javascript text/css image/jpeg image/png image/gif;
gzip_min_length 1024; # 所压缩文件的最小值,小于这个的不会压缩
gzip_buffers 4 1k; # 设置压缩响应的缓冲块的大小和个数,默认是内存一个页的大小
gzip_comp_level 1; # 压缩水平,默认1。取值范围1-9,取值越大压缩比率越大,但越耗cpu时间
}
(十八)直连memcache 和redis模块
配置举例(memcache):
upstream memserver { 把用到的memcached节点,声明在一个组里
hash_key $request_uri; // hash计算时的依据,以uri做依据来hash
server localhost:11211;
server localhost:11212;
}
location / {
# root html;
set $memcached_key $uri;
memcached_pass memserver; // memserver为上面的memcache节点的名称
error_page 404 /writemem.php;
index index.php index.html index.htm;
}
那么这里,我们会发现挂上两台memcache,nginx会以轮询的方式去连接两台memcache,如果想要用其他的负载均衡算法,可以安装第三方模式。
(十九)提高Nginx的CPU亲和力
worker_processes
worker_processes指令是用来设计Nginx进程数,官方默认设为1,赋值太多了,将会对系统IO影响效率,降低Nginx服务器性能。但是为了让多核CPU能够更好的处理并行任务,我们可以讲该值设置大一些,最好这个值是机器CPU的个数一致,并不是越大越好。 如针对双核CPU 将他设置为2 ,四核CPU可以设置为4
worker_cpu_affinity
用来分配每个进程的CPU的工作内核 ,例如是四核的CPU,可以这么设置:
worker_processes 4 ; 四个进程
worker_cpu_affinity 0001 0010 0100 1000;
//四组值,0是不使用,1是使用。 这样每一个进程都有一个 cpu内核了。
{解析 四组二进制值分别对应着四个进程,第一个进程对应的是0001 第二个进程对应的是0010,表示第二个进程计算器内核,第三个进程对应的是0100,表示第三个计算机内核,第四个进程对应1000}
如果八核,那就是八个数,以此类推,这样设置太麻烦了,所以nginx在1.9版本之后,已经可以设置为auto了,让其自动分配
(二十)其他场景问题:
- nginx如何将日志按日期分割?
利用定时任务+nginx信号管理来实现,定时脚本如下:
#!/bin/bash
base_path='/usr/local/nginx/logs'
log_path=$(date -d yesterday +"%Y%m")
day=$(date -d yesterday +"%d")
mkdir -p $base_path/$log_path
mv $base_path/access.log $base_path/$log_path/access_$day.log //定时移动日志到其他目录
#echo $base_path/$log_path/access_$day.log
kill -USR1 `cat /usr/local/nginx/logs/nginx.pid` //USR1信息号控制nginx重新生成新的日志文件
定时任务为
01 00 * * * /xxx/path/b.sh 每天0时1分
- alias的用法及与root的区别 ?
先看root
location /request_path/image/ {
root /local_path/image/;
}
Nginx把请求映射为/local_path/image/request_path/image/xxx.png 下
再看alias
location /request_path/image/ {
alias /local_path/image/;
}
Nginx把请求映射为/local_path/image/xxx.png