简介
Web服务器一般指的是“网站服务器”,是某种驻留在因特网上的计算机程序,可以向请求终端提供服务,主要功能时存储、处理和传递网页给“客户”,传递内容一般是HTML文档、图像、样式表或脚本等,也可以放置网站文件以供浏览或下载。
WEB服务器也称为WWW(Word Wide Web)服务器,核心功能就是提供网页信息浏览服务,严格来说Web服务器只负责处理Http协议请求,发送静态页面的内容。
目前主流的web服务器主要是Apache、Nginx、IIS,还有较多使用的Tomcat、Jetty、WebSphere,WebLogic,Kerstrel等。下图为市场占有率历史数据,Apache占有率较高,但是在前1K网站排名中,Nginx占有率最高。
Apache经典web服务端
Apache prefork模型
预派生模式,有一个主进程,生成多个子进程,最大并发1024.
每个子进程有一个独立的线程响应用户请求。
相对比较占内存,较稳定,进程间不会相互影响。
最古老最稳定,适用于访问量不大的场景。
并发性不高
Apache worker 模型
一种多进程和多线程混合的模型
有一个控制进程,启动多个子进程
每个子进程里面包含固定的线程
使用线程程来处理请求
当线程不够使用的时候会再启动一个新的子进程,然后在进程里面再启动线程处理请求
由于其使用了线程处理请求,因此可以承受更高的并发
优点:相比prefork 占用的内存较少,可以同时处理更多的请求
缺点:使用keepalive的长连接方式,某个线程会一直被占据,即使没有传输数据,也需要一直等待到超 时才会被释放。如果过多的线程,被这样占据,也会导致在高并发场景下的无服务线程可用(该问题在 prefork模式下,同样会发生)
Apache event模型
Apache中最新的模式,2012年发布的apache 2.4.X系列正式支持event 模型,属于事件驱动模型(epoll)
每个进程响应多个请求,在现在版本里的已经是稳定可用的模式
它和worker模式很像,最大的区别在于,它解决了keepalive场景下长期被占用的线程的资源浪费问题 (某些线程因为被keepalive,空挂在哪里等待,中间几乎没有请求过来,甚至等到超时)
event MPM中,会有一个专门的线程来管理这些keepalive类型的线程
当有真实请求过来的时候,将请求传递给服务线程,执行完毕后,又允许它释放。这样增强了高并发场 景下的请求处理能力
优点:单线程响应多请求,占据更少的内存,高并发下表现更优秀,会有一个专门的线程来管理keepalive类型的线程,当有真实请求过来的时候,将请求传递给服务线程,执行完毕后,又允许它释放
缺点:没有线程安全控制
高性能Web服务器需要满足以下要求:
- 高吞吐量:能够处理大量并发请求,确保网站的响应速度。
- 低延迟:能够在最短时间内处理请求,提高用户体验。
- 高可用性:能够在故障发生时自动恢复,确保服务的持续运行。
- 扩展性:能够根据需求增加资源,支持网站的增长。
在接下来的部分中,我们将从这些需求入手,逐步构建一个高性能的Web服务器。
Nginx-高性能的 Web 服务端
nginx 有许多有点
- 支持高并发,消耗内存资源少
- 具有多种功能
- 网站web服务功能
- 网站负载均衡功能
- 网站缓存服务
- 在多种系统平台都可以进行部署
- nginx实现网络通讯时使用的时异步网络IO模型
基于Nginx的工作场景:
Nginx 进程结构
web请求处理机制
·多进程方式:服务器每接收到一个客户端请求就有服务器的主进程生成一个子进程响应客户端,直到用户关闭连接,这样的优势是处理速度快,子进程之间相互独立,但是如果访问过大会导致服务器资源耗尽而无法提供请求
·多线程方式:与多进程方式类似,但是每收到一个客户端请求会有服务进程派生出一个线程和此客户端进行交互,一个线程的开销远远小于一个进程,因此多线程方式在很大程度减轻了web服务器对系统资源的要求,但是多线程也有自己的缺点,即当多个线程位于同一个进程内工作的时候,可以相互访问同样的内存地址空间,所以他们相互影响,一旦主进程挂掉则所有子线程都不能工作了,IIS服务器使用了多线程的方式,需要间隔一段时间就重启一次才能稳定。
Nginx是多进程组织模型,而且是一个由Master主进程和Worker工作进程组成
Nginx的源码安装
源码安装
Nginx版本
Mainline version 主要开发版本,一般为奇数版本号,比如1.19
Stable version 当前最新稳定版,一般为偶数版本,如:1.20
Legacy versions 旧的稳定版,一般为偶数版本,如:1.18
Nginx安装可以使用yum或源码安装,但是推荐使用源码编译安装
yum的版本比较旧
编译安装可以更方便自定义相关路径
使用源码编译可以自定义相关功能,更方便业务的上的使用
Nginx 编译安装
编译器:
源码安装需要提前准备标准的编译器,GCC的全称是(GNU Compiler collection),其有GNU开发,并以 GPL即LGPL许可,是自由的类UNIX即苹果电脑Mac OS X操作系统的标准编译器,因为GCC原本只能处理C语 言,所以原名为GNU C语言编译器,后来得到快速发展,可以处理C++,Fortran,pascal,objective C, java以及Ada等其他语言,此外还需要Automake工具,以完成自动创建Makefile的工作,Nginx的一些模块 需要依赖第三方库,比如: pcre(支持rewrite),zlib(支持gzip模块)和openssl(支持ssl模块) 等。
官方源码包下载地址:
https://nginx.org/en/download.html
这里用的压缩包安装
[root@Nginx ~]# dnf install gcc pcre-devel zlib-devel openssl-devel -y #下载依赖项
[root@Nginx nginx-1.24.0]# useradd -s /sbin/nologin -M nginx
[root@Nginx nginx]# tar zxf nginx-1.24.0.tar.gz
[root@Nginx nginx-1.24.0]# useradd -s /sbin/nologin -M nginx
[root@Nginx nginx]# cd nginx-1.24.0/
[root@Nginx nginx-1.24.0]# ls
auto CHANGES.ru configure html Makefile objs src
CHANGES conf contrib LICENSE man README
[root@Nginx nginx-1.24.0]# ./configure --prefix=/usr/local/nginx \
--user=nginx \ # 指定nginx运行用户
--group=nginx \ # 指定nginx运行组
--with-http_ssl_module \ # 支持https://
--with-http_v2_module \ # 支持http版本2
--with-http_realip_module \ # 支持ip透传
--with-http_stub_status_module \ # 支持状态页面
--with-http_gzip_static_module \ # 支持压缩
--with-pcre \ # 支持正则
--with-stream \ # 支持tcp反向代理
--with-stream_ssl_module \ # 支持tcp的ssl加密
--with-stream_realip_module # 支持tcp的透传ip
[root@Nginx nginx-1.24.0]# make && make install
nginx完成安装以后,有四个主要的目录:
[root@Nginx nginx-1.24.0]# ls /usr/local/nginx/
conf html logs sbin
conf:保存nginx所有的配置文件,其中nginx.conf是nginx服务器的最核心最主要的配置文件,其他
的.conf则是用来配置nginx相关的功能的,例如fastcgi功能使用的是fastcgi.conf和fastcgi_params
两个文件,配置文件一般都有一个样板配置文件,是以.default为后缀,使用时可将其复制并将default后缀
去掉即可。
html目录中保存了nginx服务器的web文件,但是可以更改为其他目录保存web文件,另外还有一个50x的web
文件是默认的错误页面提示页面。
logs:用来保存nginx服务器的访问日志错误日志等日志,logs目录可以放在其他路径,比
如/var/logs/nginx里面。
sbin:保存nginx二进制启动脚本,可以接受不同的参数以实现不同的功能。
验证版本及编译参数
[root@Nginx ~]# vim ~/.bash_profile
export PATH=$PATH:/usr/local/nginx/sbin #写了过后可以直接用nginx启动nginx
[root@Nginx ~]# source ~/.bash_profile
[root@Nginx ~]# nginx -V
nginx version: nginx/1.24.0
built by gcc 11.4.1 20231218 (Red Hat 11.4.1-3) (GCC)
built with OpenSSL 3.0.7 1 Nov 2022
TLS SNI support enabled
configure arguments: --group=nginx --with-http_ssl_module --with-http_v2_module -
-with-http_realip_module --with-http_stub_status_module --withhttp_gzip_static_module
--with-pcre --with-stream --with-stream_ssl_module --
with-stream_realip_module
使用安装完成的二进制文件nginx
[root@Nginx ~]# nginx -v
nginx version: nginx/1.18.0
Usage: nginx [-?hvVtTq] [-s signal] [-c filename] [-p prefix] [-g directives]
Options:
-?,-h : this help
-v : show version and exit
-V : show version and configure options then exit #显示版本和编译参数
-t : test configuration and exit #测试配置文件是否异
-T : test configuration, dump it and exit #测试并打印
-q : suppress non-error messages during configuration testing #静默
模式
-s signal : send signal to a master process: stop, quit, reopen, reload #
发送信号,reload信号 会生成新的worker,但master不会重新生成
-p prefix : set prefix path (default: /etc/nginx/) #指定Nginx 目录
-c filename : set configuration file (default: /etc/nginx/nginx.conf) #
配置文件路径
-g directives : set global directives out of configuration file #设置全局指令,注意和
配置文件不要同时配置,否则冲突
nginx命令应用示例
[root@nginx-node1 ~]# vim /usr/local/nginx/conf/nginx.conf
nginx: [emerg] "worker_processes" directive is duplicate in /usr/local/nginx/conf/nginx.conf:3
root@Nginx ~]# nginx -g "worker_processes 6;"
[root@nginx-node1 ~]# ps aux | grep nginx
root 48148 0.0 0.1 9868 2052 ? Ss 14:00 0:00 nginx: master
process nginx -g worker_processes 6;
nobody 48149 0.0 0.2 14200 4868 ? S 14:00 0:00 nginx: worker
process
nobody 48150 0.0 0.2 14200 4868 ? S 14:00 0:00 nginx: worker
process
nobody 48151 0.0 0.2 14200 4868 ? S 14:00 0:00 nginx: worker
process
[root@nginx-node1 ~]# nginx -s quit
[root@nginx-node1 ~]# ps aux | grep nginx
root 48171 0.0 0.1 221664 2176 pts/0 S+ 14:04 0:00 grep --
color=auto nginx
#前台运行
[root@nginx-node1 ~]# nginx -g "daemon off;"
Nginx 启动文件
实现nginx的开机自启动
#写一个启动文件
[root@nginx-node1 ~]# vim /lib/systemd/system/nginx.service
[Unit]
Description=The NGINX HTTP and reverse proxy server
After=syslog.target network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target
[Service]
Type=forking
PIDFile=/usr/local/nginx/logs/nginx.pid
ExecStartPre=/usr/local/nginx/sbin/nginx -t
ExecStart=/usr/local/nginx/sbin/nginx
ExecReload=/usr/local/nginx/sbin/nginx -s reload
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true
[Install]
WantedBy=multi-user.target
[root@nginx-node1 ~]# systemctl daemon-reload
[root@nginx-node1 ~]# systemctl enable --now nginx
Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service → /usr/lib/systemd/system/nginx.service.
Nginx 核心配置详解
配置文件说明
nginx 官方帮助文档:http://nginx.org/en/docs/
Nginx的配置文件的组成部分:
·主配置文件:nginx.conf
·子配置文件: include conf.d/*.conf
·fastcgi, uwsgi,cgi 等协议相关的配置文件
·mime.types:支持的mime类型,MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型,MIME消息能包含文本、图像、音频、视频以及其他应用程序专用的数据,是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开。多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。
nginx 配置文件格式说明
配置文件由指令与指令块构成
每条指令以;分号结尾,指令与值之间以空格符号分隔
可以将多条指令放在同一行,用分号分隔即可,但可读性差,不推荐
指令块以{ }大括号将多条指令组织在一起,且可以嵌套指令块
include语句允许组合多个配置文件以提升可维护性
使用#符号添加注释,提高可读性
使用$符号使用变量
部分指令的参数支持正则表达式
Nginx 主配置文件的配置指令方式:
directive value [value2 ...];
注意
(1) 指令必须以分号结尾
(2) 支持使用配置变量
内建变量:由Nginx模块引入,可直接引用
自定义变量:由用户使用set命令定义,格式: set variable_name value;
引用变量:$variable_name
主配置文件结构:四部分
main block:主配置段,即全局配置段,对http,mail都有效
#事件驱动相关的配置
event {
...
}
#http/https 协议相关配置段
http {
...
}
#默认配置文件不包括下面两个块
#mail 协议相关配置段
mail {
...
}
#stream 服务器相关配置段
stream {
...
}
默认的nginx.conf 配置文件格式说明
[root@nginx-node1 ~]# vim /usr/local/nginx/conf/nginx.conf
#全局配置端,对全局生效,主要设置nginx的启动用户/组,启动的工作进程数量,工作模式,Nginx的PID路径,日志路径等。
#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 { #设置块,主要影响nginx服务器与用户的网络连接,比如是否允许同时接受多个网络连接,使用哪种事件驱动模型
#处理请求,每个工作进程可以同时支持的最大连接数,是否开启对多工作进程下的网络连接进行序列化等。
worker_connections 1024; #设置单个nginx工作进程可以接受的最大并发,作为web服务器的时候最大并发数为worker_connections *worker_processes,
#作为反向代理的时候为(worker_connections * worker_processes)/2
}
http { ##http块是Nginx服务器配置中的重要部分,缓存、代理和日志格式定义等绝大多数功能和第三方模块都可以在这设置,
#http块可以包含多个server块,而一个server块中又可以包含多个location块,
#server块可以配置文件引入、MIME-Type定义、日志自定义、是否启用sendfile、连接超时时间和单个链接的请求上限等。
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; #作为web服务器的时候打开sendfile加快静态文件传输,指定是否使用sendfile系统调用来传输文件
#sendfile系统调用在两个文件描述符之间直接传递数据(完全在内核中操作)从而避免了数据在内核缓冲区和用户缓冲区之间的拷贝,操作效率很高,被称之为零拷贝,
#硬盘 >> kernel buffer (快速拷贝到kernelsocketbuffer) >>协议栈。
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65; #长连接超时时间,单位是秒
#gzip on;
server { #设置一个虚拟机主机,可以包含自己的全局块,同时也可以包含多个location模块
##比如本虚拟机监听的端口、本虚拟机的名称和IP配置,多个server可以使用一个端口比如都使用80端口提供web服务
listen 80; #配置server监听的端口
server_name localhost; #本server的名称,当访问此名称的时候nginx会调用当前serevr内部的配置进程匹配。
#charset koi8-r;
#access_log logs/host.access.log main;
location / { #location其实是server的一个指令,为nginx服务器提供比较多而且灵活的指令都是在location中体现的,
#主要是基于nginx接受到的请求字符串对用户请求的UIL进行匹配,并对特定的指令进行处理
#包括地址重定向、数据缓存和应答控制等功能都是在这部分实现,另外很多第三方模块的配置也是在location模块中配置。
root html; #相当于默认页面的目录名称,默认是安装目录的相对路径,可以使用绝对路径配置。
index index.html index.htm; #默认的页面文件名称
}
#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 { #location处理对应的不同错误码的页面定义到/50x.html,这个跟对应其server中定义的目录下。
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;
#}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
}
#和邮件相关的配置
#mail {
# ...
# } mail 协议相关配置段
#tcp代理配置,1.9版本以上支持
#stream {
# ...
# } stream 服务器相关配置段
#导入其他路径的配置文件
#include /apps/nginx/conf.d/*.conf
}
配置文件如下:
[root@nginx-node1 ~]# vim /usr/local/nginx/conf/nginx.conf
#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;
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html;
index index.html index.htm;
}
#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;
#}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
}
Nginx 压缩功能
Nginx支持对指定类型的文件进行压缩然后再传输给客户端,而且压缩还可以设置压缩比例,压缩后的文 件大小将比源文件显著变小,样有助于降低出口带宽的利用率,降低企业的IT支出,不过会占用相 应的CPU资源。 Nginx对文件的压缩功能是依赖于模块 ngx_http_gzip_module,默认是内置模块
配置指令如下
创建一个小于1k的文件和一个大于1k的文件
[root@node1 nginx]# echo hhhh > /www/web/test1.html
[root@node1 nginx]# cat /usr/local/nginx/logs/access.log > /www/web/test2.html
修改压缩版块
[root@node1 nginx]# vim /usr/local/nginx/conf/nginx.conf
gzip on;
gzip_comp_level 5;
gzip_min_length 1k;
gzip_types text/plain application/javascript application/x-javascript text/css
application/xml text/javascript application/x-httpd-php image/gif image/png;
gzip_vary on;
查看效果
[root@node1 nginx]# curl --head --compressed www.wang.org/test1.html
HTTP/1.1 200 OK
Server: nginx/1.26.1
Date: Fri, 16 Aug 2024 08:39:56 GMT
Content-Type: text/html
Content-Length: 5
Last-Modified: Fri, 16 Aug 2024 08:26:39 GMT
Connection: keep-alive
ETag: "66bf0d3f-5"
Accept-Ranges: bytes
[root@node1 nginx]# curl --head --compressed www.wang.org/test2.html
HTTP/1.1 200 OK
Server: nginx/1.26.1
Date: Fri, 16 Aug 2024 08:40:08 GMT
Content-Type: text/html
Last-Modified: Fri, 16 Aug 2024 08:27:14 GMT
Connection: keep-alive
Vary: Accept-Encoding
ETag: W/"66bf0d62-50af"
Content-Encoding: gzip
Nginx 防盗链
防盗链基于客户端携带的referer实现,referer是记录打开一个页面之前记录是从哪个页面跳转过来的标 记信息,如果别人只链接了自己网站图片或某个单独的资源,而不是打开了网站的整个页面,这就是盗链,referer就是之前的那个网站域名,正常的referer信息有以下几种:
直接访问自己的网站referer的信息就是none,通过别人的网站链接到自己网站的referer就有信息
none: #请求报文首部没有referer首部,
#比如用户直接在浏览器输入域名访问web网站,就没有referer信息。
blocked: #请求报文有referer首部,但无有效值,比如为空。
server_names: #referer首部中包含本主机名及即nginx 监听的server_name。
arbitrary_string: #自定义指定字符串,但可使用*作通配符。示例: *.wang.org
www.wang.*
regular expression: #被指定的正则表达式模式匹配到的字符串,要使用~开头,例如:
~.*\.wang\.com
为了模拟盗链,在这里让101为网站服务站点,102为网关服务器,103访问101进行盗链。
修改 102 Nginx 配置文件:
此时在浏览器输入102ip,可以正常访问101站点。
如果不想被盗链,则对101站点服务修改Nginx配置文件,防盗链的配置可以在任意的 location 模块下设置,不能在 server 下,你不想让别人盗链哪个资源就在那个资源的 location 模块下设置防盗链:
测试效果,用102ip去访问,css等静态资源返回403,获取不到
1.# 格式:
valid_referers none | blocked | server_names | strings ....;
--none:允许没有http_refer的请求访问资源,检测 Referer 头域不存在的情况,则可以访问。
--blocked:检测 Referer 头域的值被防火墙或者代理服务器删除或伪装的情况。这种情况该头域的值不以
“http://” 或 “https://” 开头。允许不是http://开头的,不带协议的请求访问资源。
--server_names :只允许指定ip/域名来的请求访问资源(白名单)。可设置一个或多个 URL ,检测 Referer 头域的值是否是这些 URL 中的某一个。在生产环境中尽量使用域名,不使用ip。
2.# 举例
valid_referers 192.168.44.101;
if ($invalid_referer) {
return 403;
}
Nginx的反向代理
Nginx作为一款高性能的Web服务器和反向代理服务器,具有以下优势:
高性能:Nginx采用事件驱动的异步处理方式,能够处理大量并发连接。
负载均衡:Nginx支持负载均衡配置,可以将请求分发到多个后端服务器上。
缓存功能:Nginx可以缓存静态资源,提高访问速度。
可扩展性:Nginx支持丰富的插件和模块,可以满足不同需求。
配置反向代理规则
在nginx.conf文件中添加如下配置:
server {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://backend_server_ip:port;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
执行命令重启Nginx服务,使配置生效:
sudo systemctl restart nginx
测试反向代理
- 修改本地Hosts文件,将yourdomain.com指向Nginx服务器IP。
- 在浏览器中访问yourdomain.com,验证是否成功代理到后端服务器。
- 检查Nginx日志以确认请求转发情况。
Nginx 四层负载均衡
配置四层负载均衡方法:
stream { #定义stream相关的服务;Context:main
upstream backend { #定义后端服务器
hash $remote_addr consistent; #定义调度算法
server backend1.example.com:12345 weight=5; #定义具体server
server 127.0.0.1:12345 max_fails=3 fail_timeout=30s;
server unix:/tmp/backend3;
}
server { #定义server
listen 12345; #监听IP:PORT
proxy_connect_timeout 1s; #连接超时时间
proxy_timeout 3s; #转发超时时间
proxy_pass backend; #转发到具体服务器组
}
}
例子:
stream{
upstream dns {
server 172.25.254.10:53 fail_timeout=15s max_fails=3;
server 172.25.254.20:53 fail_timeout=15s max_fails=3;
}
server{
listen 53 udp reuseport;
proxy_timeout 20s;
proxy_pass dns;
}
}
ginx在1.9.0版本开始支持tcp模式的负载均衡,在1.9.13版本开始支持udp协议的负载,udp主要用于DNS的域名解析,其配置方式和指令和http 代理类似,其基于ngx_stream_proxy_module模块实现tcp负载,另外基于模块ngx_stream_upstream_module实现后端服务器分组转发、权重分配、状态监测、调度算法等高级功能。
如果编译安装,需要指定 --with-stream 选项才能支持ngx_stream_proxy_module模块
官方文档:
https://nginx.org/en/docs/stream/ngx_stream_proxy_module.html
tcp负载均衡配置参数
#注意:tcp的负载均衡要卸载http语句块的外面
stream { #定义stream相关的服务;Context:main
upstream backend { #定义后端服务器
hash $remote_addr consistent; #定义调度算法
server backend1.example.com:12345 weight=5; #定义具体server
server 127.0.0.1:12345 max_fails=3 fail_timeout=30s;
server unix:/tmp/backend3;
}
upstream dns { #定义后端服务器
server 10.0.0.1:53; #定义具体server
server dns.example.com:53;
}
server { #定义server
listen 12345; #监听IP:PORT
proxy_connect_timeout 1s; #连接超时时间
proxy_timeout 3s; #转发超时时间
proxy_pass backend; #转发到具体服务器组
}
server {
listen 127.0.0.1:53 udp reuseport;
proxy_timeout 20s;
proxy_pass dns;
}
server {
listen [::1]:12345;
proxy_pass unix:/tmp/stream.socket;
}
}
udp 负载均衡实例: DNS
配置dns的负载均衡
#添加一个tcp的子配置路径
[root@nginx-node1 ~]# vim /usr/local/nginx/conf/nginx.conf
.....
include "/usr/local/nginx/tcpconf.d/*.conf";
.....
[root@nginx-node1 ~]# cd /usr/local/nginx/tcpconf.d
#编写配置文件
[root@nginx-node1 tcpconf.d]# vim dns.conf
stream{
upstream dns {
server 172.25.254.10:53 fail_timeout=15s max_fails=3;
server 172.25.254.20:53 fail_timeout=15s max_fails=3;
}
server{
listen 53 udp reuseport;
proxy_timeout 20s;
proxy_pass dns;
}
}
[root@nginx-node1 tcpconf.d]# nginx -s reload
[root@nginx-node1 nginx]# dig www.zhang.org @172.25.254.100
; <<>> DiG 9.16.23-RH <<>> www.zhang.org @172.25.254.100
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 1294
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
; COOKIE: 8b8cf765b85493050100000066c2442f0a1af9f0eec6183d (good)
;; QUESTION SECTION:
;www.zhang.org. IN A
;; ANSWER SECTION:
www.zhang.org. 86400 IN A 172.25.254.10
;; Query time: 0 msec
;; SERVER: 172.25.254.100#53(172.25.254.100)
;; WHEN: Sun Aug 18 21:23:05 CST 2024
;; MSG SIZE rcvd: 86
负载均衡实例: MySQL
后端服务器安装 MySQL
#下载mariadb-server
[root@web10 ~]# yum install mariadb-server -y
[root@web20 ~]# yum install mariadb-server -y
#添加id以便区分
[root@web10 ~]# vim /etc/my.cnf.d/mariadb-server.cnf
[mysqld]
server-id=10
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
log-error=/var/log/mariadb/mariadb.log
pid-file=/run/mariadb/mariadb.pid
[root@web20 ~]# vim /etc/my.cnf.d/mariadb-server.cnf
[mysqld]
server-id=10
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
log-error=/var/log/mariadb/mariadb.log
pid-file=/run/mariadb/mariadb.pid
#配置数据库
[root@web10 ~]# mysql
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 3
Server version: 10.5.22-MariaDB MariaDB Server
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]> CREATE USER haha@"%" identified by "redhat";
Query OK, 0 rows affected (0.001 sec)
MariaDB [(none)]> GRANT ALL ON *.* TO haha@'%';
Query OK, 0 rows affected (0.001 sec)
MariaDB [(none)]> quit
Bye
[root@web20 ~]# mysql
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 3
Server version: 10.5.22-MariaDB MariaDB Server
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]> create user haha@'%' identified by "redhat";
Query OK, 0 rows affected (0.001 sec)
MariaDB [(none)]> grant all on *.* to haha@'%';
Query OK, 0 rows affected (0.001 sec)
MariaDB [(none)]> quit
Bye
开始配置mysql的负载均衡
stream {
upstream dns {
server 172.25.254.10:53 fail_timeout=15s max_fails=3;
server 172.25.254.20:53 fail_timeout=15s max_fails=3;
}
upstream mysql {
server 172.25.254.10:3306 fail_timeout=15s max_fails=3;
server 172.25.254.20:3306 fail_timeout=15s max_fails=3;
}
server {
listen 3306;
proxy_timeout 60s;
proxy_pass mysql;
}
server {
listen 53 udp reuseport;
proxy_timeout 20s;
proxy_pass dns;
}
}
[root@nginx-node1 ~]# nginx -s reload
[root@nginx-node1 ~]# yum install mariadb -y
#测试
[root@nginx-node1 ~]# mysql -u haha -p -h 172.25.254.100
Enter password:
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 4
Server version: 10.5.22-MariaDB MariaDB Server
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]> select @@server_id;
+-------------+
| @@server_id |
+-------------+
| 20 |
+-------------+
1 row in set (0.001 sec)
MariaDB [(none)]> exit
Bye
[root@nginx-node1 ~]# mysql -u haha -p -h 172.25.254.100
Enter password:
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 8
Server version: 10.5.22-MariaDB MariaDB Server
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]> select @@server_id;
+-------------+
| @@server_id |
+-------------+
| 10 |
+-------------+
1 row in set (0.001 sec)
MariaDB [(none)]>
平滑升级和回滚
有时候我们需要对Nginx版本进行升级以满足对其功能的需求,例如添加新模块,需要新功能,而此时 Nginx又在跑着业务无法停掉,这时我们就可能选择平滑升级
平滑升级流程
将旧Nginx二进制文件换成新Nginx程序文件(注意先备份)
·向master进程发送USR2信号
·master进程修改pid文件名加上后缀.oldbin,成为nginx.pid.oldbin
·master进程用新Nginx文件启动新master进程成为旧master的子进程,系统中将有新旧两个Nginx主进程共同提供Web服务,当前新的请求仍然由旧Nginx的worker进程进行处理,将新生成的master进程的PID存放至新生成的pid文件nginx.pid
·向旧的Nginx服务进程发送WINCH信号,使旧的Nginx worker进程平滑停止
·向旧master进程发送QUIT信号,关闭老master,并删除Nginx.pid.oldbin文件
·如果发现升级有问题,可以回滚∶向老master发送HUP,向新master发送QUIT
平滑升级和回滚案例
平滑
[root@nginx-node1 ~]# wget https://nginx.org/download/nginx-1.26.2.tar.gz
[root@nginx-node1 ~]# tar zxf echo-nginx-module-0.63.tar.gz
[root@nginx-node1 ~]# tar zxf nginx-1.26.2.tar.gz
[root@nginx-node1 ~]# cd nginx-1.26.2/
#开始编译新版本
[root@nginx-node1 nginx-1.26.2]#./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --add-module=/root/echo-nginx-module-0.63 --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_stub_status_module --with-http_gzip_static_module --with-pcre --with-stream --with-stream_ssl_module --with-stream_realip_module
#只要make无需要make install
[root@nginx-node1 nginx-1.26.2]# make
#查看两个版本
[root@nginx-node1 nginx-1.26.2]# ll objs/nginx /usr/local/nginx/sbin/nginx
-rwxr-xr-x 1 root root 6170608 8月 15 19:39 objs/nginx
-rwxr-xr-x 1 root root 1234424 8月 15 19:03 /usr/local/nginx/sbin/nginx
#把之前的旧版的nginx命令备份
[root@nginx-node1 nginx-1.26.2]# cd /usr/local/nginx/sbin/
[root@nginx-node1 sbin]# ls
nginx
[root@nginx-node1 sbin]# cp nginx nginx.old
[root@nginx-node1 sbin]# ls
nginx nginx.old
#把新版本的nginx命令复制过去
[root@nginx-node1 sbin]# \cp -f /root/nginx-1.26.2/objs/nginx /usr/local/nginx/sbin
#检测一下有没有问题
[root@nginx-node1 sbin]# nginx -t
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
#查看端口,并启动平滑升级可执行程序
#USR2 平滑升级可执行程序,将存储有旧版本主进程PID的文件重命名为nginx.pid.oldbin,并启动新的nginx
#此时两个master的进程都在运行,只是旧的master不在监听,由新的master监听80
#此时Nginx开启一个新的master进程,这个master进程会生成新的worker进程,这就是升级后的Nginx进程,此时老的进程不会自动退出,但是当接收到新的请求不作处理而是交给新的进程处理。
[root@nginx-node1 sbin]# ps aux | grep nginx
root 42072 0.0 0.0 9908 2068 ? Ss 19:43 0:00 nginx: master process nginx
nginx 42073 0.0 0.1 14252 5268 ? S 19:43 0:00 nginx: worker process
root 42079 0.0 0.0 221812 2304 pts/0 S+ 19:44 0:00 grep --color=auto nginx
[root@nginx-node1 sbin]# kill -USR2 42072 #注意是写主程序的进程
[root@nginx-node1 sbin]# ps aux | grep nginx
root 42072 0.0 0.0 9908 2580 ? Ss 19:43 0:00 nginx: master process nginx
nginx 42073 0.0 0.1 14252 5268 ? S 19:43 0:00 nginx: worker process
root 42080 0.0 0.1 9776 6528 ? S 19:46 0:00 nginx: master process nginx
nginx 42081 0.0 0.1 14240 5008 ? S 19:46 0:00 nginx: worker process
root 42083 0.0 0.0 221812 2304 pts/0 S+ 19:46 0:00 grep --color=auto nginx
#发现仍然是旧的生效
[root@nginx-node1 sbin]# curl -I 172.25.254.100
HTTP/1.1 200 OK
Server: nginx/1.24.0 ##依旧是旧版本生生效
Date: Thu, 15 Aug 2024 11:49:22 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Thu, 15 Aug 2024 11:03:45 GMT
Connection: keep-alive
ETag: "66bde091-267"
Accept-Ranges: bytes
#回收旧的进程
[root@nginx-node1 sbin]# kill -WINCH 42072
[root@nginx-node1 sbin]# ps aux | grep nginx
root 42072 0.0 0.0 9908 2580 ? Ss 19:43 0:00 nginx: master process nginx
root 42080 0.0 0.1 9776 6528 ? S 19:46 0:00 nginx: master process nginx
nginx 42081 0.0 0.1 14240 5008 ? S 19:46 0:00 nginx: worker process
root 42089 0.0 0.0 221812 2304 pts/0 S+ 19:51 0:00 grep --color=auto nginx
[root@nginx-node1 sbin]# curl -I 172.25.254.100
HTTP/1.1 200 OK
Server: nginx/1.26.2
Date: Thu, 15 Aug 2024 11:51:26 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Thu, 15 Aug 2024 11:03:45 GMT
Connection: keep-alive
ETag: "66bde091-267"
Accept-Ranges: bytes
回滚
[root@nginx-node1 sbin]# ls
nginx nginx.old
[root@nginx-node1 sbin]# cp nginx nginx.new
[root@nginx-node1 sbin]# ls
nginx nginx.new nginx.old
[root@nginx-node1 sbin]# \cp -f nginx.old nginx
[root@nginx-node1 sbin]# ls
nginx nginx.new nginx.old
[root@nginx-node1 sbin]# ps aux | grep nginx
root 51750 0.0 0.0 9868 2436 ? Ss 22:14 0:00 nginx: master process nginx
root 51760 0.0 0.1 9776 6528 ? S 22:14 0:00 nginx: master process nginx
nginx 51761 0.0 0.1 14240 5264 ? S 22:14 0:00 nginx: worker process
root 51767 0.0 0.0 221812 2304 pts/0 S+ 22:16 0:00 grep --color=auto nginx
[root@nginx-node1 sbin]# kill -HUP 51750
[root@nginx-node1 sbin]# ps aux | grep nginx
root 51750 0.0 0.0 9868 2436 ? Ss 22:14 0:00 nginx: master process nginx
root 51760 0.0 0.1 9776 6528 ? S 22:14 0:00 nginx: master process nginx
nginx 51761 0.0 0.1 14240 5264 ? S 22:14 0:00 nginx: worker process
nginx 51768 0.0 0.1 14200 4996 ? S 22:17 0:00 nginx: worker process
root 51770 0.0 0.0 221812 2304 pts/0 S+ 22:17 0:00 grep --color=auto nginx
#回收旧的进程
[root@nginx-node1 sbin]# kill -WINCH 51760
[root@nginx-node1 sbin]# ps aux | grep nginx
root 51750 0.0 0.0 9868 2436 ? Ss 22:14 0:00 nginx: master process nginx
root 51760 0.0 0.1 9776 6528 ? S 22:14 0:00 nginx: master process nginx
nginx 51768 0.0 0.1 14200 4996 ? S 22:17 0:00 nginx: worker process
root 51773 0.0 0.0 221812 2304 pts/0 S+ 22:22 0:00 grep --color=auto nginx
[root@nginx-node1 sbin]# kill -9 51760
[root@nginx-node1 sbin]# ps aux | grep nginx
root 51750 0.0 0.0 9868 2436 ? Ss 22:14 0:00 nginx: master process nginx
nginx 51768 0.0 0.1 14200 5252 ? S 22:17 0:00 nginx: worker process
root 51780 0.0 0.0 221812 2304 pts/0 R+ 22:29 0:00 grep --color=auto nginx
[root@nginx-node1 sbin]# curl -I 172.25.254.100
HTTP/1.1 200 OK
Server: nginx/1.24.0
Date: Thu, 15 Aug 2024 14:26:25 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Thu, 15 Aug 2024 13:50:01 GMT
Connection: keep-alive
ETag: "66be0789-267"
Accept-Ranges: bytes
服务端I/O流程
磁盘
网络
磁盘I/O
计算方式:
机械磁盘的寻道时间、旋转延迟和数据传输时间:
寻道时间:是指磁头移动到正确的磁道上所花费的时间,寻道时间越短则 I/O 处理就越快,目前磁盘的寻道时
间一般在 3-15 毫秒左右。
旋转延迟:是指将磁盘片旋转到数据所在的扇区到磁头下面所花费的时间,旋转延迟取决于磁盘的转速,通常
使用磁盘旋转一周所需要时间的 1/2 之一表示,比如 7200 转的磁盘平均训传延迟大约为
60*1000/7200/2=4.17 毫秒,公式的意思为 (每分钟 60 秒 *1000 毫秒每秒 /7200 转每分 /2 ),如果是
15000 转的则为 60*1000/15000/2=2 毫秒。 1.2.4.2 网络 I/O
网络通信就是网络协议栈到用户空间进程的 IO 就是网络 IO
网络 I/O 处理过程
获取请求数据,客户端与服务器建立连接发出请求,服务器接受请求( 1-3 )
构建响应,当服务器接收完请求,并在用户空间处理客户端的请求,直到构建响应完成( 4 )
返回数据,服务器将已构建好的响应再通过内核空间的网络 I/O 发还给客户端( 5-7 )
不论磁盘和网络 I/O
每次 I/O ,都要经由两个阶段:
第一步:将数据从文件先加载至内核内存空间(缓冲区),等待数据准备完成,时间较长
第二步:将数据从内核缓冲区复制到用户空间的进程的内存中,时间较短
数据传输时间:指的是读取到数据后传输数据的时间,主要取决于传输速率,这个值等于数据大小除以传输速
率,目前的磁盘接口每秒的传输速度可以达到 600MB ,因此可以忽略不计。
常见的机械磁盘平均寻道时间值:
7200 转 / 分的磁盘平均物理寻道时间: 9 毫秒
10000 转 / 分的磁盘平均物理寻道时间: 6 毫秒
15000 转 / 分的磁盘平均物理寻道时间: 4 毫秒
常见磁盘的平均延迟时间:
7200 转的机械盘平均延迟: 60*1000/7200/2 = 4.17ms
10000 转的机械盘平均延迟: 60*1000/10000/2 = 3ms
15000 转的机械盘平均延迟: 60*1000/15000/2 = 2ms
每秒最大 IOPS 的计算方法:
7200 转的磁盘 IOPS 计算方式: 1000 毫秒 /(9 毫秒的寻道时间 +4.17 毫秒的平均旋转延迟时
间 )=1000/13.13=75.9 IOPS
10000 转的磁盘的 IOPS 计算方式: 1000 毫秒 /(6 毫秒的寻道时间 +3 毫秒的平均旋转延迟时
间 )=1000/9=111IOPS
15000 转的磁盘的 IOPS 计算方式: 15000 毫秒 /(4 毫秒的寻道时间 +2 毫秒的平均旋转延迟时
间 )=1000/6=166.6 IOPS
网络I/O
处理过程:
获取请求数据,客户端与服务器建立连接发出请求,服务器接受请求( 1-3 )
构建响应,当服务器接收完请求,并在用户空间处理客户端的请求,直到构建响应完成( 4 )
返回数据,服务器将已构建好的响应再通过内核空间的网络 I/O 发还给客户端( 5-7 )
磁盘和网络I/O:
每次 I/O ,都要经由两个阶段:
第一步:将数据从文件先加载至内核内存空间(缓冲区),等待数据准备完成,时间较长
第二步:将数据从内核缓冲区复制到用户空间的进程的内存中,时间较短
阻塞IO模型:你投放鱼饵后,就等待鱼儿咬钩。在等待鱼咬钩的过程中,不能做其他事情,只能一直盯着浮标,直到有鱼上钩。
非阻塞IO模型:你在每次投放鱼饵后,你不会傻等鱼儿上钩,而是不断定期检查浮标的状态。如果没有鱼上钩,就继续做其他事情,直到鱼上钩就收杆。
IO复用模型:你可能会使用多根钓竿,使用10个鱼竿,100个鱼竿,可以是无穷的鱼竿,当有任何一根钓竿的浮标动了,你就知道有鱼上钩,然后去收杆。
信号驱动IO模型:你可能会使用自动报警装置。每次投放鱼饵后,设定了一个报警器,当有鱼上钩时触发报警器发出信号。在听到报警器响时,立即去收杆。
异步IO模型:你会雇佣一个专业的钓鱼服务公司。他们会使用不同的钓鱼方式,但是你完全不用关心,你只要他们通知你来收杆就可以了。
阻塞IO
阻塞IO: 在内核将数据准备好之前, 系统调用会一直等待. 所有的套接字, 默认都是阻塞方式。
阻塞IO是最常见的IO模型。
非阻塞IO
非阻塞IO: 如果内核还未将数据准备好, 系统调用仍然会直接返回, 并且返回EWOULDBLOCK错误码。
非阻塞IO往往需要程序员循环的方式反复尝试读写文件描述符,这个过程称为轮询,这对CPU来说是较大的浪费,一般只有特定场景下才使用。
信号驱动IO
信号驱动IO: 内核将数据准备好的时候, 使用SIGIO信号通知应用程序进行IO操作
IO多路转接
IO多路转接: 虽然从流程图上看起来和阻塞IO类似. 实际上最核心在于IO多路转接能够同时等待多个文件描述符的就绪状态。
异步IO
异步IO: 由内核在数据拷贝完成时, 通知应用程序(而信号驱动是告诉应用程序何时可以开始拷贝数据)。
总结:任何IO过程中,都包含两个步骤:第一是等待,第二是拷贝。而且在实际的应用场景中,等待消耗的时间往往都远远高于拷贝的时间,让IO更高效,最核心的办法就是让等待的时间尽量少。
零拷贝介绍
传统的 Linux 系统的标准 I/O 接口( read 、 write )是基于数据拷贝的,也就是数据都 copy_to_user 或者 copy_from_user ,这样做的好处是,通过中间缓存的机制,减少磁盘 I/O 的操作,但是坏处也很明显,大量数据的拷贝,用户态和内核态的频繁切换,会消耗大量的 CPU 资源,严重影响数据传输的性能,统计表明,在Linux 协议栈中,数据包在内核态和用户态之间的拷贝所用的时间甚至占到了数据包整个处理流程时间的57.1%。
零拷贝就是上述问题的一个解决方案,通过尽量避免拷贝操作来缓解 CPU 的压力。零拷贝并没有真正做到“0” 拷贝,它更多是一种思想,很多的零拷贝技术都是基于这个思想去做的优化。
FastCGI
什么是FastCGI?
FastCGI是进程管理器。CGI会将webserver的请求转发给会解决请求的人。PHP处于一会儿上班一会儿下班的状态。
PHP-FPM:FastCGI Process Manager。worker进程一般会有多个,每个进程会嵌入一个PHP解析器,进行PHP代码的处理。
安装
fcgi-2.4.1-SNAP-0910052249.tar.gz
spawn-fcgi-1.6.5.tar.gz
fastCGI程序怎么写
- 发送与接收数据
标准输入和标准输出已经被重定向到 nginx与fastCGI进程管理器的通信fd 了
因此,使用printf就是向nginx发送数据,getchar就是接收nginx转发过来的命令
- 请求体的长度
get方式肯定没有请求体,无Content-Length,无Content-Type 而post方式有
该信息被初始化到环境变量中了,在fastCGI程序中可以使用getenv("CONTENT_LENGTH")得到长度
-char * contentLength转10进制数
int len = strtol(contentLength, NULL, 10);
-读写数据块
fread/fwrite是标准C库函数,而read/write是linux系统库函数,因此这里使用通用的前者。
//由于标准输入与输出已经被重定向过了,这里的FILE *stream直接写stdin或者stdout即可
#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
// http://localhost/login?user=zhang3&passwd=123456&age=12&sex=man
// 要包含的头文件
#include "fcgi_config.h" // 可选,看是否要读取配置文件
#include "fcgi_stdio.h" // 必须
// 编写代码的流程
int main()
{
// FCGI_Accept()是一个阻塞函数, nginx给fastcgi程序发送数据的时候解除阻塞
while (FCGI_Accept() >= 0)
{
// 1. 接收数据
// 1.1 get方式提交数据 - 数据在请求行的第二部分
// user=zhang3&passwd=123456&age=12&sex=man
char *text = getenv("QUERY_STRING");
// 1.2 post方式提交数据
char *contentLength = getenv("CONTENT_LENGTH");
// 根据长度大小判断是否需要循环
// 2. 按照业务流程进行处理
// 3. 将处理结果发送给nginx
// 数据回发的时候, 需要告诉nginx处理结果的格式 - 假设是html格式
printf("Content-type: text/html;charset=utf-8\r\n\r\n");
printf("<html>处理结果</html>");
}
}
实际使用
1 启动nginx 2 启动spawn-fcgi 3 打开http://192.168.232.132/demo.html
先编译一份fastcgi程序,这里将就使用example提供的代码。
book@100ask:~/Downloads/fcgi-2.4.1-SNAP-0910052249/examples$ gcc echo.c -o app -lfcgi
book@100ask:~/Downloads/fcgi-2.4.1-SNAP-0910052249/examples$ ldd app
linux-vdso.so.1 (0x00007ffeb4be2000)
libfcgi.so.0 => not found
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f36a622a000)
/lib64/ld-linux-x86-64.so.2 (0x00007f36a681e000)
由上可知,libfcgi.so动态库找不到(注意,后面的.0是版本号,不要管),因此先find找一下:
book@100ask:~/Downloads/fcgi-2.4.1-SNAP-0910052249/examples$ sudo find / -name libfcgi.so
find: ‘/run/user/1001/gvfs’: Permission denied
/home/book/Downloads/fcgi-2.4.1-SNAP-0910052249/libfcgi/.libs/libfcgi.so
/usr/local/lib/libfcgi.so
可以看到,/usr/local/lib目录下有该动态库文件,需要把这个路径加入到 /etc/ld.so.conf ,然后执行sudo ldconfig。
/etc/ld.so.conf记录了编译时使用的动态库的路径,也就是加载so库的路径。安装源码包时,如果没有指定--prefix为/usr/lib,就会默认安装在usr/local下,也就是动态库在/usr/local/lib下,导致系统找不到该动态库。因此,最好将/usr/local/lib这个目录添加到该文件中
启动fcgi进程 spawn-fcgi -a 127.0.0.1 -p 10000 -f ./app
fastcgi.conf里是fastCGI的环境变量
文件位置:/usr/local/nginx/conf/fastcgi.conf
php相关配置优化
第一步:指定pid文件存放位置php-fpm.conf
[root@Nginx ~]# cd /usr/local/php/etc
[root@Nginx etc]# cp php-fpm.conf.default php-fpm.conf
[root@Nginx etc]# vim php-fpm.conf
去掉注释
pid = run/php-fpm.pid #指定pid文件存放位置
第二步:创建修改监听端口的配置文件www.conf
[root@Nginx etc]# cd php-fpm.d/
[root@Nginx php-fpm.d]# cp www.conf.default www.conf
第三步:复制修改时区的配置文件php.ini
[root@Nginx php-fpm.d]# cd /root/php-8.3.9/
[root@Nginx php-8.3.9]# cp php.ini-production /usr/local/php/etc/php.ini
[root@Nginx ~]# vim /usr/local/php/etc/php.ini
[Date]
; Defines the default timezone used by the date functions
; https://php.net/date.timezone
date.timezone = Asia/Shanghai #修改时区
第四步:复制php-fpm.service
[root@Nginx ~]# cd /root/php-8.3.9/
[root@Nginx php-8.3.9]# cp sapi/fpm/php-fpm.service /lib/systemd/system/
# Mounts the /usr, /boot, and /etc directories read-only for processes invoked by
this unit.
#ProtectSystem=full #注释该内容
查看端口
[root@Nginx php-8.3.9]# systemctl start php-fpm.service
[root@Nginx php-8.3.9]# netstat -antlupe | grep php
tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN 0
820758 176202/php-fpm: mas
准备php测试页面
Nginx配置转发
Nginx安装完成之后默认生成了与fastcgi的相关配置文件,一般保存在nginx的安装路径的conf目录当 中,比如/apps/nginx/conf/fastcgi.conf、/apps/nginx/conf/fastcgi_params。
server {
listen 80;
server_name php.wang.org;
root /www/php;
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi.conf;
}
}
#重启Nginx并访问web测试
[root@Nginx ~]# nginx -s reload
php的动态扩展模块(php的缓存模块)
安装memcache模块
[root@Nginx ~]# tar zxf memcache-8.2.tgz
[root@Nginx ~]# cd memcache-8.2/
[root@Nginx memcache-8.2]# yum install autoconf
[root@Nginx memcache-8.2]# phpize
[root@Nginx memcache-8.2]# ./configure && make && make install
Installing shared extensions: /usr/local/php/lib/php/extensions/no-debug-nonzts-20230831/
[root@Nginx
memcache-8.2]# ls /usr/local/php/lib/php/extensions/no-debug-non-zts20230831/
memcache.so
opcache.so
复制测试文件到nginx发布目录中
[root@node1 memcache-8.2]# ls
autom4te.cache config.m4 CREDITS LICENSE modules
build config.nice docker Makefile README
config9.m4 config.status Dockerfile Makefile.fragments run-tests.php
config.h configure example.php Makefile.objects src
config.h.in configure.ac include memcache.la tests
config.log config.w32 libtool memcache.php
[root@node1 memcache-8.2]# cp example.php memcache.php /www/php/
修改memcache.php测试页的参数:
[root@node1 lib]# vim /www/php/memcache.php
配置php加载memcache模块
[root@node1 memcache-8.2]# vim /usr/local/php/etc/php.ini
;extension=zip
extension=memcache
;zend_extension=opcache
[root@node1 memcache-8.2]# systemctl reload php-fpm
[root@Nginx no-debug-non-zts-20230831]# php -m | grep mem
memcache
部署memcached
[root@node1 memcache-8.2]# yum install memcached -y
[root@node1 memcache-8.2]# systemctl enable --now memcached.service
[root@node1 memcache-8.2]# netstat -antlupe | grep memcache
[root@node1 memcache-8.2]# cat /etc/sysconfig/memcached
PORT="11211"
USER="memcached"
MAXCONN="1024"
CACHESIZE="64"
OPTIONS="-l 127.0.0.1,::1"