【Nginx】Nginx部署实战——静态文件+反向代理+均衡负载+Https+Websocket
Nginx是一款是由俄罗斯的程序设计师Igor Sysoev所开发高性能的 Web和 反向代理 服务器,也是一个 IMAP/POP3/SMTP 代理服务器。前一段时间听说Igor Sysoev被俄罗斯警方带走了,不知道放出来了没有。言归正常,来看一下nginx的相关配置如何满足我们的日常需求吧。
0.巧克力安装nginx
由于博主手上是windows,所以直接介绍windows下如何安装nginx,如果想知道linux下如何安装,请阅读另一篇文章http://www.randyfield.cn/post/2019-06-01-netcore-centos/#3-nginx
记得CentOs下是不是有yum
软件包管理器,那windows下呢?有没有这么方便的东西?答案是肯定的,chocolatey,
chocolatey
- chocolatey安装
Get-ExecutionPolicy
#如果返回的是Restricted
#就运行如下命令
Set-ExecutionPolicy AllSigned
#或者
Set-ExecutionPolicy Bypass -Scope Process
#接着执行-Paste the follow text into your shell and press Enter
#Wait a few seconds for the command to complete
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
#If you don't see any errors, you are ready to use Chocolatey
- 安装nginx
choco install nginx
这里有两点需要注意
-
通过
choco
安装nginx
,会配套安装NSSM
,这是个好东西,可以把nginx
注册成windows服务
,相当于linux下的Supervisor
和PM2
-
通过
choco
安装nginx
,在最后一次需要您同意选择y[Yes]
之前,注意回写的内容是,他将会执行C:\ProgramData\chocolatey\lib\nginx\tools\chocolateyInstall.ps1
脚本
$toolsDir = Split-Path -parent $MyInvocation.MyCommand.Definition
. "$toolsDir\helpers.ps1"
$pp = Get-PackageParameters
$arguments = @{
packageName = $env:chocolateyPackageName
file = "$toolsDir\nginx-1.17.8.zip"
destination = if ($pp.installLocation) { $pp.installLocation } else { Get-ToolsLocation }
port = if ($pp.Port) { $pp.Port } else { 80 }
serviceName = if ($pp.NoService) { $null } elseif ($pp.serviceName) { $pp.serviceName } else { 'nginx' }
}
if (-not (Assert-TcpPortIsOpen $arguments.port)) {
throw 'Please specify a different port number...'
}
Install-Nginx $arguments
注意看port
那行,没错,80端口
,nginx默认,所以在继续之前,请检查下80端口
是否被占用,被占用就会安装失败。怎么办?这是您只需要编辑如上脚本,把80
修改为一个未被占用的端口
。然后回到命令窗口继续执行,选择Y,就能成功。
比如我修改为
$toolsDir = Split-Path -parent $MyInvocation.MyCommand.Definition
. "$toolsDir\helpers.ps1"
$pp = Get-PackageParameters
$arguments = @{
packageName = $env:chocolateyPackageName
file = "$toolsDir\nginx-1.17.8.zip"
destination = if ($pp.installLocation) { $pp.installLocation } else { Get-ToolsLocation }
port = if ($pp.Port) { $pp.Port } else { 81 }
serviceName = if ($pp.NoService) { $null } elseif ($pp.serviceName) { $pp.serviceName } else { 'nginx' }
}
if (-not (Assert-TcpPortIsOpen $arguments.port)) {
throw 'Please specify a different port number...'
}
Install-Nginx $arguments
安装好的nginx
在C:\tools
下,配置文件也会默认如下一节的初始化配置
1.初始化配置
#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 81;
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 81;
# listen 81;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
#server {
# listen 81 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;
# }
#}
}
2.部署静态文件
其实初始化就是一个静态文件部署
/
请求到html
文件夹下的 index.html
文件
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 81;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
3.部署Vue构建的静态文件
引用Vue官方文档--”
vue-router
默认hash 模式
—— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。但是这里url
会有一个#
,那就只能改为history 模式,不过这种模式要玩好,还需要后台配置支持。因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问就会返回 404,这就不好看了。就需要加上try_files $uri $uri/ /index.html;
"
try_files的含义是:首先会匹配$uri文件
,如果没有去匹配$url/文件
,如果再没有才会去找/index.html
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 81;
server_name localhost;
location / {
root html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
#proxy_redirect off;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
4.反向代理
同域下部署前后端分离的前端与后端
后端这块可能部署在IIS,Node,Apache等等web服务器,但是我们又不想在前端api请求中硬编码后端url,且如果后端没有添加允许跨越的请求头,浏览器还会阻止。
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 81;
server_name localhost;
location / {
root html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
#proxy_redirect off;
}
location /api/ {
proxy_pass http://ip:port/outside/api/; #反向代理
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_cookie_path /api /;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_cache_bypass $http_upgrade;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
即当前端调用接口http://ip:81/api/
的请求,都会被反向代理至http://ip:port/outside/api/
,如果后台没有做允许跨域的配置,那么这种方式就是欺骗浏览器的解决方案。
5.均衡负载
前后端分离模式的流行,除了解决前后端开发的并行效率问题,解放生产力,解耦,快速定位问题等一系列的优点之外,还有一个就是横向扩展。大并发情况下,可以同时水平扩展前后端服务器,这个扩展就运用了均衡负载。
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
#api接口部署至3个不同的地方
upstream centerapi{
server 192.168.110.10:82 weight=5;
server 192.168.110.11:82 weight=3;
server 192.168.110.10:83 weight=2;
}
server {
listen 81;
server_name localhost;
location / {
root html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
#proxy_redirect off;
}
location /api/ {
proxy_pass http://centerapi/; #反向代理
proxy_http_version 1.1;
proxy_redirect off;
proxy_set_header Host $http_host;
proxy_cookie_path /api /;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_cache_bypass $http_upgrade;
#暴露反向代理的地址
#add_header backendIP $upstream_addr;
#add_header backendCode $upstream_status;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
这里的请求api接口
,nginx
就会按照权重把请求分配下面3个url
upstream centerapi{
server 192.168.110.10:82 weight=5;
server 192.168.110.11:82 weight=3;
server 192.168.110.10:83 weight=2;
}
如果您想测试每次是不是不同的地址,可以让nginx把每次请求转发到的地址返回给浏览器就再加上如下配置
#暴露反向代理的地址
add_header backendIP $upstream_addr;
add_header backendCode $upstream_status;
6.子系统的融合部署
我司有一个ASP.NET MVC
老项目 与前后端分离的新项目的融合
老项目一个平台,包含几个中心,其中一个中心就是新项目
/
---反向代理至IIS
上部署的ASP.NET MVC
主站^~/center1
---前端构建的静态页面,已通过nginx发布^~/center1/api/
---前端使用的api接口,反向代理+均衡负载
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
upstream centerapi{
server 192.168.110.10:82 weight=5;
server 192.168.110.11:82 weight=3;
server 192.168.110.10:83 weight=2;
}
server {
listen 80;
server_name 192.168.110.10;
#主站
location / {
proxy_pass http://192.168.110.10/;#ASP.NET MVC主站 IIS已部署
proxy_http_version 1.1;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_cache_bypass $http_upgrade;
}
#新中心前端
location ^~/center1 {
proxy_pass http://192.168.110.10:81/;#前端构建的静态页面 即下面的配置
proxy_http_version 1.1;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_cache_bypass $http_upgrade;
}
#新中心api
location ^~/center1/api/ {
proxy_pass http://centerapi/;#前端调用的接口
proxy_http_version 1.1;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_cache_bypass $http_upgrade;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
server {
listen 81;
server_name localhost;
location / {
root html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
#proxy_redirect off;
}
#location /api/ {
#proxy_pass http://centerapi/; #反向代理
#proxy_http_version 1.1;
#proxy_set_header Host $http_host;
#proxy_cookie_path /api /;
#proxy_set_header Upgrade $http_upgrade;
#proxy_set_header Connection keep-alive;
#proxy_cache_bypass $http_upgrade;
#暴露反向代理的地址
#add_header backendIP $upstream_addr;
#add_header backendCode $upstream_status;
#}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
7.Https
领导说,以前iis就可以让网站通过https访问,新项目也需要,https
需要ssl证书
,都知道SSL证书只有大公司的证书,人家浏览器才会认,比如Symantec、Entrust、Geotrust,不然就会报不被信任的证书。但是为了迎合领导的需求,还是通过openssl
生成了相关的证书。
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
upstream centerapi{
server 192.168.110.10:82 weight=5;
server 192.168.110.11:82 weight=3;
server 192.168.110.10:83 weight=2;
}
server {
listen 80;
server_name 192.168.110.10;
#ssl开启
ssl on;
ssl_certificate ssl/server.crt;
ssl_certificate_key ssl/server.key;
location / {
proxy_pass http://192.168.110.10/;#ASP.NET MVC主站
proxy_http_version 1.1;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_cache_bypass $http_upgrade;
}
location ^~/center1 {
proxy_pass http://centerapi/;#前端构建的静态页面 即下面的配置
proxy_http_version 1.1;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_cache_bypass $http_upgrade;
}
location ^~/center1/api/ {
proxy_pass http://192.168.110.10:81/;#前端调用的接口
proxy_http_version 1.1;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_cache_bypass $http_upgrade;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
server {
listen 81;
server_name localhost;
location / {
root html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
#proxy_redirect off;
}
#location /api/ {
#proxy_pass http://centerapi/; #反向代理
#proxy_http_version 1.1;
#proxy_set_header Host $http_host;
#proxy_cookie_path /api /;
#proxy_set_header Upgrade $http_upgrade;
#proxy_set_header Connection keep-alive;
#proxy_cache_bypass $http_upgrade;
#暴露反向代理的地址
#add_header backendIP $upstream_addr;
#add_header backendCode $upstream_status;
#}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
8.Websocket
系统突然增加了websocket的服务
,直接访问ws://ip:port
的前端硬编码应该杜绝,所以nginx
再次发挥作用。
websocket协议
不同于http协议
,但是websocket
握手是通过http
,通过协议提升实现通信方式从http转向websocket。
8.1 ws
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
upstream centerapi{
server 192.168.110.10:82 weight=5;
server 192.168.110.11:82 weight=3;
server 192.168.110.10:83 weight=2;
}
upstream websocket{
server 192.168.110.10:1443 weight=1;
}
server {
listen 80;
server_name 192.168.110.10;
#ssl on;
#ssl_certificate ssl/server.crt;
#ssl_certificate_key ssl/server.key;
location / {
proxy_pass http://192.168.110.10/;#ASP.NET MVC主站
proxy_http_version 1.1;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_cache_bypass $http_upgrade;
}
location ^~/center1 {
proxy_pass http://centerapi/;#前端构建的静态页面 即下面的配置
proxy_http_version 1.1;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_cache_bypass $http_upgrade;
}
location ^~/center1/api/ {
proxy_pass http://192.168.110.10:81/;#前端调用的接口
proxy_http_version 1.1;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_cache_bypass $http_upgrade;
}
#websocket
location ^~/websocket/ {
proxy_pass http://websocket;#前端调用的接口
proxy_http_version 1.1;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_connect_timeout 4s;
proxy_read_timeout 600;
proxy_send_timeout 12s;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
server {
listen 81;
server_name localhost;
location / {
root html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
#proxy_redirect off;
}
#location /api/ {
#proxy_pass http://centerapi/; #反向代理
#proxy_http_version 1.1;
#proxy_set_header Host $http_host;
#proxy_cookie_path /api /;
#proxy_set_header Upgrade $http_upgrade;
#proxy_set_header Connection keep-alive;
#proxy_cache_bypass $http_upgrade;
#暴露反向代理的地址
#add_header backendIP $upstream_addr;
#add_header backendCode $upstream_status;
#}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
8.2 wss
正常的情况下(http访问网站,接口),上面就完成了配置,但是一旦ssl on
,原有的ws
,就会导致控制台输出
Mixed Content: The page at ‘https://{域名}.com/‘ was loaded over HTTPS, but attempted to connect to the insecure WebSocket endpoint ‘ws://{ip}:{port}/‘. This request has been blocked; this endpoint must be available over WSS.
Uncaught DOMException: Failed to construct 'WebSocket': An insecure WebSocket connection may not be initiated from a page loaded over HTTPS.
那么就需要如下配置
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
upstream centerapi{
server 192.168.110.10:82 weight=5;
server 192.168.110.11:82 weight=3;
server 192.168.110.10:83 weight=2;
}
upstream websocket{
server 192.168.110.10:1443 weight=1;
}
server {
listen 80;
server_name 192.168.110.10;
ssl on;
ssl_certificate ssl/server.crt;
ssl_certificate_key ssl/server.key;
ssl_session_timeout 5m;
ssl_session_cache shared:SSL:50m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 SSLv2 SSLv3;
ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://192.168.110.10/;#ASP.NET MVC主站
proxy_http_version 1.1;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_cache_bypass $http_upgrade;
}
location ^~/center1 {
proxy_pass http://centerapi/;#前端构建的静态页面 即下面的配置
proxy_http_version 1.1;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_cache_bypass $http_upgrade;
}
location ^~/center1/api/ {
proxy_pass http://192.168.110.10:81/;#前端调用的接口
proxy_http_version 1.1;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_cache_bypass $http_upgrade;
}
location ^~/websocket/ {
proxy_pass http://websocket;#前端调用的接口
proxy_http_version 1.1;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_connect_timeout 4s;
proxy_read_timeout 600;
proxy_send_timeout 12s;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
server {
listen 81;
server_name localhost;
location / {
root html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
#proxy_redirect off;
}
#location /api/ {
#proxy_pass http://centerapi/; #反向代理
#proxy_http_version 1.1;
#proxy_set_header Host $http_host;
#proxy_cookie_path /api /;
#proxy_set_header Upgrade $http_upgrade;
#proxy_set_header Connection keep-alive;
#proxy_cache_bypass $http_upgrade;
#暴露反向代理的地址
#add_header backendIP $upstream_addr;
#add_header backendCode $upstream_status;
#}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
9.常见问题
Nginx 转发时Header中信息丢失
通过Nginx转发后, Header中access_token信息丢失,经查,发现Nginx在转发时,header中带下划线_的属性默认不转发,需要增加配置:
server {
listen 80;
server_name 127.0.0.1;
#charset koi8-r;
#access_log logs/host.access.log main;
underscores_in_headers on;
}
反向代理,操作超时
反向代理某接口,接口处理业务时间超过60秒,就会报time out
的错误.
proxy_read_timeout 600;
10.参考链接
https://blog.csdn.net/qq_29663071/article/details/80759098
https://www.nginx.com/blog/websocket-nginx/
https://blog.csdn.net/duyiwuerluozhixiang/article/details/100358930
作者:Garfield
同步更新至个人博客:http://www.randyfield.cn/
本文版权归作者所有,未经许可禁止转载,否则保留追究法律责任的权利,若有需要请联系287572291@qq.com
微信公众号
扫描下方二维码关注个人微信公众号,实时获取更多干货