【问题标题】:HttpUrlConnection taking 100% CPU when running on CentOSHttpUrlConnection 在 CentOS 上运行时占用 100% CPU
【发布时间】:2017-05-18 10:55:52
【问题描述】:

我有一个 HttPURLConnection 程序,它正在与运行在同一台机器(在本例中为 CentOS)上但部署在其他一些 tomcat 服务器上的某个应用程序建立连接。这意味着部署在不同 tomcat 上的两个应用程序之间会建立一个 HttpConnection,其中两个 tomcat 都在同一台机器上运行。

一切正常,当我发出一个请求时,我在 200 英里内得到了响应。 但是,当应用程序通过 JMeter 加载 100 个并行运行的请求以进行无限循环时,应用程序开始占用高 CPU 处理能力。

这是我的代码:

public String sendHttpGetReq(String urlParameters,String msisdn, InNodeConfig inNodeConfig, InNodeDetails inNodeDetails)
{
    Logger.sysLog(LogValues.info, this.getClass().getName(),"["+msisdn+"] Inside sendHttpGetReq");
    String response = "";
    //response =  "{\"id\":\"584371732\",\"status_billing\":\"INSUFFICIENT_FUNDS\",\"status_sms\":\"NO_SMS\"}";
    String url = "";
    URL obj = null;
    HttpURLConnection con = null;
    try
    {
        url = "http://"+inNodeConfig.getServerIp()+":"+inNodeConfig.getServerPort()+inNodeConfig.getServiceUri();
        url = url+urlParameters;
        Logger.sysLog(LogValues.info, this.getClass().getName(),"["+msisdn+"] url = "+url);
        obj = new URL(url);
        con = (HttpURLConnection) obj.openConnection();     

        con.setDoOutput(true);
        con.setRequestMethod("GET");
        con.setConnectTimeout(Integer.parseInt(inNodeConfig.getConTimeOut()));
        con.setReadTimeout(Integer.parseInt(inNodeConfig.getConTimeOut())+1000);

        int responseCode = con.getResponseCode();
        Logger.sysLog(LogValues.info, this.getClass().getName(),"["+msisdn+"] HTTP Response Code : " + responseCode);
        Logger.sysLog(LogValues.info, this.getClass().getName(),"["+msisdn+"] HTTP Response message : " + con.getResponseMessage());

        if(responseCode == 202 || responseCode == 200)
        {   
            BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
            String inputLine;
            StringBuffer responseBf = new StringBuffer();

            while ((inputLine = in.readLine()) != null) {
                responseBf.append(inputLine);
            }
            in.close();

            Logger.sysLog(LogValues.info, this.getClass().getName(),"["+msisdn+"] Response received = "+responseBf.toString());

            response = responseBf.toString();
            responseBf = null;
            if(inNodeDetails.getOperator().equalsIgnoreCase("digicel"))
            {
                if(response.startsWith("Received"))
                {
                    response = "ok";
                }
                else if(response.startsWith("{"))
                {
                    // {"id":"185688","status_billing":"INSUFFICIENT_FUNDS","status_sms":"FAILED"}
                    Logger.sysLog(LogValues.APP_DEBUG, this.getClass().getName(),"["+msisdn+"] Parsing json response.");
                    Gson gson = new Gson(); 
                    JsonResponse jsonResponse = gson.fromJson(response, JsonResponse.class);

                    if(jsonResponse.getStatus_billing().equalsIgnoreCase("ok") || jsonResponse.getStatus_billing().contains("SUCCESS")){
                        response="ok";
                    }
                    else if(jsonResponse.getStatus_billing().equalsIgnoreCase("INSUFFICIENT_FUNDS"))
                    {
                        Logger.sysLog(LogValues.APP_DEBUG, this.getClass().getName(),"["+msisdn+"] Response received for INSUFFICIENT_FUNDS.");
                        response = "low balance";
                    }
                    else if(jsonResponse.getStatus_billing().equalsIgnoreCase("FAILED"))
                    {
                        Logger.sysLog(LogValues.APP_DEBUG, this.getClass().getName(),"["+msisdn+"] Failure Response received for some other reason.");
                        response = "nok";
                    }           
                    else
                    {
                        if(inNodeDetails.getCountry().equalsIgnoreCase("JAM")||inNodeDetails.getCountry().equalsIgnoreCase("HAI")){
                            response=jsonResponse.getStatus_billing();
                        }
                        else{
                            gson = null;
                            jsonResponse = null;
                        }

                    }
                }
                else
                {
                    if(inNodeDetails.getCountry().equalsIgnoreCase("HAI") && response.contains("EXCEPTION"))
                    {
                        response = "ok";
                    }
                    else
                    {
                    Logger.sysLog(LogValues.APP_DEBUG, this.getClass().getName(),"["+msisdn+"] Unknown response.");
                    response = "nok";
                    }
                }
            }
        }
        else if(responseCode == 502 || responseCode == 500)
        {
            if(inNodeDetails.getCountry().equalsIgnoreCase("HAI") || inNodeDetails.getCountry().equalsIgnoreCase("JAM"))
            {
                response = "ok";
            }
            else
            {
                response = "nok";
            }

        }
        else
        {
            response = "nok";
            /*Map<String,List<String>> responseMap = con.getHeaderFields();
            for (Map.Entry entry : responseMap.entrySet()) {
                Logger.sysLog(LogValues.APP_DEBUG, Utility.class.getName(), "header key = "+entry.getKey() + " value = " + entry.getValue());
            }*/
        }

        return response;
    }
    catch(SocketTimeoutException ex)
    {
        Logger.sysLog(LogValues.APP_DEBUG, Utility.class.getName(), "Read Timeout occurs for msisdn = "+msisdn);
        return "ReadTimeout";
    }
    catch(ConnectTimeoutException ex1)
    {
        Logger.sysLog(LogValues.APP_DEBUG, Utility.class.getName(), "ConnectTimeoutException occurs for msisdn = "+msisdn);
        return "ConnectionTimeout";
    }
    catch(ConnectException ex2)
    {
        Logger.sysLog(LogValues.APP_DEBUG, Utility.class.getName(), "ConnectException occurs for msisdn = "+msisdn);
        return "ConnectionTimeout";
    }
    catch(JsonSyntaxException ex3){
        response=null;
        return response;
    }
    catch(MalformedJsonException ex4){
        response=null;
        return response;
    }
    catch(Exception e)
    {
        Logger.sysLog(LogValues.error, this.getClass().getName(),"["+msisdn+"] Internal Error occured"+coreException.GetStack(e));
        return response;
    }
    finally
    {
        response = null;
        url = null;
        obj = null;
        con.disconnect();
        Logger.sysLog(LogValues.APP_DEBUG, this.getClass().getName(),"Calling GC.");
        System.gc();
    }
    //return response;

}

但是,相同的代码在生产环境中运行得非常好,每秒大约有 50 个请求。

当我使用 JVisualVM 和 Sampler 时,我了解到这种方法“sendHttpGetReq”需要很长时间和高 CPU 处理。但从代码中,我找不到代码应该阻塞所有 CPU 的具体原因。

这是采样器屏幕截图:

请提出可能是什么问题?

【问题讨论】:

  • 用探查器检查。
  • 我们无法调试这段代码并告诉您它的哪一部分导致您的 CPU 使用率过高。你需要去分析你的代码,让分析器告诉你瓶颈在哪里。
  • @Kayaman 我已经使用了 jVisualVM 和 Sampler,并且知道这个特定的代码占用了大量的 CPU 时间。也更新了问题。
  • 该代码的哪一部分?你有几十行代码,visualvm 能够告诉你一个更具体的热点。
  • 下定决心。阻塞,还是占用 100% CPU?这不是一回事。

标签: java tomcat centos jmeter cpu-usage


【解决方案1】:

我认为最终调用的 System.gc() 方法可能会导致问题。 建议您评论 System.gc() 并尝试一下。

【讨论】:

  • 你是对的。注释掉 System.gc() 使事情变得更快。谢谢你的帮助。你拯救了我的一天。顺便说一句,但为什么它在生产环境中运行良好?
  • @KayV 生产环境可能有一个-XX:+DisableExplicitGC 标志。但实际上,您应该学会更好地使用 VisualVM。它会直接向您表明System.gc() 是负责任的(不要争辩说它没有,我已经使用它足够长的时间来了解它是如何工作的)。另外,无论如何你都不应该打电话给System.gc(),否则代码也不是那么好。您可以使用比直接使用 HttpURLConnection 更好的库。
  • @Kayaman 请看截图。 VisualVM Sampler 没有显示任何关于 System.gc() 的信息。而且代码是之前写的遗留代码。
猜你喜欢
  • 2012-12-29
  • 1970-01-01
  • 1970-01-01
  • 2022-12-15
  • 2011-11-09
  • 2019-09-06
  • 1970-01-01
  • 2012-03-10
  • 1970-01-01
相关资源
最近更新 更多