1 介绍
1.1 为什么要学习Nginx?
服务器搭建集群后如下图:
带来的问题:
- 问题1:客户端到底要将请求发送给哪台服务器?
- 问题2:如果所有客户端的请求都发送给了服务器1
- 问题3:客户端发送的请求可能是申请动态资源的,也有申请静态资源的
在搭建集群后,使用Nginx做反向代理
Nginx的特点:
- 1.稳定性极强,7*24小时不间断运行(就是一直运行)
- 2.Nginx提供了非常丰富的配置实例
- 3.占用内存小,并发能力强(随便配置一下就是5w+,而tomcat的默认线程池是150)
- 4.支持热部署,不用停止服务器,实现更新配置文件,更换日志文件、更新服务器程序版本
- 5.功能丰富,优秀的反向代理功能和灵活的负载均衡策略
- 6.模块化设计:良好的扩展性,可以通过模块方式进行功能扩展
- 7.高可靠性:主控进程和 worker 是同步实现的,一个 worker 出现问题,会立刻启动另一个 worker。
1.2 Nginx下载、安装
1.2.1 windows
下载地址
解压到本地
启动:
cmd命令: start nginx
或者 nginx -c xxx.conf
1.2.2 命令
start nginx 启动
nginx -s stop 快速关闭Nginx,可能不保存相关信息,并迅速终止web服务。
nginx -s quit 平稳关闭Nginx,保存相关信息,有安排的结束web服务。
nginx -s reload 因改变了Nginx相关配置,需要重新加载配置而重载。
nginx -s reopen 重新打开日志文件。
nginx -c filename 为 Nginx 指定一个配置文件,来代替缺省的。
nginx -t 不运行,仅仅测试配置文件。nginx 将检查配置文件的语法的正确性,并尝试打开配置文件中所引用到的文件。
nginx -v 显示 nginx 的版本。
nginx -V 显示 nginx 的版本,编译器版本和配置参数。
1.3 Nginx原理分析
1.3.1 nginx的线程模型?
Nginx默认采用多进程工作方式,Nginx启动后,会运行一个master进程和多个worker进程。其中master充当整个进程组与用户的交互接口,同时对进程进行监护,管理worker进程来实现重启服务、平滑升级、更换日志文件、配置文件实时生效等功能。worker用来处理基本的网络事件,worker之间是平等的,他们共同竞争来处理来自客户端的请求。
nginx的进程模型如图所示:
1.3.2 worker的工作模式?
worker对于连接是采用争抢的模式,谁先抢到就先交给谁处理,如果想要重新更新配置,由于已经抢到任务的worker不会参与争抢,那些空闲的worker就会去争抢连接,拿到连接后会自动更新配置信息,当那些有任务的worker完成任务后,会自动更新配置,这样就实现了无缝热部署。由于每个worker是独立的进程,如果有其中的一个worker出现问题,并不会影响其它worker继续进行争抢,在实现请求的过程,不会造成服务中断,建议worker数和服务器的cpu数相等是最为适宜的。
1.3.3 如何计算worker连接数?
如果只访问nginx的静态资源,一个发送请求会占用了 woker 的 2 个连接数
而如果是作为反向代理服务器,一个发送请求会占用了 woker 的 4 个连接数
1.3.4 如何计算最大的并发数?
如果只访问nginx的静态资源,最大并发数量应该是: worker_connections * worker_processes / 2
而如果是作为反向代理服务器,最大并发数量应该是:worker_connections * worker_processes / 4
2 反向代理
概述:
Nginx 不仅可以做反向代理,还能用作正向代理来进行上网等功能。
正向代理:如果把局域网外的 Internet 想象成一个巨大的资源库,则局域网中的客户端要访问 Internet,则需要通过代理服务器来访问,这种代理服务就称为正向代理。
对于反向代理,客户端对代理是无感知的,因为客户端不需要任何配置就可以访问,我们只需要将请求发送到反向代理服务器,由反向代理服务器去选择目标服务器获取数据后,在返回给客户端,此时反向代理服务器和目标服务器对外就是一个服务器,暴露的是代理服务器地址,隐藏了真实服务器 IP 地址。
正向代理:
1.正向代理服务是由客户端设立的
2.客户端了解代理服务器和目标服务器都是谁
3.帮助咱们实现突破访问权限,提高访问的速度,对目标服务器隐藏客户端的ip地址
反向代理:
- 1.反向代理服务器是配置在服务端的
- 2.客户端不知道访问的到底是哪一台服务器
- 3.达到负载均衡,并且可以隐藏服务器真正的ip地址
2.1 反向代理配置 proxy_pass
nginx.conf
#user nginx;
worker_processes 1;
error_log logs/error.log;
error_log logs/notice.log notice;
error_log logs/info.log info;
pid logs/nginx.pid;
# 以上同城为全局块
# user 运行用户
# worker_processes 启动进程,通常设置成和cpu的数量相等,数值越大,Nginx的并发能力就越强
# error_log Nginx错误日志存放的位置
# pid 记录当前启动的nginx的进程ID
events {
worker_connections 1024;
}
# events块
# worker_connections 单个后台worker process进程的最大并发链接数,数值越大,Nginx的并发能力就越强
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;
rewrite_log on;
#sendfile 指令指定 nginx 是否调用 sendfile 函数(zero copy 方式)来输出文件,对于普通应用,
#必须设为 on,如果用来进行下载等应用磁盘IO重负载应用,可设置为 off,以平衡磁盘与网络I/O处理速度,降低系统的uptime.
sendfile on;
#tcp_nopush on;
#连接超时时间
keepalive_timeout 65;
#gzip压缩开关
#gzip on;
include server.conf;
}
# http块
# include代表引入一个外部文件
# include mime.types; 设定mime类型(邮件支持类型),类型由mime.types文件定义
# include server.conf; 引入了server.conf配置文件
server.conf
server {
listen 18888;
server_name localhost;
location / {
proxy_pass http://localhost:18092/;
}
}
# server块
# listen代表Nginx监听的端口号
# server_name代表Nginx接受请求的IP
start nginx
启动之后,访问效果如下:
2.1.1 例子
demo1. 代理 https 的服务
demo2. 在浏览器地址栏输入地址:http://www.123.com,跳转到 Tomcat 主页面中。
步骤一:修改Windows中的hosts域名映射
复制“ C:\Windows\System32\drivers\etc\hosts ”到桌面,右键记事本打开,在里边加上以下代码保存,然后再复制回去,不要直接修改,会不让保存!
#虚拟机域名 映射的网址
192.168.206.128 www.123.com
步骤二:修改Nginx中的配置文件并启动
server {
listen 80;
server_name 192.168.206.128;
location / {
proxy_pass http:127.0.0.1:8080;
root html;
index index.html index.htm;
}
demo3. 使用 nginx 反向代理,根据访问的路径跳转到不同端口的服务中。
server {
listen 80;
server_name 192.168.206.128;
location ~ /edu/ {
proxy_pass http://127.0.0.1:8080;
}
location ~ /vod/ {
proxy_pass http://127.0.0.1:8081;
}
2.2 location路径映射
优先级关系:
(location = ) > (location /xxx/yyy/zzz) > (location ^~) > (location ~,~*) > (location /起始路径) > (location /)
通配符:
- = :用于不含正则表达式的 uri 前,要求请求字符串与 uri 严格匹配,如果匹配成功,就停止继续向下搜索并立即处理该请求。
- ~ :用于表示 uri 包含正则表达式,并且区分大小写。
- ~* :用于表示 uri 包含正则表达式,并且不区分大小写。
- ^~ :用于不含正则表达式的 uri 前,要求 Nginx 服务器找到标识 uri 和请求字符串匹配度最高的 location 后,立即使用此 location 处理请求,而不再使用 location 块中的正则 uri 和请求字符串做匹配。
注意: 如果 uri 包含正则表达式,则必须要有 ~ 或者 ~* 标识。
# 1. = 匹配
location / {
#精准匹配,主机名后面不能带任何字符串
#例如www.baidu.com不能是www.baidu.com/id=xxx
}
#2. 通用匹配
location /xxx {
#匹配所有以/xxx开头的路径
#例如127.0.0.1:8080/xxx xxx可以为空,为空则和=匹配一样
}
#3. 正则匹配
location ~ /xxx {
#匹配所有以/xxx开头的路径
}
#4. 匹配开头路径
location ^~ /xxx/xx {
#匹配所有以/xxx/xx开头的路径
}
#5. 匹配结尾路径
location ~* \.(gif/jpg/png)$ {
#匹配以.gif、.jpg或者.png结尾的路径
}
demo
server {
listen 80;
server_name localhost;
location /index {
proxy_pass http://ncthz.top:8081/; #tomcat首页
}
location ^~ /CR/ {
proxy_pass http://ncthz.top:8080/CR/; #毕设前台首页
}
location / {
proxy_pass http://ncthz.top:8080/CRAdmin/; #毕设后台首页
}
}
# 访问ncthz.top/index可以进入tomcat首页
# 访问ncthz.top/CR/XXX可以进入毕设前台首页
# 访问ncthz.top或者ncthz.top:80可以进入毕设后台首页
2.3 Https 反向代理
2.3.1 生成crt证书
使用 jdk 自带的 keytool 工具生成crt证书。
命令:
-alias指定别名为eviltop;
-keyalg指定RSA算法;
-keypass指定私钥密码;
-keystore指定密钥文件名称为key.keystore;
-validity指定有效期为365天。
# 创建密钥库
keytool -genkey -alias eviltop -keyalg RSA -keystore D:/key.keystore -dname "CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN" -keypass 123456 -storepass 123456 -validity 365
# 查看密钥库
# 显示详细
keytool -list -v -keystore D:/key.keystore -storepass 123456
# 编码打印
keytool -list -rfc -keystore D:/key.keystore -storepass 123456
# 根据密钥库导出crt证书
keytool -export -alias eviltop -file D:/key.crt -keystore D:/key.keystore -storepass 123456
# 根据证书生成密钥库
keytool -import -alias eviltop -file D:/key.crt -keystore D:/newkey.keystore -storepass 123456
# 修改密钥库密码
keytool -storepasswd -keystore D:/key.keystore -storepass 123456 -new 567890
2.3.2 Https 反向代理设置
一些对安全性要求比较高的站点,可能会使用 HTTPS(一种使用 ssl 通信标准的安全 HTTP 协议)。
使用 nginx 配置 https 需要知道几点:
- HTTPS 的固定端口号是 443,不同于 HTTP 的 80 端口
- SSL 标准需要引入安全证书,所以在 nginx.conf 中你需要指定证书和它对应的 key
其他和 http 反向代理基本一样,只是在 Server 部分配置有些不同。
#HTTP服务器
server {
#监听443端口。443为知名端口号,主要用于HTTPS协议
listen 443 ssl;
#定义使用localhost访问
server_name localhost;
#ssl证书文件位置(常见证书文件格式为:crt/pem)
ssl_certificate D:/key.crt;
#ssl证书key位置
ssl_certificate_key D:/key.key;
#ssl配置参数(选择性配置)
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
#数字签名,此处使用MD5
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://localhost:18092/;
}
}
server {
listen 80;
server_name localhost;
#将http请求自动跳转到https上
return 301 https://$server_name$request_uri;
}
出现问题:nginx: [emerg] cannot load certificate "D:/key.crt": PEM_read_bio_X509_AUX() failed (SSL: error:0909006C:PEM routines:get_name:no start line:Expecting: TRUSTED CERTIFICATE)
可能是生成的证书有问题,使用openssl生成证书,替换原来的可解决问题。
openssl 下载地址
使用openssl生成证书:
- 生成密钥:
openssl genrsa -des3 -out D:\key.key 2048
这个是产生一个des3算法,2048位强度的rsa私钥,D盘下会产生一个名为key.key的秘钥文件。
输入一个最少4位数的密码 - 生成csr文件:
openssl req -config openssl.conf -new -key D:\key.key -out D:\key.csr
- 删除秘钥的密码(要不然启动nginx每次都要求输入密码),输入命令:
openssl rsa -in D:\key.key -out D:\key_no_passwd.key
要求输入密码(前面输入的密码) - 创建生成证书:
openssl x509 -req -days 365 -in D:\key.csr -signkey D:\key_no_passwd.key -out D:\key.crt
D盘下有4个文件,key.key 、key_no_passwd.key、key.csr、key.crt。其中配置nginx的话需要key_no_passwd.key和key.crt这两个文件,至此,证书生成结束。
3 负载均衡
概述:
客户端发送多个请求到服务器,服务器处理请求,有一些可能要与数据库进行交互,服务器处理完毕后,再将结果返回给客户端。
这种架构模式对于早期的系统相对单一,并发请求相对较少的情况下是比较适合的,成本也低。但是随着信息数量的不断增长,访问量和数据量的飞速增长,以及系统业务的复杂度增加,这种架构会造成服务器相应客户端的请求日益缓慢,并发量特别大的时候,还容易造成服务器直接崩溃。很明显这是由于服务器性能的瓶颈
造成的问题,那么如何解决这种情况呢?
我们首先想到的可能是升级服务器的配置,比如提高 CPU 执行频率,加大内存等提高机器的物理性能来解决此问题,但是我们知道摩尔定律的日益失效,硬件的性能提升已经不能满足日益提升的需求了。最明显的一个例子,天猫双十一当天,某个热销商品的瞬时访问量是极其庞大的,那么类似上面的系统架构,将机器都增加到现有的顶级物理配置,都是不能够满足需求的。那么怎么办呢?
上面的分析我们去掉了增加服务器物理配置来解决问题的办法,也就是说纵向解决问题的办法行不通了,那么横向增加服务器的数量呢?这时候集群的概念产生了,单个服务器解决不了,我们增加服务器的数量,然后将请求分发到各个服务器上,将原先请求集中到单个服务器上的情况改为将请求分发到多个服务器上,将负载分发到不同的服务器,也就是我们所说的负载均衡
。
实现思路:
Nginx为我们默认提供了三种负载均衡的策略:
- 轮询:
将客户端发起的请求,平均分配给每一台服务器- 加权:
会将客户端的请求,根据服务器的权重值不同,分配不同的数量- ip_hash:
基于发起请求的客户端的ip地址不同,他始终会将请求发送到指定的服务器上
就是说如果这个客户端的请求的ip地址不变,那么处理请求的服务器将一直是同一个
3.1 轮询
想实现Nginx轮询负载均衡
机制只需要修改server.conf
文件如下:
#设定实际的服务器列表
upstream my_server{
# 默认所有服务器权重为 1
server 127.0.0.1:18092;
server 127.0.0.1:18094;
}
server {
listen 80;
server_name localhost;
#反向代理的路径(和upstream绑定),location 后面设置映射的路径
location / {
proxy_pass http://my_server; #或者 http://my_server/
proxy_set_header Host $host;
}
}
upstream 名字{
server ip:端口;
server 域名:端口;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://upstream的名字/;
proxy_set_header Host $host;
}
}
注意:使用 upstream 出现400
必须添加proxy_set_header Host $host;
,这是因为后端服务器设置有类似防盗链或者根据http请求头中的host字段来进行路由或判断功能。
3.2 加权
实现权重的方式:在配置文件中 upstream 块中加上weight
upstream my_server{
server 127.0.0.1:18094 weight=2;
server 127.0.0.1:18092; # default weight=1
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://my_server/;
proxy_set_header Host $host;
}
}
3.3 ip_hash
实现ip_hash方式:在配置文件upstream块中加上ip_hash
;
upstream my_server{
ip_hash;
server 127.0.0.1:18094;
server 127.0.0.1:18092;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://my_server/;
proxy_set_header Host $host;
}
}
3.4 其他方式
- 最少连接
upstream bck_testing_01 {
least_conn;
# with default weight for all (weight=1)
server 192.168.250.220:8080
server 192.168.250.221:8080
server 192.168.250.222:8080
}
- 加权最少连接
upstream bck_testing_01 {
least_conn;
server 192.168.250.220:8080 weight=3
server 192.168.250.221:8080 # default weight=1
server 192.168.250.222:8080 # default weight=1
}
- 普通 Hash
upstream bck_testing_01 {
hash $request_uri;
# with default weight for all (weight=1)
server 192.168.250.220:8080
server 192.168.250.221:8080
server 192.168.250.222:8080
}
- 第三方
按后端服务器的响应时间来分配请求,响应时间短的优先分配。
upstream server_pool{
server 192.168.5.21:80;
server 192.168.5.22:80;
fair;
}
4 Nginx动静分离
概述:
Nginx 动静分离简单来说就是把动态跟静态请求分开,不能理解成只是单纯的把动态页面和静态页面物理分离。严格意义上说应该是动态请求跟静态请求分开,可以理解成使用 Nginx 处理静态页面,Tomcat 处理动态页面。动静分离从目前实现角度来讲大致分为两种,一种是纯粹把静态文件独立成单独的域名,放在独立的服务器上,也是目前主流推崇的方案;另外一种方法就是动态跟静态文件混合在一起发布,通过 Nginx 来分开。
效果:
如果不设置动静分离,默认会通过Nginx的反向代理去找Tomcat对应的资源,现在我们在根目录下创建一个/data/www/文件夹,里边放上静态资源,比如一个html页面,在8080的那台Tomcat的webapps下也创建一个www目录,同样是放一个静态资源,当输入这个静态资源的请求时,访问到的是/data/www中的数据
实现:
location /www/ {
root /data/;
index index.html index.htm;
}
Nginx的并发能力公式:
worker_processes * worker_connections / 4|2 = Nginx最终的并发能力
动态资源需要/4,静态资源需要/2
Nginx通过动静分离来提升Nginx的并发能力,更快的给用户响应
4.1 动态资源代理
#配置如下
location / {
proxy_pass 路径;
}
4.2 静态资源代理
server.conf
server {
listen 80;
server_name localhost;
#反向代理的路径(和upstream绑定),location 后面设置映射的路径
location / {
root conf/file; #静态资源路径
index index.html; #默认访问路径下的什么资源,若文件夹下有index.html,则会打开该页面
autoindex on; #代表展示静态资源的全部内容,以列表的形式展开
}
}
动静分离demo
server {
listen 80;
server_name localhost;
#静态资源html页面
location /html/ {
root decs/;
index index,html index.htm;
}
#静态资源图片
location /images/ {
root decs/;
#可以列表查看
autoindex on;
}
#动态资源tomcat
location ~ /edu/ {
proxy_pass http://localhost:8080;
}
}
部署vue项目:
将vue项目打包生成的dist复制到html文件夹下,并修改server.conf的location
location / {
root html/dist; #项目路径
index index.html index.htm;
#防止重复刷新空白问题
#try_files $uri $uri/ /index.html;
}
部署静态html–自定义访问路径:
静态文件位置:D:/arcode/ar
ar文件夹下有left.html、right.html和static静态文件夹
server {
listen: 18090
server_name: locahost
location /ar{
root D:/arcode
index left.html
}
}
//访问地址:http://41.228.1.109:18090 -> nginx页面
//访问地址:http://41.228.1.109:18090/ar -> left页面
//访问地址:http://41.228.1.109:18090/ar/left.html -> left页面
//访问地址:http://41.228.1.109:18090/ar/right.html -> right页面
5 Nginx集群
单点故障,避免nginx的宕机,导致整个程序的崩溃
准备多台Nginx
准备keepalived,监听nginx的健康情况
准备haproxy,提供一个虚拟的路径,统一的去接收用户的请求
6 遇到的问题
6.1 关闭Nginx后仍然可以访问页面
(1)查看nginx pid:tasklist /fi "IMAGENAME eq nginx.exe"
(2)杀死进程:taskkill /f /pid 21552
切记使用start nginx
启动,不可直接使用nginx
,否则会出现该问题!!!
6.2 session共享问题
原因:由于tomcat1,tomcat2部署了同一个工程,如果有数据直接存在于session,而nginx会负载均衡到随机的服务器上 。如果在刷新页面时跳转到另外一个服务器,之前的服务器上的session则会消失,导致数据丢失。
- 解决方式1:可以将session的id放入redis中
- 解决方式2:保证一个ip地址永远的访问一台web服务器,就不存在session共享问题了,尤其在linux。在nginx的配置文件中的 upstream节点中添加 ip_hash;
upstream xxx_server{
server 127.0.0.1:8090;
server 127.0.0.1:8100;
ip_hash;
}
6.3 跨域问题
反向代理。
把前端的地址和后端的地址,使用nginx转换到相同的地址下,如把上面的node服务3000端口 和 网页打开的服务5500端口都转换到nginx的8000端口下
server {
listen 8000;
server_name localhost;
# / 表示匹配路径为/的url
location / {
proxy_pass http://localhost:5500;
}
# /user 表示访问以/user 开头 的地址 如/username,/user/find等
location /user {
proxy_pass http://localhost:3000;
}
}
6.4 上传文件大小限制
显示错误信息:413 Request Entity Too Large。
意思是请求的内容过大,浏览器不能正确显示。常见的情况是发送 POST 请求来上传大文件。
解决方法
可以在 http 模块中设置:client_max_body_size 20m;
可以在 server 模块中设置:client_max_body_size 20m;
可以在 location 模块中设置:client_max_body_size 20m;
三者区别是:
如果文大小限制设置在 http 模块中,则对所有 Nginx 收到的请求。
如果文大小限制设置在 server 模块中,则只对该 server 收到的请求生效。
如果文大小限制设置在 location 模块中,则只对匹配了 location 路由规则的请求生效。