电商高并发秒杀之Nginx反向代理负载均衡
1. 部署Nginx OpenResty
安装部署Nginx
OpenResty将许多配置编译成了一个二进制包,内部集成了许多nginx特性;
1、安装openresty
1)通过在CentOS 系统中添加 openresty 仓库,便于未来安装或更新我们的软件包(通过 yum update 命令)
sudo yum install yum-utils
sudo yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo
2)安装openresty
sudo yum install openresty
3)安装命令行工具 resty
sudo yum install openresty-resty
命令行工具 opm 在 openresty-opm 包里,而 restydoc 工具在 openresty-doc 包里头。
4)查看openresty 仓库里头的软件包
sudo yum --disablerepo="*" --enablerepo="openresty" list available
在Nginx web服务器中:
location节点path:指定url映射key;
location节点内容:root指定location path后对应的根路径,index指定默认的访问页;
sbin/nginx -c conf/nginx.conf启动;
修改配置后直接sbin/nginx -s reload无缝重启;
2.3 前端资源部署
打包上传前端资源文件;
配置前端资源路由;
2.4 配置nginx反向代理
nginx动静分离服务器
location节点path特定resources:静态资源路径;
location节点其它路径:动态资源用;
如何使用动态资源?
nginx做反向代理服务器
设置upstream server
设置动态请求location为proxy pass路径;
#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;
upstream backend_server{
server 192.168.239.102 weight=1;
server 192.168.239.103 weight=1;
keepalive 30;
}
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location /resources/ {
alias /usr/local/openresty/nginx/html/resources/;
index index.html index.htm;
}
location / {
proxy_pass http://backend_server;
proxy_set_header Host $http_host:$proxy_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
}
$remote_addr
获取到上一级代理的IP
如果想要通过X-Forwarded-For获得用户ip,就必须先使用proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 这样就可以获得用户真实ip。
开启tomcat access log(访问日志)验证
application.properties
server.tomcat.accesslog.enabled=true
server.tomcat.accesslog.directory=/usr/local/www/miaosha/tomcat
server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D
netstat -an | grep 192.168.239.1 | wc -l
netstat -an | grep 192.168.239.1 | grep ESTABLISHED
2.Nginx高性能原因
Nginx高性能主要分为以下三点:
epoll多路复用完成非阻塞式的IO操作;
master worker进程模型,允许其进行平滑重启和配置,不会断开和客户端连接;
协程机制,完成单进程单线程模型,并支持并发编程调用接口;
2.1 epoll多路复用(解决IO阻塞回调通知问题)
I/O多路复用就是通过一种机制,可以监视多个描述符,一旦某个IO能够读写,通知程序进行相应的读写操作。
I/O多路复用的场合
当客户处理多个描述字时(通常是交互式输入和网络套接字),必须使用I/O复用
如果一个TCP服务器既要处理监听套接字,又要处理已连接套接字,一般也要用到I/O复用
如果一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用
Java BIO模型
client和server之间通过tpc/ip建立联系,javaclient只有等到所有字节流socket.write到字节流的缓冲之后,对应的java client才会返回;若网络很慢,缓冲区填满之后,client就必须等待信息传输过去之后,使得缓冲区可以给上游去写时,才可达到直接返回的效果;
Linux select模型
Linux select模型,变更触发轮训查询,文件描述符有1024数量上限;一旦java server被唤醒,并且对应的socket连接打上有变化的标识之后,就代表已经有数据可以让你读写;
弊端:
轮询效率低,有1024数量限制;
epoll模型
epoll是为了解决select和poll的轮询方式效率低问题;
假设一个场景:
有100万个客户端同时与一个服务器进程保持着TCP连接。而每一时刻,通常只有几百上千个TCP连接是活跃的(事实上大部分场景都是这种情况)。如何实现这样的高并发?
在select/poll时代,服务器进程每次都把这100万个连接告诉操作系统(从用户态复制句柄数据结构到内核态),让操作系统内核去查询这些套接字上是否有事件发生,轮询完后,再将句柄数据复制到用户态,服务器应用程序轮询处理已发生的网络事件,这一过程资源消耗较大。因此,select/poll一般只能处理几千的并发连接。
epoll的设计和实现与select完全不同:
epoll通过在Linux内核中申请一个简单的文件系统(文件系统一般由B+树实现)
把原先的select/poll调用分成了3个部分:
- 调用epoll_create()建立一个epoll对象(在epoll文件系统中为这个句柄对象分配资源);
- 调用epoll_ctl向epoll对象中添加这100万个连接的套接字;
- 调用epoll_wait收集发生的事件的连接;
实现上面说是的场景,只需要在进程启动时建立一个epoll对象,然后在需要的时候向这个epoll对象中添加或者删除连接。同时,epoll_wait的效率也非常高,因为调用epoll_wait时,并没有一股脑的向操作系统复制这100万个连接的句柄数据,内核也不需要去遍历全部的连接。
2.2 master-worker进程模型
2.2.1进程模型
Nginx之所以为广大码农喜爱,除了其高性能外,还有其优雅的系统架构。与经典多线程模型相比,Nginx是经典的多进程模型。Nginx启动后以daemon的方式在后台运行,后台进程包含一个master进程和多个worker进程
Nginx多进程模型如下所示:
Nginx多进程模型
master进程主要用来管理worker进程,具体包括如下4个主要功能:
(1)接收来自外界的信号。
(2)向各worker进程发送信号。
(3)监控woker进程的运行状态。
(4)当woker进程退出后(异常情况下),会自动重新启动新的woker进程。
woker进程主要用来处理网络事件,各个woker进程之间是对等且相互独立的,它们同等竞争来自客户端的请求,一个请求只可能在一个woker进程中处理,woker进程个数一般设置为机器CPU核数。
2.2.2进程控制
对Nginx进程的控制主要是通过master进程来做到的,主要有两种方式:
(1)手动发送信号
从图1可以看出,master接收信号以管理众woker进程,那么,可以通过kill向master进程发送信号,比如kill -HUP pid用以通知Nginx从容重启。所谓从容重启就是不中断服务:master进程在接收到信号后,会先重新加载配置,然后再启动新进程开始接收新请求,并向所有老进程发送信号告知不再接收新请求并在处理完所有未处理完的请求后自动退出。
(2)自动发送信号
可以通过带命令行参数启动新进程来发送信号给master进程,比如./nginx -s reload用以启动一个新的Nginx进程,而新进程在解析到reload参数后会向master进程发送信号(新进程会帮我们把手动发送信号中的动作自动完成)。当然也可以这样./nginx -s stop来停止Nginx。
nginx reload master进程不会重启,而是让worker进程重启
image.png2.2.3网络事件
Nginx采用异步非阻塞的方式来处理网络事件,类似于[Libevent](http://blog.chinaunix.net/uid-22312037-id-3546904.html),具体过程如下图:
Nginx网络事件
master进程先建好需要listen的socket后,然后再fork出多个woker进程,这样每个work进程都可以去accept这个socket。当一个client连接到来时,所有accept的work进程都会受到通知,但只有一个进程可以accept成功,其它的则会accept失败。Nginx提供了一把共享锁accept_mutex来保证同一时刻只有一个work进程在accept连接,从而解决惊群问题。当一个worker进程accept这个连接后,就开始读取请求,解析请求,处理请求,产生数据后,再返回给客户端,最后才断开连接,这样一个完成的请求就结束了。
2.3协程机制
一个线程可以有多个协程,协程是线程的内存模型
依附于线程的内存模型,切换开销小;
遇阻塞及归还执行权,代码同步,调用新的不阻塞的协程;
无需加锁;