NGINX [engine x], 是一个 HTTP 和反向代理服务器, 通用 TCP/UDP 代理服务器。
C10K 问题
服务器基于进程,线程模型,新接收到一个TCP连接, 就需要分配一个进程或是线程。如果客户端请求超过10K就要创建10K个进程或是线程,造成进程资源效率低下,单机情况下操作系统无法承受,此类普遍的网络现象和技术局限被称为C10K问题。
![notion image](https://www.notion.so/image/https%3A%2F%2Ffile.notion.so%2Ff%2Ff%2F2b703890-16aa-4df9-8d4c-fdef88b2413e%2F443e4870-8180-4f0f-baf6-3b0ad8d6132d%2FUntitled.png%3Ftable%3Dblock%26id%3Dd5639a98-5097-48fa-9045-7d86182998c0%26spaceId%3D2b703890-16aa-4df9-8d4c-fdef88b2413e%26expirationTimestamp%3D1725811200000%26signature%3Drb03QhEZhkUnufkuQ1lSHcDZaNhNqXATPwv1eOYeVxU?table=block&id=d5639a98-5097-48fa-9045-7d86182998c0&cache=v2)
其本质上创建进程,线程过多, 数据在用户态和内核态拷贝频繁; 进程和线程上下文切换消耗,导致IO 阻塞。
有限的资源内,高效的利用CPU 资源,减少上下文切换?
基于事件循环驱动,只创建少量的进程或是线程来处理若干连接,在操作系统层面, 应运而生 select ,kqueue,poll, epoll 网络模型,
在Node.js ( libuv),Python (selector),Redis 等单线程程序网络处理比较突出。
![notion image](https://www.notion.so/image/https%3A%2F%2Ffile.notion.so%2Ff%2Ff%2F2b703890-16aa-4df9-8d4c-fdef88b2413e%2Fb17f04a2-1746-4eda-9ab2-a5aaa1273f41%2FUntitled.png%3Ftable%3Dblock%26id%3Da615a8f5-7114-4292-a66b-fffff5049574%26spaceId%3D2b703890-16aa-4df9-8d4c-fdef88b2413e%26expirationTimestamp%3D1725811200000%26signature%3DGZIFgo0kpDHnP16JTZN5t9YmuugWXlN05WAps8rXONI?table=block&id=a615a8f5-7114-4292-a66b-fffff5049574&cache=v2)
NGINX 多进程模型
nginx分为单进程模式和多进程模式。单进程模式常常在开发环境调试时候使用。在对外服务时nginx多以多进程方式工作。
多进程工作方式中为方便进程的统一管理,系统中分为一个master进程和多个work进程,master进程主要负责信号处理以及work进程的管理,包括接收外界信号、向worker进程发送信号,监控worker进程的运行状态等,不直接对外提供web服务;
worker进程则主要对外提供web服务,各个work进程之间相互隔离且相互平等,从而避免进程之间的资源竞争导致的性能损耗,worker进程数目可以设置,一般为了降低进程之间上下文切换带来的损耗, 机器cpu核数和worker 设置相同。
NGINX 主子进程
![notion image](https://www.notion.so/image/https%3A%2F%2Ffile.notion.so%2Ff%2Ff%2F2b703890-16aa-4df9-8d4c-fdef88b2413e%2Fbf452b9d-5e02-4363-9473-d6867193cefa%2FUntitled.png%3Ftable%3Dblock%26id%3D42956717-48e4-4fcf-93cc-cc061e6564de%26spaceId%3D2b703890-16aa-4df9-8d4c-fdef88b2413e%26expirationTimestamp%3D1725811200000%26signature%3DK0JRtcQN0DKBWagRzMqF9r15W-vHRuS4pn2fxHczQac?table=block&id=42956717-48e4-4fcf-93cc-cc061e6564de&cache=v2)
![notion image](https://www.notion.so/image/https%3A%2F%2Ffile.notion.so%2Ff%2Ff%2F2b703890-16aa-4df9-8d4c-fdef88b2413e%2F8f7aae0c-ada5-43d1-a2bf-fbee4fa0d8ea%2FUntitled.png%3Ftable%3Dblock%26id%3D168b6ff2-ea6b-470c-99b8-525a50ab421c%26spaceId%3D2b703890-16aa-4df9-8d4c-fdef88b2413e%26expirationTimestamp%3D1725811200000%26signature%3D56_kMhxCgtOhbhPI2dtFk9I-5e1CjfXC5U6eOVm1bXg?table=block&id=168b6ff2-ea6b-470c-99b8-525a50ab421c&cache=v2)
NGINX 源码编译
环境
Ubuntu20.04.3 LTS
GCC 安装
apt-get update apt-get upgrade apt-get install build-essential
OpenSSL 下载
wget -c https://www.openssl.org/source/openssl-1.1.1l.tar.gz && tar zxvf openssl-1.1.1l.tar.gz
Zib 下载
wget http://zlib.net/zlib-1.2.11.tar.gz && tar zxvf wget zlib-1.2.11.tar.gz
Pcre 下载
wget -c https://ftp.pcre.org/pub/pcre/pcre-8.45.tar.gz && tar zxvf pcre-8.45.tar.gz
## 下载源码 git clone https://github.com/chobits/ngx_http_proxy_connect_module.git
Nginx 编译
## 下载 Nginx 源码 wget -c http://nginx.org/download/nginx-1.18.0.tar.gz && tar zxvf nginx-1.18.0.tar.gz ## 进入nginx 目录 cd nginx-1.18.0 ## 加载ngx_http_proxy_connect 补丁 patch -p1 < /root/ngx_http_proxy_connect_module/patch/proxy_connect_rewrite_1018.patch ## 配置检查 ./configure --with-cc-opt='-g -O2 -fdebug-prefix-map=/build/nginx-HdenXw/nginx-1.18.0=. -fstack-protector-strong -Wformat -Werror=format-security -fPIC -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -fPIC' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-compat --with-debug --with-pcre-jit --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_v2_module --with-http_dav_module --with-http_slice_module --with-threads --with-http_addition_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_sub_module --add-module=/root/ngx_http_proxy_connect_module --with-pcre=../pcre-8.45 --with-openssl=../openssl-1.1.1l --with-zlib=../zlib-1.2.11 ## 编译 make ## 安装 sudo make install sudo mkdir -p /var/lib/nginx ## 检查 sudo ./objs/nginx -t
NGINX 代理
正向代理
正向代理是一个位于客户端和目标服务器之间充当代理服务器。
客户端向代理服务器发送一个请求,并且指定目标服务器,之后代理向目标服务器转交并且将获得的内容返回给客户端。客户端感知其请求的目标服务资源。
![notion image](https://www.notion.so/image/https%3A%2F%2Ffile.notion.so%2Ff%2Ff%2F2b703890-16aa-4df9-8d4c-fdef88b2413e%2Fa9861463-375d-47bc-b5de-090c0483837d%2FUntitled.png%3Ftable%3Dblock%26id%3D313baec8-a9fc-4c11-ae7e-452c98727f81%26spaceId%3D2b703890-16aa-4df9-8d4c-fdef88b2413e%26expirationTimestamp%3D1725811200000%26signature%3DhZUcsgE9fezUxFFJSZ_Q0205rQfieiV-CtttP8Hcj24?table=block&id=313baec8-a9fc-4c11-ae7e-452c98727f81&cache=v2)
- http 代理配置 (ngx_http_proxy_connect_module )
server { listen 7777; # dns resolver used by forward proxying resolver 114.114.114.114; # forward proxy for CONNECT request proxy_connect; proxy_connect_allow 443 563; proxy_connect_connect_timeout 10s; proxy_connect_read_timeout 10s; proxy_connect_send_timeout 10s; # forward proxy for non-CONNECT request location / { proxy_pass http://$host; proxy_set_header Host $host; } }
- TCP 正向代理
## 编译追加stream模块和stream_ssl模块 --with-stream --with-stream_ssl_module
反向代理
反向代理正好相反。对于客户端来说,反向代理就好像目标服务器。并且客户端不会感知目标服务。客户端向反向代理发送请求,反向代理判断请求指向具体服务,并将响应转交给客户端,一次客户端并不会感知到反向代理后面的服务。
通常情况下,我们常用到反向代理做负载均衡。
- RR(round robin :轮询 默认)
每个请求按时间顺序逐一分配到不同的后端服务器,也就是说第一次请求分配到第一台服务器上,第二次请求分配到第二台服务器上,如果只有两台服务器,第三次请求继续分配到第一台上,这样循环轮询下去,也就是服务器接收请求的比例是 1:1, 如果后端服务器down掉,能自动剔除。轮询是默认配置,不需要太多的配置
同一个项目分别使用8081和8082端口启动项目
upstream web_servers { server localhost:8081; server localhost:8082; } server { listen 80; server_name localhost; #access_log logs/host.access.log main; location / { proxy_pass http://web_servers; # 必须指定Header Host proxy_set_header Host $host:$server_port; } }
- 权重
指定轮询几率,weight和访问比率成正比, 也就是服务器接收请求的比例就是各自配置的weight的比例,用于后端服务器性能不均的情况,比如服务器性能差点就少接收点请求,服务器性能好点就多处理点请求。
upstream test { server localhost:8081 weight=1; server localhost:8082 weight=3; server localhost:8083 weight=4 backup; }
- ip_hash
上面的2种方式都有一个问题,那就是下一个请求来的时候请求可能分发到另外一个服务器,当我们的程序不是无状态的时候(采用了session保存数据),这时候就有一个很大的很问题了,比如把登录信息保存到了session中,那么跳转到另外一台服务器的时候就需要重新登录了,所以很多时候我们需要一个客户只访问一个服务器,那么就需要用iphash了,iphash的每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。
upstream test { ip_hash; server localhost:8080; server localhost:8081; }
- fair(第三方)
按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。在upstream中加入hash语句,server语句中不能写入weight等其他的参数,hash_method是使用的hash算法
upstream backend { fair; server localhost:8080; server localhost:8081; }
- url_hash(第三方)
按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。在upstream中加入hash语句,server语句中不能写入weight等其他的参数,hash_method是使用的hash算法
upstream backend { hash $request_uri; hash_method crc32; server localhost:8080; server localhost:8081; }
网站镜像
upstream google_upstream { server 142.250.80.46:443 weight=1; server 142.250.80.47:443 weight=1; server 142.250.80.49:443 weight=1; server 142.250.80.50:443 weight=1; # add more servers here } server{ set $simplebox "simplebox"; listen 443 ssl http2 fastopen=5; server_name zeit.fun *.zeit.fun; resolver 8.8.8.8; ssl_certificate /etc/letsencrypt/live/zeit.fun/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/zeit.fun/privkey.pem; ssl_trusted_certificate /etc/letsencrypt/live/zeit.fun/fullchain.pem; # 首页 location / { proxy_set_header Host "www.google.com"; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; proxy_pass https://google_upstream; # gzip, subs_filer 需要设置为空 proxy_set_header Accept-Encoding ""; proxy_set_header Referer "$simplebox , $http_referer"; proxy_set_header X-Real-IP $remote_addr; proxy_set_header User-Agent $http_user_agent; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Accept-Language "en-US,en;q=0.5"; proxy_set_header Cookie "PREF=ID=047808f19f6de346:U=0f62f33dd8549d11:FF=2:LD=en-US:NW=1:TM=1325338577:LM=1332142444:GM=1:SG=2:S=rE0SyJh2W1IQ-Maw"; #设置cookie proxy_set_header X-Forwarded-Proto https; # 其他应用# proxy_redirect off; #关闭重定向 add_header zeit "hi from zeit.fun"; }
![notion image](https://www.notion.so/image/https%3A%2F%2Ffile.notion.so%2Ff%2Ff%2F2b703890-16aa-4df9-8d4c-fdef88b2413e%2F245db8ad-fa9d-4d10-8946-c0254e19f375%2FUntitled.png%3Ftable%3Dblock%26id%3D698014eb-c087-480c-bce8-e79326e55fdd%26spaceId%3D2b703890-16aa-4df9-8d4c-fdef88b2413e%26expirationTimestamp%3D1725811200000%26signature%3DioSYw0rGwidJpdJXi8b43lrSboqZO9DAM3-1WOFt0lQ?table=block&id=698014eb-c087-480c-bce8-e79326e55fdd&cache=v2)
其他
openresty
apisix
envoy
参考
- Thread Pools in NGINX Boost Performance 9x! https://www.nginx.com/blog/thread-pools-boost-performance-9x/
- The C10K problem http://www.kegel.com/c10k.html
- C10k and beyond - Uri Shamay, Akamai https://www.slideshare.net/CodemotionTLV/uri-shamay-c10k-and-beyond
- ngx_http_proxy_connect_module 模块https://github.com/chobits/ngx_http_proxy_connect_module