@Alex-Zhao
2018-04-25T16:59:44.000000Z
字数 6770
阅读 92
负载均衡
本文主要从 Nginx
的进程模块、事件模块、http网络模块三方面介绍了 Nginx
的底层实现原理,希望你通过本文能对Nginx
的基本实现有一定了解。
Nginx
默认采用守护模式启动,守护模式让master进程启动后在后台运行,不在窗口上卡住。
Nginx
启动后会有一个 Master进程
和多个Worker
进程,Master 进程主要用来管理 Worker 进程,对网络事件进程进行收集和分发,调度哪个模块可以占用 CPU 资源,从而处理请求。一般配置Worker进程的个数与机器cpu个数一致
,从而打到cpu资源的最大化利用,也避免由于进程资源分配带来的额外开销。
进程 icon
Master 进程的工作包括
接收来自外界的信号
向各worker进程发送信号
监控worker进程的运行状态,当worker进程退出后(异常情况下),会自动重新启动新的worker进程
惊群现象
是指请求到来的时候,只有可以成功accept的那个进程会惊醒,其他进程会继续阻塞。nginx 采用accept-mutex
来解决惊群问题,当一个请求到达的时候,只有竞争到锁的worker进程才能处理请求,其他进程结合timer_solution
配置的最大的超时时间,再去获取监听锁
来看源码
void
ngx_process_events_and_timers(ngx_cycle_t *cycle)
{
ngx_uint_t flags;
ngx_msec_t timer, delta;
if (ngx_timer_resolution) {
timer = NGX_TIMER_INFINITE;
flags = 0;
} else {
timer = ngx_event_find_timer();
flags = NGX_UPDATE_TIME;
#if (NGX_THREADS)
if (timer == NGX_TIMER_INFINITE || timer > 500) {
timer = 500;
}
#endif
}
/* 检测是否启用mutex,多worker进程下一般都会启用 */
if (ngx_use_accept_mutex) {
if (ngx_accept_disabled > 0) {
ngx_accept_disabled--;
} else {
if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {
/* 尝试获取锁,不管成功还是失败都会立即返回 */
return;
}
if (ngx_accept_mutex_held) {
/* 获取到锁之后添加flag */
flags |= NGX_POST_EVENTS;
} else {
/* 如果获取不到锁需要结合timer事件设置下一次抢锁的时间 */
if (timer == NGX_TIMER_INFINITE
|| timer > ngx_accept_mutex_delay)
{
timer = ngx_accept_mutex_delay;
}
}
}
}
delta = ngx_current_msec;
/* 开始epoll收集处理事件 */
(void) ngx_process_events(cycle, timer, flags);
/* delta就是epoll_wait消耗掉的时间 */
delta = ngx_current_msec - delta;
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"timer delta: %M", delta);
/* accept事件已经被加入到单独的任务队列并会被优先处理 */
ngx_event_process_posted(cycle, &ngx_posted_accept_events);
/* accept事件处理完之后先释放accept锁,因为其它事件的处理可能耗时较长,不要占着茅坑不睡觉 */
if (ngx_accept_mutex_held) {
ngx_shmtx_unlock(&ngx_accept_mutex);
}
if (delta) {
ngx_event_expire_timers();
}
/* 之后可以放心处理其它事件了 */
ngx_event_process_posted(cycle, &ngx_posted_events);
}
当一个worker进程在accept这个连接之后,就开始读取请求,解析请求,处理请求,产生数据后,再返回给客户端,最后才断开连接,这样一个完整的请求就是这样的了。我们可以看到,一个请求,完全由worker进程来处理,而且只在一个worker进程中处理。
采用这种方式的好处:
user nobody nobody;
worker_processes 8;
worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;
worker_rlimit_nofile 65535;
error_log logs/error.log info;
对于一个基本的 WEB 服务器来说,事件通常有三种类型,网络事件
、信号
和定时器
。
一个请求的基本过程:建立连接
-> 接受连接
-> 发送数据
,在系统底层就是读写事件。
Epoll
出现在 linux2.6以后,Nginx采用Epoll
这种异步非阻塞的事件处理机制。这种机制的原理就是把一个完整的请求,划分成多个事件,比如accept(), recv(),磁盘I/O,send(),每个事件都有不同的模块进行处理。一个请求由一个worker进程处理,在请求多的时候,无需频繁的切换进程。
事件模块 icon
Select
模型在启动的时候创建多个进程,放在一个进程池里,并且进程池里的进程数会随着请求数目的增加而增加,对于每一个连接,都是在一个进程内处理完毕。所以Select模型能接收的并发量受到所能开启的进程数影响,进程之间是互相阻塞的,且频繁的切换进程造成大量开销。
Select 是基于一个线程处理一个请求的非阻塞IO的策略,Apache使用这种策略。
events {
use epoll;
worker_connections 1024;
}
当作为http服务器的时候:
max_clients = worker_processes * worker_connections;
当作为反向代理的时候:
max_clients = worker_processes * worker_connections/4
nginx的upstream目前支持的5种方式的分配
upstream backserver {
server host:port;
server host:port;
}
upstream backserver {
server host:port weight=10;
server host:port weight=10;
}
upstream backserver {
ip_hash;
server host:port;
server host:port;
}
upstream backserver {
server server1;
server server2;
fair;
}
upstream backserver {
server squid1:3128;
server squid2:3128;
hash $request_uri;
hash_method crc32;
}
在需要使用负载均衡的server中增加
proxy_pass http://backserver/ ;
upstream backserver{
ip_hash;
server host:port down; (down 表示单前的server暂时不参与负载)
server host:port weight=2; (weight 默认为1.weight越大,负载的权重就越大)
server host:port
server host:port backup; (其它所有的非backup机器down或者忙的时候,请求backup机器)
}
#代理缓冲区相关配置
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
# 通过令牌桶原理实现用户访问次数限制
limit_req_zone $http_x_forwarded_for zone=req_one:10m rate=30r/s;
#设置Web缓存区名称为cache_web,内存缓存空间大小为300MB,1天没有被访问的内容自动清除,硬盘缓存空间
#大小为3GB。
proxy_temp_path /xxx/proxy_temp_dir 1 2;
proxy_cache_path /xxx/proxy_cache_dir levels=1:2 keys_zone=cache_web:300m inactive=1d max_size=1g;
location ~ /\.ht {
deny all;
}
一个http指令下可以配置多个server指令块,一个server指令块里可以根据不同的url做配置
http {
include /xxx/nginx/conf/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr^A $remote_user^A [$time_local]^A $request^A '
'$status^A $body_bytes_sent^A $http_referer^A '
'$http_user_agent^A $http_x_forwarded_for^A '
'$request_body^A $http_X_Cache^A $upstream_http_X_Cache^A '
'$upstream_cache_status^A $http_x_accel_expires^A $dna_device';
access_log /xxx/nginx/logs/access.log main;
sendfile on;
tcp_nopush on;
#server_names_hash_bucket_size 128;
#client_header_buffer_size 8k;
open_file_cache max=10240 inactive=20s;// max打开文件指定缓存数量,inactive指多长时间没请求就删除缓存
open_file_cache_valid 30s; // 30s检查一次缓存的有效信息
open_file_cache_min_uses 1;// 最少使用次数,如果超过这个数字,就一直在缓存中打开
keepalive_timeout 60;
# 代理缓冲区相关配置
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
# 通过令牌桶原理实现用户访问次数限制
limit_req_zone $http_x_forwarded_for zone=req_one:10m rate=30r/s;
#设置Web缓存区名称为cache_web,内存缓存空间大小为300MB,1天没有被访问的内容自动清除,硬盘缓存空间
#大小为3GB。
proxy_temp_path /xxx/proxy_temp_dir 1 2;
proxy_cache_path /xxx/proxy_cache_dir levels=1:2 keys_zone=cache_web:300m inactive=1d max_size=1g;
upstream www_backend_server {
server host:port;
}
upstream m_backend_server {
server host:port;
}
server {
listen 80;
server_name www.xxx.com;
access_log logs/xxx.access.log main;
location ~ \.php {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index /index.php;
include /xxx/nginx/conf/fastcgi_params;
fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
fastcgi_busy_buffers_size 256k;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~ /\.ht {
deny all;
}
}
server {
listen 80;
server_name www.xxx.com;
access_log logs/xxx.access.log main;
location / {
index index.html;
root /xxx/htdocs;
}
}
}
通过fastcgi模块进行交互,交互模式有两种:
fastcgi_pass unix:/xxx/php/var/php-cgi.sock;
fastcgi_pass 127.0.0.1:9000;
mail {
auth_http 127.0.0.1:80/auth.php;
pop3_capabilities "TOP" "USER";
imap_capabilities "IMAP4rev1" "UIDPLUS";
server {
listen 110;
protocol pop3;
proxy on;
}
server {
listen 25;
protocol smtp;
proxy on;
smtp_auth login plain;
xclient off;
}
}
作者:柳清的禅
链接:https://www.jianshu.com/p/b77482d4b670
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。