高性能Web服务器
一、Web服务介绍
1.Apache服务模型
1.1 Apache prefork模型
预派生模式,有一个主控制进程,然后生成多个子进程,使用select模型,最大并发1024
每个子进程有一个独立的线程响应用户请求
相对比较占用内存,但是比较稳定,可以设置最大和最小进程数
是最古老的一种模式,也是最稳定的模式,适用于访问量不是很大的场景
优点:稳定
缺点:每个用户请求需要对应开启一个进程,占用资源较多,并发性差,不适用于高并发场景
被淘汰原因:最大并发连接是1024,已经无法满足企业需求。
1.2 Apache worker 模型
一种多进程和多线程混合的模型
有一个控制进程,启动多个子进程
每个子进程里面包含固定的线程
使用线程程来处理请求
当线程不够使用的时候会再启动一个新的子进程,然后在进程里面再启动线程处理请求,
由于其使用了线程处理请求,因此可以承受更高的并发
优点:相比prefork 占用的内存较少,可以同时处理更多的请求
缺点:使用keepalive的长连接方式,某个线程会一直被占据,即使没有传输数据,也需要一直等待到超时才会被释放。如果过多的线程,被这样占据,也会导致在高并发场景下的无服务线程可用(该问题在prefork模式下,同样会发生)
1.3 Apache event模型
Apache中最新的模式,2012年发布的apache 2.4.X系列正式支持event 模型,属于事件驱动模型(epoll)
每个进程响应多个请求,在现在版本里的已经是稳定可用的模式
它和worker模式很像,最大的区别在于,它解决了keepalive场景下长期被占用的线程的资源浪费问题(某些线程因为被keepalive,空挂在哪里等待,中间几乎没有请求过来,甚至等到超时)
event MPM中,会有一个专门的线程来管理这些keepalive类型的线程
当有真实请求过来的时候,将请求传递给服务线程,执行完毕后,又允许它释放。这样增强了高并发场景下的请求处理能力
优点:单线程响应多请求,占据更少的内存,高并发下表现更优秀,会有一个专门的线程来管理keep
alive类型的线程,当有真实请求过来的时候,将请求传递给服务线程,执行完毕后,又允许它释放
缺点:没有线程安全控制
当请求发送至子进程时,会优先发送给监听线程,监听线程会根据请求进行分配,缺点也在这里,如果监听线程没了,那么整个工作线程都没了。
2.Nginx-高性能的Web服务端
2.1Nginx简介
Nginx是由1994年毕业于俄罗斯国立莫斯科鲍曼科技大学的同学为俄罗斯rambler.ru公司开发的,开发工作最早从2002年开始,第一次公开发布时间是2004年10月4日,版本号是0.1.0
官网地址 www.nginx.org
Nginx历经十几年的迭代更新(https://nginx.org/en/CHANGES), 目前功能已经非常完善且运行稳定,另外Nginx的版本分为开发版、稳定版和过期版,nginx以功能丰富著称,它即可以作为http服务器,也可以作为反向代理服务器或者邮件服务器能够快速的响应静态网页的请求支持FastCGI/SSL/Virtual Host/URL Rwrite /Gzip / HTTP Basic Auth/http或者TCP的负载均衡(1.9版本以上且开启stream模块)等功能,并且支持第三方的功能扩展。
天猫 淘宝 京东 小米 163 新浪等一线互联网公司都在用Nginx或者进行二次开发
基于Nginx的工作场景:
当请求经过Nginx,静态与动态会访问各自不同的服务器
2.2 Nginx安装配置
[root@nginx-node1 ~]# tar zxf nginx-1.24.0.tar.gz -----解压安装包
[root@nginx-node1 ~]# cd nginx-1.24.0/
[root@nginx-node1 nginx-1.24.0]# ls
auto CHANGES CHANGES.ru conf configure contrib html LICENSE man README src
configure -----环境检测
[root@nginx-node1 nginx-1.24.0]# ./configure --help -----查看参数
[root@nginx-node1 nginx-1.24.0]# ./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --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
检测环境是否能满足如上参数
检测结果如下
报错原因:缺少编译时需要的C语言
[root@nginx-node1 nginx-1.24.0]# dnf install gcc -y -----安装C语言包
报错信息如下
缺少pcre(节约时间,把所需的包一起安装)
[root@nginx-node1 nginx-1.24.0]# dnf install pcre-devel zlib-devel openssl-devel -y
最后进行检测安装
安装成功后,会生成一个Makefile的文件
[root@nginx-node1 nginx-1.24.0]# ls
auto CHANGES CHANGES.ru conf configure contrib html LICENSE **Makefile** man objs README src
objs中产生编译生成的文件
使用make进行安装
[root@nginx-node1 nginx-1.24.0]# make
此时进入objs目录,会发现objs其中会含有nginx的启动程序
[root@nginx-node1 nginx-1.24.0]# ls
auto CHANGES CHANGES.ru conf configure contrib html LICENSE Makefile man objs README src
[root@nginx-node1 nginx-1.24.0]# cd objs/
[root@nginx-node1 objs]# ls
autoconf.err Makefile nginx nginx.8 ngx_auto_config.h ngx_auto_headers.h ngx_modules.c ngx_modules.o src
[root@nginx-node1 nginx-1.24.0]# make install -----将objs里的文件拷贝到指定位置
[root@nginx-node1 nginx-1.24.0]# cd /usr/local/nginx/
[root@nginx-node1 nginx]# ls
conf html logs sbin -----进入nginx目录查询,发现当前目录下已存在文件
[root@nginx-node1 sbin]# /usr/local/nginx/sbin/nginx
nginx: [emerg] getpwnam("nginx") failed -----运行nginx发现报错,原因是不存在nginx用户
[root@nginx-node1 sbin]# useradd -s /sbin/nologin -M nginx -----添加用户
[root@nginx-node1 sbin]# id nginx
uid=1001(nginx) gid=1001(nginx) groups=1001(nginx)
[root@nginx-node1 sbin]# ./nginx -----再执行nginx,就可以启动了
[root@nginx-node1 sbin]# ps aux | grep nginx -----查看nginx工作状态
root 58473 0.0 0.1 9864 2052 ? Ss 15:12 0:00 nginx: master process ./nginx 管理进程
nginx 58474 0.0 0.2 14196 4996 ? S 15:12 0:00 nginx: worker process 工作进程
root 58885 0.0 0.1 221664 2176 pts/0 S+ 15:13 0:00 grep --color=auto nginx
[root@nginx-node1 sbin]# netstat -antlupe | grep nginx -----端口查询 80端口存在
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 0 87458 58473/nginx: master
nginx启动完毕,可以拿主机进行访问
[root@nginx-node1 sbin]# /usr/local/nginx/sbin/nginx -s stop -----关闭nginx
[root@nginx-node1 sbin]# netstat -antlupe | grep nginx -----此时查看nginx,已经没有了
[root@nginx-node1 ~]# cd nginx-1.24.0/
[root@nginx-node1 nginx-1.24.0]# ls
auto CHANGES.ru configure html Makefile objs src CHANGES conf contrib LICENSE man README
[root@nginx-node1 nginx-1.24.0]# rm -fr /usr/local/nginx/ -----删除nginx
[root@nginx-node1 nginx-1.24.0]# make clean -----删除相关配置
rm -rf Makefile objs
[root@nginx-node1 nginx-1.24.0]# ls
auto CHANGES.ru configure html man src CHANGES conf contrib LICENSE README
关闭debug功能
[root@nginx-node1 nginx-1.24.0]# vim auto/cc/gcc -----进入gcc文件下,将debug内容注释
再重新执行检测安装
[root@nginx-node1 nginx-1.24.0]# ./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --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
[root@nginx-node1 nginx-1.24.0]# make && make install
[root@nginx-node1 ~]# cd /usr/local/nginx/sbin/ -----进入安装目录
每次进入安装目录指令过长,为了方便使用,可以编译环境变量配置文件,将nginx软件执行命令路线添加到环境变量中。
[root@nginx-node1 ~]# vim ~/.bash_profile -----进入环境变量文件
添加如下配置
[root@nginx-node1 ~]# source ~/.bash_profile -----加载环境变量
[root@nginx-node1 ~]# du -sh /usr/local/nginx/sbin/nginx -----此时查看nginx的大小,减少了很多(关闭debug的作用)
1.2M /usr/local/nginx/sbin/nginx
[root@nginx-node1 ~]# nginx -----启动nginx然后浏览器访问
[root@nginx-node1 ~]# curl -I 172.25.254.100 -----查看网站信息
此时,版本号是1.24
2.3 Nginx平滑升级与版本回滚
2.3.1 平滑升级
平滑升级过程
平滑升级类似于游戏中的不停机更新,工作内容就是老版本与新版本同时工作,最后再将老版本中的worker回收,则实现版本平滑升级
[root@nginx-node1 ~]# tar zxf nginx-1.26.1.tar.gz
[root@nginx-node1 ~]# tar zxf echo-nginx-module-0.63.tar.gz -----解压文件1.26.1版本nginx与nginx模块
[root@nginx-node1 nginx-1.26.1]# ls
auto CHANGES CHANGES.ru conf configure contrib html LICENSE man README src -----进入nginx1.26.1文件包,发现未进行环境检测,目录里没有objs文件
[root@nginx-node1 nginx-1.26.1]# ./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 ----- 对文件环境进行编译。加入了--add-module=/root/echo-nginx-module-0.63部分 目的是将模块内的内容添加进去 让内部可以执行echo命令
[root@nginx-node1 nginx-1.26.1]# make
#编译后生成objs中bin目录下的nginx文件
#将老版本备份
#强制复制(\cp)且覆盖(-f)
[root@nginx-node1 sbin]# \cp -f /root/nginx-1.26.1/objs/nginx /usr/local/nginx/sbin/nginx
#检测是否有问题
#使用USR2平滑升级,升级后进行检测
#USR2 平滑升级可执行程序,将存储有旧版本主进程PID的文件重命名为nginx.pid.oldbin,并启动新的nginx
#此时两个master的进程都在运行,只是旧的master不在监听,由新的master监听80
#此时Nginx开启一个新的master进程,这个master进程会生成新的worker进程,这就是升级后的Nginx进
程,此时老的进程不会自动退出,但是当接收到新的请求不作处理而是交给新的进程处理。
此时产生了新的worker(所有资源打到52119)
#回收旧版本
#此时使用curl查看
2.3.2 回滚
回滚顾名思义即将老的版本激活,将新的版本回收
使用hub命令将其激活,会发现有两个worker及进行工作,新的ID(55933)
把新的激活的同时,将老的回收
最后再将文件复制与覆盖
回滚完成
二、I/O模型
2.1 I/O 模型相关概念
2.2 网络I/O模型
阻塞型、非阻塞型、复用型、信号驱动型、异步
2.2.1 阻塞型 I/O 模型(blocking IO)
2.2.2 非阻塞型 I/O 模型 (nonblocking IO)
2.2.3 多路复用 I/O 型(I/O multiplexing)
2.2.4 信号驱动式 I/O 模型 (signal-driven IO)
2.2.5 异步 I/O 模型 (asynchronous IO)
2.2.6 五种 IO 对比
三、Nginx架构与进程
请求发送至Nginx,Nginx先找Master进行分配Worker去处理。访问Web server,使用静态资源HTTP返回;PHP使用CGI去访问Application server;使用缓存,去访问Memcached。接下来就是Nginx的代理proxy cache,可以让你访问后端的服务器。用到Haproxy模块。
进程结构:一个主进程和若干个子进程,子进程之间彼此平级且独立,上级都是Master
子进程1要访问2,需要把请求发送到管道内,主进程发现这一请求再进行处理,彼此相互独立,不能沟通。
Nginx常用参数
Nginx启动脚本编写
[root@nginx-node1 ~]# vim /lib/systemd/system/nginx.service
创建文件,并将脚本写入
Unit 环境 Service 主体
保存退出后,重新加载
查看nginx进程 发现此时并没有nginx
用常规命令进行nginx启动
启动完毕
四、核心配置
4.1Nginx 全局配置参数优化调整
user:用户名
worker_processes: 工作内核数,auto表示自动
按如上配置加载重启后会发现,由于有两个核,于是有两个工作进程。
但是由于cpu是两个,各自工作并不稳定,nginx运行时会在核心1与2之间来回选择,为了稳定工作,可以尝试核心绑定。
第一个CPU绑定01 第二个绑定10
如果是四核,那就是 0001 0010 0100 1000 (1*10n次幂)
表示能够并发处理的工作量,但是这个数值是根据虚拟机本身自带的连接数
(如何查看)
open files表示能够处理的工作量,设计大于其数字也没用,因为虚拟机本身处理能力有限
#修改文件数
最后一行添入
此时,再查看,发现修改完毕。
#新建一个PC站点
写一个子配置文件.conf 为了防止内容过长,简便查询,使用include参数
建立这个文件并进行编辑
编辑内容如下
其中还需要创建一个/data/web/html文件
-t 检阅 -s reload 加载 进入C盘windows-system32-divers-etc-hosts文件进行域名解析
4.2root和alias
打开vhost.conf配置文件,加入以上内容(含义是访问test1,会自动访问data目录下的test1)同样,括号内一个root括号外一个root,括号内的会将括号外的覆盖掉
编辑后同样web查询
发现问题
[root@nginx-node1 ~]# tail /usr/local/nginx/logs/error.log
问题出现在有两个/test1/,重新打开vhost.conf进行编辑
为什么会访问/data/web/test1/test1/呢,是因为编辑location后是最后的文件,于是root后跟的地址应该到test1上层的根目录,修改结果如下
最后刷新加载,修改成功
加入alias进行编辑
至此能够看出,root在引用时会读取/test1/为地址的一部分,而alias将/test2引用为地址。
同样 alias中因为是代替,所以“/”要加一起加,否则会404报错
4.3location详细使用
location指定root目录下/data/web/test文件
[root@nginx-node1 ~]# mkdir /data/web/test -p ------新建文件
[root@nginx-node1 ~]# echo test page > /data/web/test/index.html
[root@nginx-node1 ~]# nginx -s reload ------导入并重启
浏览器访问
尝试将编辑中test的“/”删除,查看变化
此时再访问,发现报错
查看日志,寻找问题所在
打开日志发现,自动寻找的是/html目录下的test文件,与编译的文件地址不符。
所以说“/”是必须存在的,精准匹配
“=”的使用
编译时加入“=”
访问时成功,说明“=”可以使用于其中
注:“=”使用时,要求url与客户访问时要精确匹配,例如如下访问,便会有误
#location下优先级大小查询
编辑两个文件web1与web2
访问查询看哪一个生效
查询出是web1生效,此时换一下位置,对比实验
此时又是web2生效,说明无“=”的优先级是大于有“=”的
#location正则匹配
编辑文件并进行访问
所以tee可以访问,lee不行
写入以html结尾的进行查询
ps:由于路径写的是/lee/index.html
所以在配置的时候,不能写入root /data/web1/lee; 因为这样会导入两个lee导致位置错误。
~ 区分大小写
~* 不区分大小写
优先级判定
~* 与 ~ 优先级相同,谁前选谁
其次是不加符号
接下来是^(以开头)
最次的是“=”
4.4 Nginx账户认证
4.4.1 创建认证文件
[root@nginx-node1 ~]# htpasswd -cm /usr/local/nginx/.htpasswd admin -----创建用户
[root@nginx-node1 ~]# cat /usr/local/nginx/.htpasswd -----查看账户密码信息
[root@nginx-node1 ~]# htpasswd -m /usr/local/nginx/.htpasswd lee -----添加另一用户lee(用“-m”,因为-cm会将信息覆盖)
结果如下所示
4.4.2 编辑配置
访问成功
4.4.3 添加认证
当访问lee时,会通过file文件下的认证文件进行认证,通过才可以访问。
此时再访问lee,就发现需要用户认证才可以进行访问
认证通过后,可以访问内容。
4.5 Nginx自定义错误页面
对编辑内容进行解释:
当发现404时,显示40x.html文件
最后对40x.html文件进行解释
建立对应文件与内容
最后浏览器访问
自定义成功
4.5 Nginx自定义错误日志
建立对应文件,重新加载nginx
正常加载和报错都有,此时查阅日志,都可以查到
4.6 Nginx检测文件是否存在
顺序:访问uri是否存在、uri.html是否存在、uri/index.html是否存在
若都不存在,访问/error/default.html
此刻访问,uri存在,所以不会报错,接下来将删除试验
明显,将uri、index.html、error等删除后,会进行web报错(原因是一系列文件都没有)。
根据配置地址建立报错文件
4.6 Nginx中的长链接管理
keepalive_timeout 即超过多少秒后自动断开
keepalive_requests 即最大的请求数
配置中,最大请求数为2
安装测试工具telnet
80端口访问
当请求数为2时自动断开
65表示长连接保持65秒 60表示客户所能看到的只有60秒
编辑后,显示超时时间是60s
4.7 下载服务器的设定与配置
建立文件
然后做一个大小为100M的文件,保存在/data/web/download/leefile文件中,数据从/dev/zero拷贝而来
进入usr/local/nginx/conf.d/vhost.conf目录中,添加上述download内容
此时并不能完全访问,需要加入 autoindex on参数
添入后,浏览器访问查询
可下载界面完成
此时下载的时间与本地不一致(伦敦时间)
添加参数,让时间与本地配置保持一致
将数据大小转为粗略大小
限速
限速修改成功
五、Nginx高级配置
5.1 Nginx中的状态页面
重新编辑一个status.conf文件
windows下进行域名解析
浏览器访问
可以通过用户认证或者指定用户进行访问
然后在本地做域名解析
curl访问
Forbidden拒绝
5.2Nginx中的数据压缩功能
进入/usr/local/nginx/conf/nginx.conf文件中,其中包含压缩开关,现在将其打开。
所需配置参数如下
下图是所压缩的类型(在vim/usr/local/nginx/conf/mine.types中查看文件类型)
配置完成后,检测刷新
编辑两个大小不一的文件
压缩小文件,由于文件太小,未压缩
压缩大文件,压缩成功。
5.2 Nginx中的变量详解
编辑vars.conf文件
在etc/hosts目录下做解析
命令时要加入目录下var编辑,这样才会执行配置中location/var配置
1.$remote_addr;
输出远程变量的IP
2. $args;
输出URL的所有参数
3. $is_args;
对args值进行判断,若为空,显示为空,若不为空,显示为?
4. $document_root;
会显示默认发布目录(root /data/web/html)
5. $document_uri
显示不包含内容的uri目录(即?与后面都消失)
6. $host;
访问的主机名字
7. $remote_port
nginx连接的端口号
8. $remote_user
登录的用户信息(使用用户信息登录的时候才会有)
9. $request_method;
method会输出请求资源的方式,GET/PUT/DELETE等。
10. $request_filename
显示最终访问的文件(即var文件,在默认发布目录下的/var目录中)
11. $request_uri;
查询uri
12. $scheme
显示协议
六、 Nginx Rewrite 相关功能
6.1 模块指令
6.1.1 if指令
if指令解释:判断这个文件是否(不存在)
不存在回复 echo内容
这是文件不存在回复的,如果文件存在呢?
建立文件进行访问试验
注意:建立文件的地址应该是server下的地址,应与其保持一致
6.1.2 set指令
6.1.3 break指令
不加break配置
两个值都可以显示
现在加入break进行配置
有一个空白行原因是echo执行了,但是动作并没有执行
6.1.4 return指令
编辑如下配置
测试查看
建立这个文件重新查询
6.2 rewrite指令
6.1.1 永久重定向
建立目标文件 配置重定向
重启后,使用curl访问报错。原因是curl不支持重定向
但是curl -I 可以看到,实际上重定向是生效的,只是不显示。
通过浏览器进行访问尝试(提前做域名解析)
输入:
自动跳转:
F12查看
6.1.2 临时重定向
重启后变成302了(临时) 301(永久)
6.1.3 break与last
rewrite ^/break/(.) /test1/$1; ------当访问break’时转到test1
rewrite ^/test1/(.) /test2/$1; ------当访问test1时转到test2
访问结果下图所示
break与last的区别
break读到第一行就终止了 不会继续运行第二行内容
搜索时要注意:应具体到index.html(文件名字)
反之如果是last,会终止然后在其中搜索到location /test1
6.3 rewrite的企业示例及防盗链
6.3.1 全站加密
认证
编辑下述配置
重定向配置
配置含义:当并没有使用http时,则重定向到https下
优化:
解释: / 后如果有uri,也会自动去转换成https,中间会将uri数值带上
(.*)表示可能有元素,可能没有元素。写$1的意义在于,访问网址后会重定向到你uri添加的网站上。
含义:当访问的网站不存在时,自动访问到主站上去。
这次再访问/a/b发现会自动跳转