zoukankan      html  css  js  c++  java
  • Tomcat NGINX 选哪个?我全都要!

    1,前言

    以下内容仅仅是记录个人配置 Springboot 运行在 Tomcat 和 NGINX 上的过程。内容混乱,容易引起强烈的不适。

    SpringBoot 默认使用 Tomcat 作为 Web 服务器,但是如果使用 Tomcat 来提供静态资源,速度和内存占用上会有问题。因为Tomcat 对静态资源的处理也是需要通过 Servlet 来处理的[1],这时候,NGINX 就上场了,为了减少对 Servlet 的调用,我们可以使用 NGINX 来处理静态资源请求。一般来说,可以使用 NGINX 来接收所有的 HTTP 请求,将动态请求转发给 Tomcat 来进行处理,对于静态资源的请求,则直接使用 NGINX 处理。

    在我们日常的开发当中,有时候,我们还需要对用户是否可以上传下载资源进行鉴权。这篇博客还会介绍如何搭配 NGINX 和 Tomcat 来实现上传下载的鉴权。

    这篇博客的思路如下:先介绍如何安装配置 NGINX,接下来介绍如何将一个使用嵌入 Tomcat 的 SpringBoot 项目转为使用外部 Tomcat,接着配置 NGINX 来支持上传下载。

    总结:这篇博客介绍如何配置 NGINX 和 Tomcat 实现 SpringBoot 中带有鉴权的上传和下载静态资源。

    环境

    系统:Windows 10。如果不需要上传,使用 Windows 的 NGINX 即可。但是为了增加上传功能,需要将上传模块编译到 NGINX 中,所以我后面用了 WSL。Tomcat 使用 Windows 版本的,版本是 9.0.40。

    2,NGINX

    安装

    首先,第一件事情就是下载:http://nginx.org/en/download.html

    直接到这个网站下载即可。如果这个链接失效了,那么请用自己喜欢的搜索引擎找下载地址即可。

    由于笔者用的 Windows,直接下载 Windows 的包。之后找个地方解压出来之后,双击运行。运行后,发现什么反应都没有的样子。其实,NGINX 运行在了后台。

    接着用浏览器打开,localhost,就可以看到如下图。这是打开 localhost 就不要用 8080 端口啦,用 80 端口。

    静态资源下载

    配置 conf/nging.conf 这个文件。确保自己有 D:/2019/file 这个文件夹。

          server {
    		listen 8888;
    		server_name resource;
    		charset utf-8;
    		
    		location /file/ {
    			root D:/2019/;
    			autoindex on;
    		}
    	}
    

    一开始我在这里卡住了,访问 /file/ 报 404。这里的问题是 “/file/” 的配置中用的是 root,查 StackOverflow,下面的答案说用 alias。alias 和 root 的区别在于,alias 不会将 location 配置的路径名字放到 alias 配置的路径后面,而 root 会。所以,对于上面的配置,实际上访问的地址是 “D:/2019/file”,然而我并没有这个文件夹,所以 404 了。

    小问题

    如果访问文件名带有中文的文件,会出 404。访问文件夹,会出 500。

    对于这个问题,解决方法是处理好编码问题。在 Windows 上,需要将系统的默认编码改变 utf-8(参考链接:https://jingyan.baidu.com/article/25648fc1471e6a9191fd002e.html),同时设置 NGINX 的编码为 utf-8。

    3,tomcat 启动 spring boot 项目

    主要步骤参考链接:https://blog.csdn.net/u011768325/article/details/78018635

    1, 修改打包方式为 war : https://blog.csdn.net/qq_46632322/article/details/105045502

    2, 移除内嵌的 tomcat,再添加 tomcat 的包

            <!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-servlet-api -->
            <dependency>
                <groupId>org.apache.tomcat</groupId>
                <artifactId>tomcat-servlet-api</artifactId>
                <version>9.0.40</version>
                <scope>provided</scope>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-catalina -->
            <dependency>
                <groupId>org.apache.tomcat</groupId>
                <artifactId>tomcat-catalina</artifactId>
                <version>9.0.40</version>
            </dependency>
    

    中间遇到的问题是:程序包org.apache.catalina不存在。最开始配置的时候,使用的是 tomcat 10,在 maven 中添加的版本也是 10。但是出现了问题,第一个问题就是,我没有添加 tomcat-catalina,于是 build 的时候,找不到 tomcat 相关的包,比如 catalina 之类的。添加依赖,不仅仅要添加 servlet,还要添加 tomcat。接下来的问题就很麻烦了,这个问题就是包名的变化,从 javax 到 jakarta 的改变,可以将自己程序中的 javax 改成 jakarta。但是,如果是仅仅改改自己的包名那还简单。问题是,很多其他的包,比如 shiro,依赖的是 javax,如果传入一个 jakarta,shiro 可是不认的呢。

    Fuck Oracle!

    因此,最终是用 tomcat 9.0.40 这个版本,这样就避免了代码的更改,直接改 pom 依赖就好了。

    3,配置 IDEA,这个参考了这里:https://blog.csdn.net/Goodbye_Youth/article/details/85640726

    4,配置 tomcat 和 nginx

    tomcat 不需要配置。

    nginx 需要配置,将请求转发到 8080 端口。注意下面将 “/file/” 配置在了 “/” 前面,因为 NGINX 的规则采用优先匹配,在前面的先匹配。对于静态资源的请求,我们直接处理,如果不是,我们再转发给 tomcat 处理。

    
    worker_processes  1;
    
    events {
        worker_connections  1024;
    }
    
    http {
        include       mime.types;
        default_type  application/octet-stream;
        sendfile        on;
        keepalive_timeout  65;
        gzip  on;
    
        upstream tomcat {
          server 127.0.0.1:8080;
        }
    	
        server {
            listen       80;
            server_name  localhost;
    	charset utf-8;
            
    	location /file/ {
          	      root D:/2019/;
    	      autoindex on;
    	}
    		
            location / {
                proxy_set_header	Host $host;
    	    proxy_set_header        X-Real-IP $remote_addr;
                proxy_pass http://tomcat;
            }
    		
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
    
        }
    
    }
    
    
    

    5,NGINX 资源下载鉴权

    在 NGINX 的静态资源配置选项下面使用 internal 关键字,SpringBoot 在返回体中设置好 X-Accel-Redirect 头部,参考:https://www.shuzhiduo.com/A/E35pRY4Kzv/

    做的过程中遇到一个坑了我很久时间的问题是:编码问题。如果文件名字没有处理好,遇到了中文字符,那么下载下来的文件总是只有 4KB。真是个坑,把我坑了好久。

        @GetMapping("/download/{key}/{filename}")
        @ApiOperation(value = "下载文件", notes = "下载文件, 鉴权")
        public void downloadFile(@PathVariable("filename") String filename,
                                 @PathVariable("key") String key,
                                 HttpServletResponse response) {
            if ("233333".equals(key)) {
                try {
                    response.setHeader("X-Accel-Redirect", "/resource/" +
                            URLEncoder.encode(filename, "UTF-8"));
                    response.setHeader("Content-Disposition", "attachment; filename=" +
                            URLEncoder.encode(filename, "UTF-8"));
                    response.setHeader("Content-Type", "application/octet-stream");
                }
                catch (UnsupportedEncodingException e) {
                    LoggerUtil.getLogger().info("后端错误:使用了不支持的编码");
                }
            }
            else {
                JSONObject responseJson = new JSONObject();
                responseJson.put("reason", "密码错误");
                ResponseUtil.responseJson(response, responseJson);
            }
        }
    
    

    6,NGINX 上传鉴权

    重新编译 NGINX

    使用 NGINX 的上传模块。这个模块的原理是,先将上传的东西存储到临时文件夹,再向 tomcat 发送请求。

    主要参考:https://breeze2.github.io/blog/scheme-nginx-php-js-upload-process

    对于上传,需要添加上传相关的模块然后重新编译 NGINX

    看 NGINX 的官网,需要安装依赖的库。不过安装的时候,各种下载不了,所以直接使用 ubuntu 的 apt 来安装。

    https://docs.nginx.com/nginx/admin-guide/installing-nginx/installing-nginx-open-source/#dependencies

    需要安装三个库,pcre, zlib, openssl,对于这每一个库,在 ubuntu 上每个库的报安装的名字如下所示,每个都要谷歌一下才能查到...

    sudo apt install libpcre3 libpcre3-dev
    sudo apt install zlib1g zlib1g-dev
    sudo apt install libssl-dev
    

    显示如图,就表示配置成功了。

    顺便一提:WSL 在 Windows 上 Home 目录在:C:UserszzkAppDataLocalPackagesCanonicalGroupLimited.Ubuntu20.04onWindows_79rhkp1fndgscLocalState ootfshomezzkweb ginx-1.18.0

    WSL 开放 80 端口:https://mlog.club/article/4865246

    配置 NGINX 和 Tomcat

    让 NGINX 运行在当前目录: sudo ./nginx -p $(pwd)

    参考这里 https://breeze2.github.io/blog/scheme-nginx-php-js-upload-process 编写一个上传页面的 html,放到 nginx 工作目录中的 html 文件夹下面。

    问题1:403,这个好解决,问题来自于文件太大,修改 NGINX 配置就好。
    问题2:有时候上传一直在转圈圈,这是为什么呢?

    查看 NGINX 的日志,发现了 408 错误码,304 的意思是内容没有改变,客户端可以使用缓存。

    解决方案:https://blog.csdn.net/AmateurLee/article/details/111963288 ,添加一个 client_body_buffer_size 配置就好了。

        @PostMapping("/nginx-upload")
        @ApiOperation(value = "上传文件", notes = "上传文件, 鉴权")
        public void downloadFile(HttpServletRequest request, HttpServletResponse response) {
            JSONObject requestJson = RequestUtil.retrieveForm(request);
            if ("application/zip".equals(requestJson.get("file_content_type"))) {
                response.setStatus(200);
                response.setContentType("text/html");
                try {
                    response.getWriter().println("upload success");
                }
                catch (IOException e) {
                    LoggerUtil.getLogger().info("后端错误:输出有问题");
                }
            }
            else {
                response.setStatus(403);
            }
        }
    

    NGINX 配置

    
    worker_processes  1;
    
    events {
        worker_connections  1024;
    }
    
    http {
        include       mime.types;
        default_type  application/octet-stream;
        sendfile        on;
        keepalive_timeout  65;
        gzip  on;
        charset utf-8;
        client_max_body_size 1024m;
    
        upstream tomcat {
    	server 127.0.0.1:8080;
        }
    
        server {
            listen       80;
            server_name  localhost;
    
            location /file/ {
    		root /home/zzk/web/nginx-1.18.0/upload_store;
    		autoindex on;
    	}
    		
    	location /resource/ {
    	    # internal;
    	    alias /home/zzk/web/nginx-1.18.0/upload_store;
    	    autoindex on;
    	}
    
            location = /file.html {
                root html;
            }
            
            location /upload {
                client_max_body_size 1024m;
                client_body_buffer_size 1024k;
    
                upload_pass /example/nginx-upload;
                upload_store /home/zzk/web/nginx-1.18.0/upload_store 1;
                upload_store_access user:r;
    
                # 设置请求体的字段
                upload_set_form_field "${upload_field_name}_name" "$upload_file_name";
                upload_set_form_field "${upload_field_name}_content_type" "$upload_content_type";
                upload_set_form_field "${upload_field_name}_path" "$upload_tmp_path";
    
                # 指示后端关于上传文件的md5值和文件大小
                upload_aggregate_form_field "${upload_field_name}_md5" "$upload_file_md5";
                upload_aggregate_form_field "${upload_field_name}_size" "$upload_file_size";
    
                upload_pass_form_field "^submit$|^description$";
    
                # 若出现如下错误码则删除上传的文件
                upload_cleanup 400 404 499 500-505;
            }
    
            location / {
                proxy_set_header	Host $host;
    	    proxy_set_header        X-Real-IP $remote_addr;
                proxy_pass http://tomcat;
            }
            
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
    
        }
    }
    
    
    

    参考链接

    Tomcat 是否使用 Servlet 来处理静态资源请求 https://www.zhihu.com/question/57400909
    Windows 下的静态资源配置 https://blog.csdn.net/Yl12fh/article/details/81026391
    tomcat 对比 NGINX,还有 Linux 下的静态资源配置 https://juejin.cn/post/6844903875296641038
    NGINX 常用命令 https://segmentfault.com/a/1190000012953480
    设置 Windows 默认编码为 utf-8 https://jingyan.baidu.com/article/25648fc1471e6a9191fd002e.html
    nginx location 详解,下面也讲到了 alias 和 root 的区别 https://www.jianshu.com/p/a16936455018
    关闭 nginx 进程,https://blog.csdn.net/weixin_38111957/article/details/81023124

    Over

  • 相关阅读:
    通过java代码获取jvm信息和系统信息
    java cp与java jar的区别
    vue下实现WebRTC
    MANIFEST.MF文件详解
    element 前端排序 与 后端排序
    JAVA获取CPUID、主板序列号、硬盘序列号、MAC地址(自己验证过)
    PHP常用代码大全
    程序员从初级到中级10个秘诀
    移动平台还有哪些创业机会
    程序员招聘:如何识别真正的程序员
  • 原文地址:https://www.cnblogs.com/zzk0/p/14220716.html
Copyright © 2011-2022 走看看