回答
收藏
分享
举报
ngx_http_read_client_request_body() 导致java HttpURLConnection 会偶数次POST超时
提问于2024-11-12 16:14

浏览 4.2k

文章标签

1、 nginx增加自定义模块,代码如下nginx-1.12.2\src\http\modules\ngx_http_demo1_module.c

其中 调用rc=ngx_http_read_client_request_body(r, ngx_http_demo1_body_handler); 读取POST内容  。


2、客户端使用 java: connection =(HttpURLConnection) urls.openConnection();的POST请求


** 问题:HttpURLConnection请求一次成功后,该connect无法断开,导致第二次post会超时。

但只要做一次  get的请求 ,再做post就会成功。

发现问题出在 增加了改语句 ngx_http_read_client_request_body(...)就存在这个问题。

对比发现,使用Python POST或苹果手机APP的 POST都没有这个问题。只有  JAVA HttpURLConnection的 POST才存在这个问题。



nginx-1.12.2\src\http\modules\ngx_http_demo1_module.c



#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>






#define NGX_DEMO1_DATA_LEN  2*1024   // 655360  // 640*1024




static char *ngx_http_demo1(ngx_conf_t *cf, ngx_command_t *cmd,void *conf);
static ngx_command_t  ngx_http_demo1_commands[] = {


    { ngx_string("DEMO1"),
      NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | \
      NGX_CONF_NOARGS,
      ngx_http_demo1,
      NGX_HTTP_LOC_CONF_OFFSET,
      0,
      NULL },


      ngx_null_command
};


static ngx_http_module_t  ngx_http_demo1_module_ctx = {
    NULL,                          /* preconfiguration */
    NULL,       /* postconfiguration */
    NULL,                          /* create main configuration */
    NULL,                          /* init main configuration */
    NULL,                          /* create server configuration */
    NULL,                          /* merge server configuration */
    NULL,                          /* create location configuration */
    NULL                           /* merge location configuration */
};




ngx_module_t  ngx_http_demo1_module = {
    NGX_MODULE_V1,
    &ngx_http_demo1_module_ctx, /* module context */
    ngx_http_demo1_commands,   /* module directives */
    NGX_HTTP_MODULE,               /* module type */
    NULL,                          /* init master */
    NULL,                          /* init module */
    NULL,                          /* init process */
    NULL,                          /* init thread */
    NULL,                          /* exit thread */
    NULL,                          /* exit process */
    NULL,                          /* exit master */
    NGX_MODULE_V1_PADDING
};




static ngx_int_t ngx_http_demo1_get_handler(ngx_http_request_t *r,char *str) {
	ngx_int_t rc=NGX_OK;
	
    rc = ngx_http_discard_request_body(r);
    if (rc != NGX_OK) {
    	return rc;
    }


    ngx_str_t type = ngx_string("text/plain");
    ngx_str_t response =ngx_string(str);// ngx_string("Hello World!");
    response.len=strlen(str);
    r->headers_out.status = NGX_HTTP_OK;
    r->headers_out.content_length_n = response.len;
    r->headers_out.content_type = type;
 
    rc = ngx_http_send_header(r);
    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
    	return rc;
    }
 
    ngx_buf_t *b;
    b = ngx_create_temp_buf(r->pool, response.len);
    if (b == NULL) {
    	return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }
 
    ngx_memcpy(b->pos, response.data, response.len);
    b->last = b->pos + response.len;
    b->last_buf = 1;
 
    ngx_chain_t out;
    out.buf = b;
    out.next = NULL;
 
    return ngx_http_output_filter(r, &out);
}


void ngx_http_demo1_body_handler(ngx_http_request_t *r)
{
    ngx_connection_t *c;
    c = r->connection;
    ngx_log_error(NGX_LOG_ERR, c->log, 0, "ngx_http_demo1_body_handler -->");
}


static ngx_int_t ngx_http_demo1_handler(ngx_http_request_t *r) {
    ngx_int_t rc=NGX_OK;
    ngx_connection_t *c;
    c = r->connection;


	ngx_log_error(NGX_LOG_ERR, c->log, 0, "demo POST	-->  r->method:%d",r->method);


	if (!(r->method & (NGX_HTTP_POST))) {
		   return ngx_http_demo1_get_handler(r,"[GET] hello world!");
	}
	//POST:


	rc=ngx_http_read_client_request_body(r, ngx_http_demo1_body_handler);///// HERE ,ISSUE!!!
	
	rc=ngx_http_demo1_get_handler(r,"[POST] test!");	
	return rc;
}






static char *
ngx_http_demo1(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_core_loc_conf_t  *clcf;


    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
    clcf->handler = ngx_http_demo1_handler;


    return NGX_CONF_OK;


}






java代码如下:

NginText1.java

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;



public class NginxTest1 {
    static String HTTP_URL="http://10.162.129.21:8080/demo1";

    private static String[] postUrl={
            "{\"user\": \"demo1\",\"age\": 1}",
            "{\"user\": \"demo1\",\"age\": 2}",
            "{\"user\": \"demo1\",\"age\": 3}",
            "{\"user\": \"demo1\",\"age\": 4}",
            "{\"user\": \"demo1\",\"age\": 5}",
            "{\"user\": \"demo1\",\"age\": 6}",
    };
    public void test(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                testThreaqd();
            }
        }).start();
    }
    static int postUrlIndex=0;
    public void testThreaqd() {
        System.out.println("test-->");
        for (int i=0;i<postUrl.length;i++){
            System.out.println("--->\n");
            postURL(HTTP_URL, postUrl[postUrlIndex]);
            postUrlIndex++;
        }
    }
    private String postURL(String url,String jsonParam) {
        //System.setProperty("sun.net.http.retryPost", "false");
        System.out.println(postUrlIndex+ ":"+jsonParam);
        String back = "";
        HttpURLConnection connection=null;
        try {
            URL urls = new URL(url);
            System.out.println("--1->");
            connection =(HttpURLConnection) urls.openConnection();

            if (connection==null) {
                System.out.println("Error: connect lost!");
            }
            connection.setRequestMethod("POST");
            connection.setRequestProperty("Connection", "Keep-Alive");
            connection.setRequestProperty("content-Type", "application/json;charset=utf-8");
            // 接收的内容类型也是json格式
            connection.setRequestProperty("Accept", "application/json;charset=utf-8");
            connection.setDoOutput(true);//允许输出流
            connection.setConnectTimeout(3000);
            connection.setReadTimeout(3000);
            OutputStream os = connection.getOutputStream();
            os.write(jsonParam.getBytes("utf-8"));//将 JSON 数据写入输出流
            os.flush();//确保数据被发送出去
            os.close();// 关闭输出流
            connection.connect();

            int responseCode = connection.getResponseCode();
            System.out.println("--2: " +responseCode);
            if (responseCode == HttpURLConnection.HTTP_OK) {
                InputStream inputStream = connection.getInputStream();// 获取输入流来读取响应数据。
                BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
                StringBuilder stringBuilder = new StringBuilder();
                stringBuilder.setLength(0);
                String line;
                while ((line = reader.readLine()) != null) {
                    System.out.println("--3:: "+line);
                    stringBuilder.append(line);
                }
                reader.close();
                inputStream.close();
                back = stringBuilder.toString();
            }else{
                //处理错误响应。
                back = "error";
                System.out.println("--4:error back");
            }
            System.out.println("--5->");
        }catch(Exception e){
            System.out.println(e.toString());
            back = e.toString();
        }finally{
            if (connection != null) {
                System.out.println("disconnect()");
                //connection.disconnect();
                if (false){
                    try {
                        System.out.println("--6->");
                        // 出现一个问题java.net.SocketTimeoutException: Read timed out,
                        // 这个postURL()函数连续执行,会出现第一次成功,偶数次都会超时的问题。
                        // 增加这几行代码,刻意解决问题
                        URL urls = new URL(url);
                        connection = (HttpURLConnection) urls.openConnection();
                        connection.setRequestMethod("GET");
                        connection.setReadTimeout(1);
                        connection.getResponseCode();
                    } catch (Exception e) {
                    }
                }
             }
        }
        System.out.println("return :"+back);
        return back;
    }
}


已修改于2024-11-12 16:14



写下您的回答
发表回答
全部回答(1)

按点赞数排序

按时间排序

LOG如下:偶数次是超时。

Hello and welcome!

test-->

--->


0:{"user": "demo1","age": 1}

--1->

--2: 200

--3:: [POST] test!

--5->

disconnect()

return :[POST] test!

--->


1:{"user": "demo1","age": 2}

--1->

java.net.SocketTimeoutException: Read timed out

disconnect()

return :java.net.SocketTimeoutException: Read timed out

--->


2:{"user": "demo1","age": 3}

--1->

--2: 200

--3:: [POST] test!

--5->

disconnect()

return :[POST] test!

--->


3:{"user": "demo1","age": 4}

--1->

java.net.SocketTimeoutException: Read timed out

disconnect()

return :java.net.SocketTimeoutException: Read timed out

--->


4:{"user": "demo1","age": 5}

--1->

--2: 200

--3:: [POST] test!

--5->

disconnect()

return :[POST] test!

--->


5:{"user": "demo1","age": 6}

--1->

java.net.SocketTimeoutException: Read timed out

disconnect()

return :java.net.SocketTimeoutException: Read timed out


Process finished with exit code 0


赞同

0

回复举报

回答于2024-11-12 16:15



回复9106592hhhc
回复
提问者
9106592hhhc
这家伙很懒还未留下介绍~
0
文章
1
问答
0
粉丝
相关问答

options请求?需要跨域访问? 

点赞 0
浏览 7.8k

sticky模块只能在linux下吧,win下没有。tomcat好像有个session复制吧,或者可以用共享session。

点赞 0
浏览 7.7k

没办法完全不受影响的,因为mirror是子请求,当子请求未结束时,主请求消耗的内存至少是无法释放的。你可以尝试在/mirror里,把超时时间大幅度调低,包括connect/read/send,再压下看看。

点赞 0
浏览 10.4k