*
1.安装
Nginx 在 Windows 下的安装
Nginx( "engine x" )是一个高性能的 HTTP 和反向代理服务器,也是一个 IMAP / POP3 / SMTP 代理服务器。 Nginx 是由 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的,第一个公开版本 0.1.0 发布于 2004 年 10 月 4 日。其将源代码以类 BSD 许可证的形式发布,因它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名。2011 年 6 月 1 日,nginx 1.0.4 发布。
警告
解压路径不要有空格和中文!例如:C:\ProgramFiles 或 D:\ProgramFiles 就是很好的就选择。
Nginx 只有解压版本,没有安装版。
1. 下载、解压
从官网 nginx-1.20.2
。
解压压缩包到 D:\ProgramFiles ,因此,后文的 NGINX_HOME 指的就是 D:\ProgramFiles\nginx-1.20.2
。
解压后,可到看到如下内容:
NGINX_HOME
├── conf 配置文件目录
├── contrib
├── docs
├── html 类似 tomcat 的 webapps
├── logs 日志目录
├── temp
└── nginx.exe 启动程序
其中 conf/nginx.conf 文件是 Nginx 的配置文件,Nginx 启动后占用哪个端口等配置信息就定义在这个文件中。
2. Nginx 的启停
启动 Nginx
在 NGINX_HOME 目录下打开 cmd 命令窗口,输入命令 start nginx
启动 Nginx 或者直接双击 nginx.exe
。
这里会有一个黑窗口快速地一闪而过,电脑性能强大的同学可能反而看不见,因外太快了...
判断 / 验证 Nginx 是否正在运行
在 cmd 命令窗口输入命令 tasklist /fi "imagename eq nginx.exe"
。你会看到类似如下页面:
映像名称 PID 会话名 会话# 内存使用
=========== ======= =========== ======= ============
nginx.exe 17220 Console 8 7,148 K
nginx.exe 17660 Console 8 7,508 K
如果显示有 2 条 Nginx 信息则表明有一个 Nginx 正在后台运行中。
如果出现了 4 条、6 条甚至更多的 Nginx 信息,则表明你无意之中启动了多次 Nginx ,此时有多个 Nginx 正在后台运行。这会为你正常使用 Nginx 带来麻烦和错误。
Nginx 的使用
直接在浏览器地址栏输入网址
。你会看到欢迎页面。即,NGINX_HOME 下的 html 目录中的 index.html 内容。改变 html 目录下的内容就会影响你在
网址所看到的内容。Nginx 的停止
下述方案有一个小缺陷
以管理员权限打开 cmd 命令行终端,进入到 NGINX_HOME 。在 NGINX_HOME 下打开 cmd 命令行,输入如下 2 条命令之一:
# 快速停止 nginx
nginx -s stop
# 完整有序地停止 nginx
nginx -s quit
这种停止方式有一个小缺陷在于:它只能停止一个 Nginx 程序。
如果你像上面所说地,有意或无意中开启了多个 Nginx 进程,现在你想关闭所有的 Nginx 进程,那么你就需要不停地执行上述停止命令,直到看到错误信息为止。
如果想依靠用以地解决这个问题,你可使用如下命令一口气关闭掉所有的 nginx ,无论它有多少个进程:
taskkill /f /t /im nginx.exe
3. 借助 WinSW 启停 Nginx
提示
有一小部分同学操作完全正确,但是仍会注册失败。原因不明,暂时无解决方案。怀疑和 Windows 系统版本有关。
第 1 步:下载 Windows Service Wrapper 工具
将下载好的 WinSW 放到 NGINX_HOME 目录下,并重命名。名字任意,例如:systemctl.exe 。
第 2 步:为 WinSW 创建配置文件
在 NGINX_HOME 下为 WinSW 创建配置文件( 配置文件与 WinSW 程序平级 )。配置文件为 .xml 文件,且名字必须与 WinSW 程序相同。例如:systemctl.xml ,与上面的 systemctl.exe 相呼应。
systemctl.xml 配置文件内容如下:
<service>
<id>nginx</id>
<name>Nginx</name>
<description>Nginx</description>
<executable>%BASE%\nginx.exe</executable>
<stopexecutable>%BASE%\nginx.exe</stopexecutable>
<stopargument>-s</stopargument>
<stopargument>stop</stopargument>
<logpath>%BASE%\logs</logpath>
<logmode>roll</logmode>
</service>
在上述的配置文件中,我们「告知」了 WinSW 以什么命令启停 Nginx 。未来,我们不再亲自启停 Nginx ,而是通过 WinSW 间接启停 Nginx 。
第 3 步:安装 Nginx 服务
在 NGINX_HOME 目录下打开 cmd 命令行执行如下命令:
# 安装服务。开机启动,当前未启动,重启后生效。
systemctl install
auto 开机启动。当前未启动,重启后生效
demand 手动启动
disabled 禁用
通过 Windows 的 sc 命令调整成手动启动
sc config nginx start= demand
安装成功后,你可以在 Windows 系统的服务中看到 Nginx 。默认是开机启动,下次你的系统再开机,nginx 就自动启动了。
install 的反向操作是 uninstall ,uninstall 之后在 Windows 服务中就看不到它了。
# uninstall 的前提是服务已停止
systemctl uninstall
注意,install 和 uninstall 的操作只用执行一次,在日常使用中并非反复执行( 也无必要 )。
第 4 步:启停 nginx 服务
提示
你在 Windows 的 服务
点点点,也能实现下述命令的功能。就看你偏好哪种方式了。
# 查看状态
systemctl status
# 启动服务
systemctl start
# 停止服务
systemctl stop
提示当启动不起来时,需要自行查看log目录下的error.log日志
2.使用nginx
1. 反向代理服务器
1.1 概念
由于请求的方向是从客户端发往服务端,因此 客户端 -> 服务端
这个方向是『正向』。
所谓『反向代理服务器』指的就是:Nginx『站』在服务端的角度,分担了服务端的负担,增强了服务端的能力。
在这种情况下,在客户端看来,
Nginx
+服务端
整体扮演了一个更大意义上的服务端的概念。
1.2 基于 Nginx 的动静分离方案
对 Nginx 的最简单的使用是将它用作静态资源服务器。
在这种方案种,将 .html
、.css
、.js
、.png
等静态资源放置在 Nginx 服务器上。
将对静态资源的访问流量就分流到了 Nginx 服务器上,从而减轻 Servlet 容器的访问压力。
1.3 基于 Nginx 的前后端分离
随着前端单页应用技术的发展,『前端』从简单的『前端页面』演进成了『前端项目』。
这种情况下,在动静分离方案的基础上进一步延伸出了『更激进』的方案:前后端分离。
1.4 实现原理
要实现前后端分离(涵盖动静分离),这里需要 Nginx 能提供一种能力:请求转发。
在整个过程中,所有的请求首先都是『交到了 Nginx 手里』,有一部分请求是 Nginx 自己能响应的,它就响应了;而另一部分请求则是被 Nginx 转给了 SpringBoot,而等到 Nginx 获得到 SpringBoot 的 JSON 的返回之后,Nginx 再将响应数据回复给客户端。
3.Nginx代理转发规则
所谓『代理』指的就是 Nginx『帮』真正的服务端所接收的请求,那么也就意味着这样的请求,Nginx 最终需要再交给真正的服务端去处理。
3.1 两个需要提前交代的问题
「减法」问题
在处理转发请求时,Nginx 常常对 URL 做一个「减法」操作,即,减去 URL 中的协议、IP 和端口部分,然后再使用剩下的部分。例如:
URL
http://127.0.0.1:8080
做减法后啥,都不剩;URL
http://127.0.0.1:8080/
做减法后,还剩/
;URL
http://127.0.0.1:8080/api
做减法后,还剩/api
;URL
http://127.0.0.1:8080/api/
做减法后,还剩/api/
。
「规则 2 选 1」问题:
用户的原始 URL 会被 Nginx「加工」成什么样子?请求会被转发到谁那里?有 2 套规则,具体是哪套规则起作用取决于你的 location 中的 proxy_pass 做「减法」后还剩不剩东西?例如
上面 4 个 URL ,后 3 个 URL 使用同一个规则,而第 1 个 URL 则使用的是另一个规则。
URL 1:
http://127.0.0.1:8080
URL 2:
http://127.0.0.1:8080/
URL 3:
http://127.0.0.1:8080/api
URL 4:
http://127.0.0.1:8080/api/
3.2 两个 URL 处理规则
下述的
path
是用户请求的「原始路径做减法」之后剩下的内容。
Nginx 会使用两个 URL 处理规则的哪一个来处理用户请求?这取决于你的 proxy_pass
做「减法」之后还剩不剩东西。
规则一:如果『啥都不剩』,转发路径就是
proxy_pass
+path
规则二:如果『还剩东西』(哪怕就剩个
/
),转发路径是proxy_pass
+ (path
-location
)。另外,对于第二个规则,有个配置上的小技巧,如果你的 proxy_pass 是以
/
结尾的( 无论它是长是短 ),那么你的 location 最好也以/
结尾,以避免不必要的麻烦。
Nginx 的判断逻辑伪代码如下:
/* * 根据 `规则一` 和 `规则二` 的不同,它们的使用场景简而言之归纳成一句话: * 如果你需要转发路径被 Nginx 「截取掉」一部分, * 那么你就使用规则二,否则你就使用规则一。 */ if (当前请求是否应该被我转发 == true) { // 取决于 location 的值
// 这个请求 URL 是 Nginx 收到的请求路径
String originURL = “…”; // 比如:http://127.0.0.1:80/api/login
// URI 是 URL 的一部分,URI = URL - 协议 - IP - 端口
String originURI = “…”; // 比如:/api/login
// 转发路径要不要 “砍掉” 一部分
if ( proxy_pass - 协议 - ip - 端口 == “” ) { // 规则一:不砍,URI 直接拼接
return proxy_pass + originUri; // 比如:http://192.172.0.110/api/login
}
else { // 规则二:URI 会砍掉 location ,再拼接
String toUri = originUri - location; // 比如:/api/login - /api/ = login
return proxy_pass + toUri; // 比如:http://192.172.0.110/login
}
}
补充两点:
无论如何配置你配置
proxy_pass
的内容最后一定会『完全地』包含在转发、去往的路径中。location 是否以
/
结尾问题不大,因为 Nginx 会认为/
本身就是 location 的内容本身( 的一部分 )。
3.3 示例
假设 Nginx 运行在 127.0.0.1 ,它所代理的目标服务在 192.172.3.110 上。
示例一:不需要截取路径中的 /api
假设 Nginx 接收到的请求是 127.0.0.1:8080/api/login
nginx 配置如下:
location /api {
proxy_pass http://192.172.3.110:8080;
}
用户原始请求的 URL 做减法后的剩下的内容是:/api/login 。
上述配置的 proxy_pass 做减法之后啥都不剩,因此使用上述规则一:proxy_pass + path :
http://192.172.3.110:8080 + /api/login
└──> http://192.172.3.110:8080/api/login
最终 Nginx 会将请求发给 http://192.172.3.110:8080/api/hello 。
注意,一番处理之后,这里的 /api/ 没有被截取掉,仍然还在。
示例二:需要截取路径中的 /api
假设 Nginx 接收到的请求是 127.0.0.1/api/login 。
location /api/ {
proxy_pass http://192.172.3.110:8080/;
}
用户原始请求的 URL 做减法后的剩下的内容是:/api/login 。
上述配置的 proxy_pass 做减法之后还剩个 / ,因此使用上述规则二:proxy_pass + (path - location) 。
http://192.172.3.110:8080/ + ( /api/login - /api/ )
└──> http://192.172.3.110:8080/login
最终 Nginx 会将请求发给 http://192.172.3.110:8080/login 。
注意,一番处理之后,这里的 /api 被截取掉,还在了。
3.4. 一个完整的 http 配置片段
其中绝大多数内容都是默认配置:
error_log logs/error.log info; # 打开错误日志的 INFO 级别,方便观察错误信息。 ... http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; # TCP 连接存活 65 秒 server { # Nginx 监听 localhost:80 端口 listen 80; server_name localhost;
# 访问 URI 根路径时,返回 Nginx 根目录下的 html 目录下的 index.html 或 index.htm
location / {
root html;
index index.html index.htm;
}
# URI 路径以 /api 开头的将转交给『别人』处理
location /api {
proxy_pass http://localhost:8080/api;
}
# 出现 500、502、503、504 错误时,返回 Nginx 根目录下的 html 目录下的 50x.html 。
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
4.Nginx 实现负载均衡
负载均衡( load balance )就是将负载分摊到多个操作单元上执行,从而提高服务的可用性和响应速度,带给用户更好的体验。
1. 负载均衡的配置
通过 Nginx 中的 upstream 指令可以实现负载均衡,再该指令中能够配置负载均衡服务器组。
目前负载均衡有 4 种典型的配置方式。分别是:
# | 负载均衡方式 | 特点 |
---|---|---|
1 | 轮询方式 | 默认方式。每个请求按照时间顺序逐一分配到不同的后端服务器进行处理。如果有服务器宕机,会自动删除。 |
2 | 权重方式 | 利用 weight 指定轮循的权重比率,与访问率成正比。用于后端服务器性能不均衡的情况。 |
3 | ip_hash 方法 | 每个请求俺早访问 IP 的 hash 结果分配,这样可以使每个方可固定一个后端服务器,可以解决 Session 共享问题。 |
4 | 第三方模块 | 取决于所采用的第三方模块的分配规则。 |
在 upstream 指定的服务器组中,若每个服务器的权重都设置为 1(默认值)时,表示当前的负载均衡是一般轮循方式。
2. 准备工作
编写后台(SpringBoot)项目,简单起见,以占用不同的端口的形式启动 2 次,并在返回的信息中返回各自所占用的端口号。
@Value("${server.port}") String port;
@RequestMapping(“/api/hello”)
public Map<String, String> index(HttpServletRequest request) {
HashMap<String, String> map = new HashMap<>();
map.put(“code”, “10086”);
map.put(“msg”, “success”);
map.put(“data”, this.port);
return map;
}
3. 负载:轮循
轮循方式的关键配置如下:
server { ... location /api { proxy_pass http://xxx/api; ...
# 解决 upstream 名称携带下划线(_)时,访问 400 问题。
# proxy_set_header Host $http_host;
}
}
upstream xxx {
server 127.0.0.1:8080;
server 127.0.0.1:9090;
}
上述的配置中有 2 点需要注意的:
upstream
配置项在http
配置项内,但是在server
配置项外,它们 3 者整体结构如下( 不要写错地方了 ):
http { # http 是爹 server { ... } # 它们俩是两儿子 upstream { ...} }
你所配置的
upstream
的 name 是自定义的,但是不要出现-
号,否则会和 tomcat 有冲突。
你持续访问 http://127.0.0.1/api/hello
你会发现页面的内容会是交替出现 8080
端口和 9090
端口。
4. 负载:加权轮循
加权轮循就是在轮循的基础上,为每个单点加上权值。权值越重的单点,承担的访问量自然也就越大。
upstream xxx { server 127.0.0.1:8080 weight=1; server 127.0.0.1:9090 weight=2; }
按照上述配置,9090
端口的服务将承担 2/3 的访问量,而 8080
端口则承担 1/3 的访问量。
将配置改为上述样子并重启 Nginx 后,再持续访问 http://127.0.0.1/api/hello
你会发现 8080
端口和 9090
端口会以 1-2-1-2-...
的次数交替出现。
除了 weight 外,常见的状态参数还有:
配置方式 | 说明 |
---|---|
max_fails | 允许请求失败次数,默认为 1 。通常和下面的 fail_timeout 连用。 |
fail_timeout | 在经历了 max_fails 次失败后,暂停服务的时长。这段时间内,这台服务器 Nginx 不会请求这台 Server |
backup | 预留的备份机器。它只有在其它非 backup 机器出现故障时或者忙碌的情况下,才会承担负载任务。 |
down | 表示当前的 server 不参与负载均衡。 |
例如:
upstream web_server { server 192.168.78.128 weight=1 max_fails=1 fail_timeout=30s; server 192.168.78.200 weight=2 max_fails=1 fail_timeout=30s; server 192.168.78.201 backup; server 192.168.78.210 down; }
5. 负载:ip_hash 负载
ip_hash 方式的负载均衡,是将每个请求按照访问 IP 的 hash 结果分配,这样就可以使来自同一个 IP 的客户端固定访问一台 Web 服务器,从而就解决了 Session 共享问题。
upstream xxx { ip_hash; server 127.0.0.1:8080; server 127.0.0.1:9090; }
使用上例配置后,你会发现无论你请求多少次 http://127.0.0.1/api/hello
你所看到的端口始终是 8080
和 9090
中的某一个。
目前有四种方案解决用户登录信息存储问题
1.通过ip_hash的方法判断用户ip锁定同一台服务器
2.后端无状态模式,用户登录后后端将获取的权限发送给前端,前端存储在客户端中,权限判断由前端写
3.tomcat共享session功能,每台服务器的session同步更新,缺点:服务端耗费资源,并且只有少数服务器有这种功能
4.使用redis储存用户信息,在用户首次登录时将用户信息用redis的hash类型进行存在,token做键其他权限、信息等做值,后续都从redis中进行获取
6. 将客户端浏览器的 IP 传递到后台 了解
对于后台而言,它所面对的『客户端』就是 Nginx,后台看不见『客户端』浏览器。
这就意味着,你如果你需要在后台获取客户端浏览器的 IP 地址,你需要明确指出让 Nginx 『额外地多携带』一些数据。
location /api {
proxy_pass http://xxx/api;
proxy_set_header X-Real-IP $remote_addr;
# 解决 nginx 不会“转发” Cookie 问题
# proxy_set_header Cookie $http_cookie;
# 解决 upstream 名称携带下划线(_)时,访问 400 问题。
# proxy_set_header Host $http_host;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
在 Spring Boot 的 Controller 中你有 2 种方式来获得这个额外的信息:
public Map<String, String> index(
HttpServletRequest request,
@RequestHeader("X-Real-IP") String realIP2) {
String realIP1 = request.getHeader("X-Real-IP");
...
}