一、讲在Nginx之前
1.1 同步与异步:
同步与异步的重点在消息通知的方式上,也就是调用结果的通知方式不同。
- 同步:当一个同步调用发出去后,调用者要一直等待调用的结果通知后,才能进行后续的执行。
- 异步:当一个异步调用发出去后,调用者不必一直等待调用结果的返回,异步调用,要想获得结果。
一般有两种方式:
1、主动轮询异步调用的结果;(主动轮询依旧会占用系统该进程的资源,不断的获取异步调用结果)
2、被调用方通过 callback(回调通知)来通知调用方调用结果。
实例解释:
同步取快递:小明收到快递将送达的短信,在楼下一直等到快递送达。
异步取快递:小明收到快递将送达的短信,快递到楼下后,小明再下楼去取。
异步取快递,小明知道快递到达楼下有两种方式:
1、不停的电话问快递小哥到了没有,即主动轮询;
2、快递小哥到楼下后,打电话通知小明,然后小明下楼取快递,即回调通知。
1.2 阻塞与非阻塞:
阻塞与非阻塞的重点在于 进/线程 等待消息时候的行为,也就是在等待消息的时候,当前进/线程是挂起状态,还是非挂起状态。
阻塞:调用在发出去后,在消息返回之前,当前进/线程会被挂起,直到有消息返回,当前进/线程才会被激活。
非阻塞:调用在发出去后,不会阻塞当前进/线程,而会立即返回。
实例解释:
- 阻塞取快递:小明收到快递即将送达的信息后,什么事都不做,一直专门等快递。
- 非阻塞取快递:小明收到快递即将送达的信息后,等快递的时候,还一边敲代码、一边刷微信。
同步与异步,重点在于消息通知的方式(调用结果的通知方式);阻塞与非阻塞,重点在于等消息时候的行为。所以,就有了下面4种组合方式。
- 同步阻塞:小明收到信息后,啥都不干,等快递;【Apache 默认的处理方式是同步阻塞型】
- 同步非阻塞:小明收到信息后,边刷微博,边等着取快递;
- 异步阻塞:小明收到信息后,啥都不干,一直等着快递员通知他取快递;
- 异步非阻塞:小明收到信息后,边刷着微博,边等快递员通知他取快递。【Nginx 默认的处理方式是异步非阻塞型】
大部分程序的 I/O 模型都是同步阻塞的,单个进程每次只在一个文件描述符上执行 I/O 操作,每次 I/O 系统调用都会阻塞,直到完成数据传输。传统的服务器采用的就是同步阻塞的多进程模型。一个 Server 采用一个进程负责一个 request 的方式,一个进程负责一个 request,直到会话结束。进程数就是并发数,而操作系统支持的进程数是有限的,且进程数越多,调度的开销也越大,因此无法面对高并发。
Nginx 采用了异步非阻塞的方式工作。我们先来先了解一下 I/O 多路复用中的 epoll 模型。
epoll模型:
epoll 多路复用模型:当连接有I/O事件产生的时候,epoll就会去告诉进程哪个连接有I/O事件产生,然后进程就去处理这个事件。 :::warning 一个用户请求到达服务器,epoll 会将请求接收进来,不一定会马上处理。如果有空闲的工作进程那么就会将该用户请求调用给空闲进程进行处理。当服务器的进程繁忙,没有空闲进程时,那么就用户请求就会存储在 epoll 中,知道有空闲进程可以对该请求进行处理。 ::: 例如:小明家楼下有一个收发室,每次有快递到了,门卫就先代收并做了标记;然后通知小明去取送给小明的快递。
为什么Nginx 比其他 web 服务器并发高(Nginx工作原理)
Nginx 配置 use epoll后,以异步非阻塞方式工作,能够轻松处理百万级的并发连接。
处理过程:每进来一个request,会有一个worker进程去处理。但不是全程的处理,处理到可能发生阻塞的地方。比如向后端服务器转发request,并等待请求返回。那么,这个处理的worker 不会这么傻等着,他会在发送完请求后,注册一个事件:“如果后端服务器返回了,告诉我一声,我再接着干”。于是他就休息去了。此时,如果再有新的 request 进来,他就可以很快再按这种方式处理。而一旦后端服务器返回了,就会触发这个事件,worker 才会来接手,这个 request 才会接着往下走。通过这种快速处理,快速释放请求的方式,达到同样的配置可以处理更大并发量的目的。
:::warning
Nginx worker 进程通过快速的处理,快速的释放,快速的接收,来完成一个更高的并发量。
Nginx 是异步非阻塞型,多路复用模型,能够适用于高并发的场景。
:::
二、Nginx 详解
2.1 概述
Nginx (engine x)是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务。Nginx是由 伊戈尔·赛索耶夫 为俄罗斯访问量第二的 Rambler.u 站点开发的,第一个公开版本0.1.0发布于2004年10月4日。
Nginx 是一款轻量级的Web服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,在BSD-like协议下发行。其特点是占有内存少,并发能力强。
2.2 工作模式
nginx有两种工作模式:master-worker模式和单进程模式。在master-worker模式下,有一个master进程和至少一个的worker进程,单进程模式顾名思义只有一个进程。这两种模式有各自的特点和适用场景。
master-worker:
该模式(是Nginx 的默认模式)下,nginx启动成功后,会有一个master进程和至少一个的worker进程。master进程负责处理系统信号,加载配置,管理worker进程(启动,杀死,监控,发送消息/信号等)。worker进程负责处理具体的业务逻辑,也就是说,对外部来说,真正提供服务的是worker进程。生产环境下一般使用这种模式,因为这种模式有以下优点:
- 稳定性高,只要还有 worker进程存活,就能够提供服务,并且一个 worker进程挂掉 master 进程会立即启动一个新的worker进程,保证 worker 进程数量不变,降低服务中断的概率。
- 配合 Linux 的 CPU 亲和性配置,可以充分利用多核cpu的优势,提升性能(配置进程和CPU之间进行绑定,可以配置worker 进程数和CPU核数一样,每个进程绑定不同的CPU上)
- 处理信号 / 配置重新加载 / 升级时可以做到尽可能少或者不中断服务(热重启)
单进程模式:
单进程模式下,nginx启动后只有一个进程,nginx 的所有工作都由这个进程负责。由于只有一个进程,因此可以很方便地利用gdb等工具进行调试。该模式不支持 nginx 的平滑升级功能,任何的信号处理都可能造成服务中断,并且由于是单进程,进程挂掉后,在没有外部监控的情况下,无法重启服务。因此,该模式一般只在开发阶段和调试时使用,生产环境下不会使用。(了解)
2.3 配置文件结构
user www www;
#程序运行用户和组
worker_processes auto;
#启动进程,指定nginx启动的工作进程数量,建议按照 cpu数目来指定,一般等于cpu核心数目
error_log /home/www/logs/nginx_error.log crit;
#全局错误日志
pid /usr/local/nginx/logs/nginx.pid;
#主进程PID保存文件
worker_rlimit_nofile 51200;
#文件描述符数量
events
{
use epoll;
#使用epoll模型,对于2.6以上的内核,建议使用epoll模型以提高性能
worker_connections 51200;
#工作进程的最大连接数量,一个工作进程能够处理最多的用户连接数量
}
http{
#网站优化参数
server{ #具体的某一网站的配置信息,一个server就代表一个网站,类似于Apache中的虚拟主机
listen 80; #监听端口
root html; #网页根目录(/usr/local/nginx/html)
server_name www.atguigu.com; #服务器域名
index index.html; #默认加载页面
access_log logs/access.log; #访问日志保存位置
......;
location (.*)\.php$ { #用于具体请求内容的匹配
#用正则匹配具体的访问对象
}
location {
#跳转等规则
}
}
server {
#虚拟主机
}
}
:::warning Nginx 服务器能够处理的最大用户连接数量:worker_processes * worker_connections :::
2.4 Nginx 相关实验
<注意事项>
- 注意配置文件中的结尾有 “;” 作为结束!(切记!)
- 每次实验修改完配置文件后需要重启Nginx才会生效
pkill -HUP nginx
2.4.1 实验1:Nginx 的状态统计
a、安装 Nginx 时将 —with-http_stub_status_module 模块开启
b、修改 Nginx 配置文件(写入要访问的server标签中) ```bash location /nginx_status { #/nginx_status 请求匹配的内容 stub_status on; #开启nginx状态统计 access_log off; #关闭写入到访问日志的功能 }
检查nginx配置文件语法
nginx -t
重启nginx服务
nginx -s reload
c、客户端访问网址:"http://<IP Address>/nginx_status"
:::warning
"Active connections":表示当前的活动连接数;<br />"server accepts handled requests":表示已经处理的连接信息;<br />三个数字依次表示已处理的连接数,成功的TCP握手次数,已处理的请求数
:::

<a name="yzaFw"></a>
### 2.4.2 实验2:目录保护
a、原理和 Apache 的目录保护原理一样(利用上一个实验接着完成)<br />b、在状态统计的 location中添加:
```bash
auth_basic "welcome to nginx_status!";
auth_basic_user_file /usr/local/nginx/html/htpasswd.nginx;
c、使用http的命令htpasswd进行用户密码文件的创建(生成在上面指定的位置)
yum install -y httpd-tools
htpasswd -c /usr/local/nginx/html/htpasswd.nginx user
2.4.3 实验3:基于IP的身份验证(访问控制)
a、利用上一个实验接着完成
b、在状态统计的 location 中添加:
#allow 10.0.0.0/24;
allow 10.0.0.1;
deny 192.168.88.0/24;
#deny all;
#仅运行10.0.0.1访问服务器
2.4.4 实验4:nginx 的虚拟主机(基于域名)
a、提前准备好两个网站的域名,并且规划好两个网站网页存放目录。
b、在Nginx主配置文件中并列编写两个server标签,并分别写好各自信息
server {
listen 80;
server_name blog.kubesphere.com;
index index.html index.htm index.php;
root /usr/share/nginx/html/blog;
include enable-php.conf;
access_log /var/log/nginx/blog-access.log access;
}
server {
listen 80;
server_name bbs.kubesphere.com;
index index.html index.htm index.php;
root /usr/share/nginx/html/bbs;
include enable-php.conf;
access_log /var/log/nginx/bbs-access.log access;
}
范例:
mkdir -pv /usr/share/nginx/html/{blog,bbs}
echo "<h1>hello,blog.kubesphere.com</h1>" >> /usr/share/nginx/html/blog/index.html
echo "<h1>hello,bbs.kubesphere.com</h1>" >> /usr/share/nginx/html/bbs/index.html
cat >> /etc/hosts <<EOF
# Kubesphere Hosts BEGIN
10.0.0.101 blog.kubesphere.com
10.0.0.101 bbs.kubesphere.com
# Kubesphere Hosts END
EOF
vim /etc/nginx/nginx.conf
server {
listen 80;
server_name blog.kubesphere.com;
index index.html index.htm index.php;
root /usr/share/nginx/html/blog;
#include enable-php.conf;
access_log /var/log/nginx/blog-access.log main;
}
server {
listen 80;
server_name bbs.kubesphere.com;
index index.html index.htm index.php;
root /usr/share/nginx/html/bbs;
#include enable-php.conf;
access_log /var/log/nginx/bbs-access.log main;
}
~ nginx -t
~ nginx -s reload
~ cat > /usr/share/nginx/html/blog/index.php <<EOF
<?php
echo "PHP.blog.kubesphere.com"
?>
EOF
~ cat > /usr/share/nginx/html/bbs/index.php <<EOF
<?php
echo "PHP.bbs.kubesphere.com"
?>
EOF
~ vim /usr/local/nginx/conf/nginx.conf
server {
listen 80;
server_name blog.kubesphere.com;
index index.html index.htm index.php;
root /usr/share/nginx/html/blog;
#include enable-php.conf;
access_log logs/blog-access.log main;
location ~ \.php$ {
root /usr/share/nginx/html/blog;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
include fastcgi.conf;
}
}
server {
listen 80;
server_name bbs.kubesphere.com;
index index.html index.htm index.php;
root /usr/share/nginx/html/bbs;
#include enable-php.conf;
access_log logs/bbs-access.log main;
location ~ \.php$ {
root /usr/share/nginx/html/bbs;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
include fastcgi.conf;
}
}
~ curl http://blog.kubesphere.com/index.php
~ curl http://bbs.kubesphere.com/index.php
~ elinks http://blog.kubesphere.com/index.php
~ elinks http://bbs.kubesphere.com/index.php
2.4.5 实验5:Nginx 的反向代理
代理和反向代理?
- 代理:找别人代替你去完成一件你完不成的事(代购),代理的对象是客户端。
- 反向代理:替厂家卖东西的人就叫反向代理(烟酒代理),代理的对象是服务器端。反向代理即帮助服务器处理请求数据,可以隐藏真实服务器地址,保护真实服务器的安全。
a、在另外一台机器上安装 Apache,启动并填写测试页面。
b、在 Nginx 服务器的配置文件中添加(写在某一个网站的server标签内)。
102~ yum install -y httpd
102~ echo "Hello,world.http://www.10.0.0.102.com" > /var/www/html/index.html
102~ systemctl enable --now httpd
101~ vim /etc/nginx/nginx.conf
server {
listen 80;
server_name blog.kubesphere.com;
index index.html index.htm index.php;
root /usr/share/nginx/html/blog;
#include enable-php.conf;
access_log /var/log/nginx/blog-access.log main;
location / {
proxy_pass http://10.0.0.102:80;
}
}
101~ nginx -t
101~ nginx -s reload
101~ curl http://blog.kubesphere.com/
101~ elinks http://blog.kubesphere.com/
2.4.6 实验6:负载调度(负载均衡)
负载均衡(Load Balance)其意思就是将任务分摊到多个操作单元上进行执行,例如Web服务器、FTP服务器、企业关键应用服务器和其它关键任务服务器等,从而共同完成工作任务。
Nginx 虽然通过PHP-FastGCI 来处理php的请求,但是并没有像 Apache 处理 PHP 的速度快和稳定高效。 一般动态资源交给Apache服务器处理。
a、使用默认的 RR 轮训算法,修改nginx配置文件。
103~ yum install -y httpd
103~ echo "Hello,world.http://www.10.0.0.103.com" > /var/www/html/index.html
103~ systemctl enable --now httpd
#101进行负载均衡
101~ vim /etc/nginx/nginx.conf
upstream blog { #此标签在 Server 标签前面添加声明
server 10.0.0.102:80;
server 10.0.0.103:80;
}
server {
listen 80;
server_name blog.kubesphere.com;
index index.html index.htm index.php;
root /usr/share/nginx/html/blog;
#include enable-php.conf;
access_log /var/log/nginx/blog-access.log main;
#修改自带的 location / 的表情,将原有的内容删除,添加下列两项
location / {
proxy_pass http://blog; #添加反向代理,代理地址填写 upstream 声明的名字
proxy_set_header Host $host; #重写请求头部,保证网站所有页面都可以访问成功
}
}
101~ nginx -t
101~ nginx -s reload
c、开启并设置两台10.0.0.102 & 10.0.0.103 的主机·
安装 Apache 并设置不同的index.html 页面内容(设置不同页面是为了看实验效果)
d、重启nginx,并使用客户端访问测试
curl blog.kubesphere.com
拓展补充:rr算法实现加权轮询(后期集群再讲更多算法类型和功能)
upstream blog {
server 10.0.0.102:80 weight=l;
server 10.0.0.103:80 weight=2;
}
2.4.7 实验7:Nginx 实现 https {证书 + rewrite}
a、安装nginx时,需要将 —with-httpssl module 模块开启
b、在对应要进行加密的server标签中添加以下内容开启SSL
mkdir -pv /usr/local/nginx/conf/ssl/
~ vim /etc/nginx/nginx.conf
server {
......;
ssl on;
ssl_certificate /usr/local/nginx/conf/ssl/kubesphere.crt;
ssl_certificate_key /usr/local/nginx/conf/ssl/kubesphere.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5";
}
server {
listen 80;
server_name bbs.kubesphere.com;
index index.html index.htm index.php;
root /usr/share/nginx/html/bbs;
#include enable-php.conf;
access_log logs/bbs-access.log main;
location ~ \.php$ {
root /usr/share/nginx/html/bbs;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
include fastcgi.conf;
}
ssl on;
ssl_certificate /usr/local/nginx/conf/ssl/kubesphere.crt;
ssl_certificate_key /usr/local/nginx/conf/ssl/kubesphere.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5";
}
c、生成证书和秘钥文件
注意:在实验环境中可以用命令生成测试,在生产环境中必须要在 https 证书厂商注册
mkdir -pv /usr/local/nginx/conf/ssl/
cd /usr/local/nginx/conf/ssl/
openssl genrsa -out kubesphere.key 1024
#建立服务器私钥,生成RSA私钥
~ openssl req -new -key kubesphere.key -out kubesphere.csr
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:BJ
Locality Name (eg, city) [Default City]:BJ
Organization Name (eg, company) [Default Company Ltd]:kubesphere
Organizational Unit Name (eg, section) []:Cloud
Common Name (eg, your name or your server's hostname) []:bbs.kubesphere.com
Email Address []:935523993@qq.com
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []':
#需要依次输入国家,地区,组织,email。最重要的是有一个common name,可以写你的名字或者域名。
#如果为了https申请,这个必须和域名吻合,否则会引发浏览器警报。
#生成的csr文件交给CA签名后形成服务端自己的证书。
openssl x509 -req -days 365 -sha256 -in kubesphere.csr -signkey kubesphere.key -out kubesphere.crt
#生成签字证书
#cp -a kubesphere.crt /usr/local/nginx/conf/ssl/kubesphere.crt
#cp -a kubesphere.key /usr/local/nginx/conf/ssl/kubesphere.key
d、设置 http 自动跳转 https 功能
原有的 server 标签修改监听端口
vim /etc/nginx/nginx.conf
server {
......;
listen 443;
}
新增以下 server 标签(利用虚拟主机 + rewrite 的功能)
vim /etc/nginx/nginx.conf
server {
listen 80;
server_name bbs.kubesphere.com;
root /usr/share/nginx/html/bbs;
index index.html index.htm index.php;
rewrite ^(.*)$ https://bbs.kubesphere.com permanent;
#permanent 永久跳转
}
nginx -t
service nginx restart
#nginx -s reload
客户端浏览器访问http://bbs.kubesphere.com 会自动跳转到 https://bbs.kubesphere.com