背景:Nginx反向代理springboot后端服务

问题通过浏览器后台发起请求够,由于后台处理时间长,出现504 Gateway Timeout,实际后台程序依然在执行如何解决

如果你恰好在寻找这种问题解决方案,并且不喜欢啰嗦,直接移动到:两种解决方案

目录

两种解决方案


504从哪来:本文的场景下504是nginx返回的。

nginx配置控制超时时间属性

Syntax: proxy_read_timeout time;
Default:
proxy_read_timeout 60s;
Context: httpserverlocation

官方地址Module ngx_http_proxy_module (nginx.org)

官方描述如下:Defines a timeout for reading a response from the proxied server. The timeout is set only between two successive read operations, not for the transmission of the whole response. If the proxied server does not transmit anything within this time, the connection is closed.

一个请求三方参与:浏览器nginx后台服务器

504的错误码是有nginx返回的。结合官网解释我们可以得出结论:

nginx与后台的链接两次读取有效数据之间超过配置时间时,就会产生504超时nginx会主动关闭与后台服务器链接注意两次成功读取间隔,不是整个reponse的时间

默认情况下proxy_read_timeout时60s。

如果你百度google,通常解决方式有两种:提高后台处理效率增大proxy_read_timeout

增大方法简单proxy_read_timeout  [你期望时间]。

But,后台效率提升总是有极限的。而proxy_read_timeout固定值。总会有些正常业务场景,超过了设置timeout值。

两种解决方案

本人解决的问题:上传excel文件后,由于文件大小无法预计,所以后台处理时间也无法预计。同时还要支持文件上传上传后由后台解析处理。post请求返回的是json

一,关闭read-timout可以实现,但是生产环境下你敢不设置超时时间么?所以不建议

二,既然nginx只要从reponse成功读取数据两次的间隔proxy_read_timeout设置时间内,就不会超时。那么我们是不是可以通过持续的向response中写数据来保证不超时呢。

答案是肯定的。

想通了这一点,实现就十分简单

1,正常上传文件

2,新建一个线程。持有response引用,含有标志位,满足条件循环执行程序开始处理数据前,启动线程

3,线程功能只有一个,以固定间隔response中写数据。使nginx与后台链接不超时。

4,这里需要注意,我的方法是返回json,同时要持续向response写入数据,所以我手动拼装json字符串。相当于在之前返回的json中增加一个属性,名称随意,我的叫pending,值随意,非空即可。我是用英文半角的句号” . “。

5,数据处理完后,回调线程stop方法终止线程中的循环

注意:如有雷同纯属巧合。如果已经有大佬讲过这种解决方式,请艾特我,我立即删除本文。

保持线程代码如下

#上下文代码
//获取鲜橙池executor,具体方式个人。不会的直接百度,有很多
response.setContentType(ContentType.APPLICATION_JSON.getMimeType());
ResponseKeeper responseKeeper = new ResponseKeeper(response);
executorService.execute(responseKeeper);
#上下文代码


public class ResponseKeeper implements Runnable {

        /**
        * 循环标志:true停止循环终止线程
        */
        private boolean done = false;
        
        private HttpServletResponse response;

        public void stop(){
            done = true;
        }

        public ResponseKeeper(HttpServletResponse response) {
            this.response = response;
        }

        @Override
        public void run() {
            try {
                response.getWriter().write("{"pending":"");
                while(!done){
                    response.getWriter().write(".");
                    response.getWriter().flush();
                    LOGGER.error("flush-{}", System.currentTimeMillis());
                    Thread.sleep(1000);
                }
                response.getWriter().write("", "status": "0", "msg":"success"}");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

其他问题:

如果你遇到异常

IllegalStateException – if the getOutputStream method has already been called for this response object

那就说明你的程序中有地方调用过了,response.getOutputStream();

需要与已有程序保持一致使用outputStream即可

即将response.getWriter() 提换成 response.getOutputStream();

原因简单来讲就是两个方法互斥。调用了一个就不能调用另一个。

补充:

如果你的应用前端不止一层nginx整体组成是:客户端nginxnginx服务端,本方案可能失效,此时需要第二个nginx中增加配置

具体位置为:localtion / {} 中增加 proxy_buffering off

proxy_buffering关闭后可能影响性能。具体位置应该可以再细化到具体的请求,有兴趣的可以自己试试。

欢迎打赏

原文地址:https://blog.csdn.net/fox_mt/article/details/125249100

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_50338.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注