Nginx从入门到解决实际问题

Nginx从入门到解决实际问题

本文为nginx的入门教程,适合零基础同学,以及了解较浅显的同学。

Docker启动的nginx如何解决浏览器跨域问题

1. 序言

Nginx是异步框架的网页服务器,也可以用作反向代理、负载平衡器和Http缓存。该软件由俄罗斯程序员伊戈尔 ⋅ \cdot ⋅塞索耶夫开发并于2004年首次公开发布。2011年成立同名公司以提供支持服务。2019年3月11日,Nginx公司被F5网络公司以6.7亿美元收购。

Nginx是免费的开源软件,根据类BSD许可证的条款发布。大部分Web服务器使用Nginx,通常作为负载均衡器。

2. Nginx介绍

对于大多数个人开发者,Nginx的主要作用有两个:

  • (前端开发)静态资源代理(Web服务器):前端项目打包后的dist、视频、图像等
  • (后端开发)反向代理:将流量转发到后端服务器

我们默认使用Docker启动Nginx服务器,这是目前最方便、最普遍、最纯粹的启用方式,如果还不了解,可以先行快速了解一下。(目前,我在后端开发中,Nginx MySQL Redis RabbimtMQ Nacos都使用Docker容器)

快速介绍

启动一个Nginx镜像,宿主机端口80,容器名称为the-nginx,在后台运行:

shell 复制代码
docker run -d --name the-nginx -p 80:80 nginx

在自己的浏览器中,输入http://localhost:80/ 观察到以下界面:

Nginx的功能主要由两个关键的配置文件决定。

  1. 系统配置文件目录/etc/nginx/
  2. 媒体资源文件目录/usr/share/nginx/html

在/etc/nginx目录下的主要配置文件为:

  • /etc/nignx/nginx.conf:全局配置文件,定义了nginx的基本配置(默认不需要改)
  • /etc/nginx/conf.d/default.conf:默认配置文件(用户关注)

查看一下nginx.conf配置文件:

dart 复制代码
user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/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  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}

这里不做详细介绍,只提醒一点include /etc/nginx/conf.d/*.conf;,我们可以自定义配置文件(创建),但是需要以.conf为结尾。

查看default.conf文件:

ini 复制代码
server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;

    #access_log  /var/log/nginx/host.access.log  main;

    location / {
        root   /usr/share/nginx/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   /usr/share/nginx/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;
    #}
}

这里关于server的配置文件,我也不做过多介绍,网上一抓一大把,我也不希望制造更多的文字垃圾。

下面我直接使用例子出发,介绍如何使用。

3. Nginx使用场景

3.1 静态资源代理(Web服务器)

这里我们使用Vue前端项目来作示例。需要保证你的PC上已经安装了npm和git,核心代码内容

ts 复制代码
// 不跨域
axios.get("/api/hello")
    .then(function (response) {
      console.log(response)
    })


// 跨域访问后端项目
axios.get("http://localhost:9000/hello")
    .then(function (response) {
      console.log(response)
    })
  1. 拉取仓库
shell 复制代码
git clone https://github.com/xlxingRun/nginx-demo-front.git
  1. 安装依赖
shell 复制代码
npm install
  1. 开发环境热启动(仅在开发环境使用)
shell 复制代码
npm run dev
  1. 编译打包
shell 复制代码
npm run build

本项目只需要执行步骤1 2 4,编译打包后会生成一个dist文件夹,这个是生产环境要部署的静态资源。

将静态资源放置到nginx的资源目录中,并重新启动该容器

shell 复制代码
docker cp dist/. the-nginx:/usr/share/nginx/html && docker restart the-nginx 

访问http://localhost:80 可以看到如下界面: 可以看到此时nginx是一个web服务器的角色,生产环境基本都是这么弄的。 当然如果你使用nohup npm run dev &也可以,默认5173端口,但是这种方式仅在开发环境使用。

上述关键内容在于文件default.conf,根目录是/usr/share/nginx/html,默认使用index.html或者index.htm作为默认首页。此处我们是将dist/.复制到html中。(注意不是dist,这会将整个文件夹复制到容器中)

ini 复制代码
location / {
    root   /usr/share/nginx/html;
    index  index.html index.htm;
}

其实不只是前端项目,包括服务器上的视频、音频、图像都可以,如果感兴趣可以自行尝试(我都试过,有问题可以联系我交流)。

3.2 反向代理服务器

完成上述步骤后,我们启动go语言实现的简单后端服务,监听9000端口

go 复制代码
func handler1(w http.ResponseWriter, r *http.Request) {
    _, err := fmt.Fprintf(w, "Hello, 第一条消息")
    if err != nil {
       return
    }
}

func handler2(w http.ResponseWriter, r *http.Request) {
    _, err := fmt.Fprintf(w, "Hello, 第二条消息")
    if err != nil {
       return
    }
}

func main() {
    http.HandleFunc("/hello", handler1)
    http.HandleFunc("/api/hello", handler2)
    err1 := http.ListenAndServe(":9000", nil)
    if err1 != nil {
       return
    }

}

拉取仓库

shell 复制代码
git clone https://github.com/xlxingRun/nginx-demo-backend.git

热启动

shell 复制代码
go run main.go

启动Go服务,并监听9000端口,可以直接访问http://localhost:9000/hello/ 查看

此时我们再次查看前端项目http://localhost:80/ F12查看网络

这两个后端请求都失败,一个是跨域错误,一个是404。下面我来简单分析和总结

  • 跨域错误:浏览器80端口服务通过axios访问9000端口
  • 404:资源不存在,访问http://localhost:80/api/hello 没有这个后端服务

下面我们来解决这两个问题,解决的原则是,尽可能前后端服务解耦合。 解决跨域的方式有三种:

前端解决:配置虚拟代理

例如在vue项目中配置:

js 复制代码
module.exports = {
  // 设置端口号和自动打开
  devServer: {
    // 自动打开浏览器
    open: true,
    // 配置端口号
    port: 8888,
    // 配置代理
    proxy: {
      // 请求路径中携带 /api 的话就会,向 http://localhost:3000 发送请求
      '/api': {
        // 目标服务器地址 
        target: 'http://localhost:3000',
        // pathRewrite: { '^/api': '' }
      }
    }
  },
  // 关闭ESLint
  lintOnSave: false
}

该方法不是很推荐,更多信息自行搜索网络(前端不是很懂,就不搬运太多内容了)

后端解决:配置允许跨域

例如SpringBoot项目创建一个配置Bean

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
public class MyCorsFilter {
    @Bean
    public CorsFilter corsFilter() {
        // 1.创建 CORS 配置对象
        CorsConfiguration config = new CorsConfiguration();
        // 支持域
        config.addAllowedOriginPattern("*");
        // 是否发送 Cookie
        config.setAllowCredentials(true);
        // 支持请求方式
        config.addAllowedMethod("*");
        // 允许的原始请求头部信息
        config.addAllowedHeader("*");
        // 暴露的头部信息
        config.addExposedHeader("*");
        // 2.添加地址映射
        UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
        corsConfigurationSource.registerCorsConfiguration("/**", config);
        // 3.返回 CorsFilter 对象
        return new CorsFilter(corsConfigurationSource);
    }
}

各个开发框架都有不止一种配置后端跨域的方式,这里也不班门弄斧,只做基本介绍。

Nginx解决跨域(重点)

上述的前端项目中,我们访问/api/hello的期望是访问的是后端服务,而不是前端项目自己的路由,解决方案为配置反向代理

在default.conf的server块中增加

bash 复制代码
location /api {
    # proxy_pass https://localhost:9000/;
    proxy_pass http://host.docker.internal:9000;
}

其中host.docker.internal为docker内流量转发到localhost的地址(本项目使用docker启动nginx。若直接在操作系统中安装nginx,则使用localhost)

将该配置文件覆盖docker容器中的default.conf并重新启动

shell 复制代码
docker cp default.conf the-nginx:/etc/nginx/conf.d/default.conf && docker restart the-nginx

再次访问http://localhost:80 可以看到访问成功

这里我做一个简单的解释:

nginx作为Web服务器,当访问以/api为前缀的路径时,将流量转发到另一台服务器go,这是一个反向代理,我们也可以基于这一原理实现负载均衡,将流量转发到服务而不是具体的实例,例如

ini 复制代码
upstream goServer { 
    server http://localhost:9000 weight=10; 
    server http://localhost:9001 weight=2; 
    server http://localhost:9002; 
}
location /api {
    # proxy_pass https://localhost:9000/;
    proxy_pass goServer;
}

更多详细内容请自行搜索。


http://localhost:80通过axois访问http://localhost:9000/hello需要解决跨域问题,因为这个是直接调用后端服务的url,允许全部域名。(直接在前端项目中将后端服务写死的这种情况非常不推荐)

我们把后端服务部署在另一台服务器上(api.simple.com)。 详细地说:

在server块中的server_name的意义是,你通过此域名访问该服务,可以匹配的特定的块,一个服务器上是可以定义多个访问域名的。

perl 复制代码
server {
    listen 80;
    server_name www.simple.com; # 用一个域名部署前端项目
    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }
}
server {
    listen 80;
    server_name www.api.simple.com;  #单独用一个api域名代理到后端,并设置允许跨域
    location / {
        proxy_pass http://127.0.0.1:8080; 
        add_header 'Access-Control-Allow-Origin' 'http://www.simple.com';  # 允许www.simple.com跨域
    }
}

4. 最佳实践

  • 前端路由,直接填写如/user /order
  • 前端服务访问后端,前缀添加api,如/api/user /api/order
  • nginx部署反向代理到后端,匹配api前缀,并把前缀去掉访问到后端。
相关推荐
姜鱼问生9 小时前
每次开机手动检查 6 样东西?我写了个 2 秒自检脚本
运维·网络·nginx·docker·容器
xixingzhe21 天前
Nginx 看懂请求耗时瓶颈
服务器·前端·nginx
北风toto1 天前
Windows 下用 OpenSSL 生成 SSL 密钥并配置 Nginx 教程
windows·nginx·ssl
卜锦元2 天前
Docker Compose 部署 Nginx 1.30.6 完整指南
nginx·docker·容器
小小龙学IT2 天前
Nginx 开源 Web 服务器与反向代理深度解
前端·nginx·开源
Coisinier2 天前
docker命令管理镜像及容器
linux·运维·nginx·docker·容器·操作系统
2301_32241428042 天前
基于web的校园失物招领管理系统59695-计算机毕设 原创(免费领源码+部署教程+开发环境)
mysql·nginx·php·apache·thinkphp·phpstudy·wampserver
flytalei3 天前
从YAML到Nginx服务访问:动手理解 Kubernetes Pod、Deployment 与 Service
运维·nginx·kubernetes
健康活着就好3 天前
# Nginx负载均衡四种策略,从原理到配置
nginx