@Alex-Zhao
2018-04-25T08:59:44.000000Z
字数 6770
阅读 215
负载均衡
本文主要从 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配置的最大的超时时间,再去获取监听锁
来看源码
voidngx_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:portserver 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
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。