【问题标题】:Jersey Client HTTPS performance issueJersey 客户端 HTTPS 性能问题
【发布时间】:2012-11-07 19:51:18
【问题描述】:

我正在使用带有JERSEY Client + HTTPSJUnit 测试来测试在Jetty 上运行的secure web service。我对wr.get( ClientResponse.class) 的一个呼叫在9-10 requests 的每个块后挂起10 秒。等效的Apache client 代码在几毫秒内运行。如果我切换Jetty to HTTP 模式,问题就会消失。

我正在使用Jersey bundle & client 1.14 and Jetty 6.1.26



apache客户端工作

@Test
public void testApacheClient() throws Exception
{
  HttpClient client = new HttpClient();
  ProtocolSocketFactory socketFactory = new EasySSLProtocolSocketFactory();
  Protocol https = new Protocol( "https", socketFactory, 443 );
  Protocol.registerProtocol( "https", https );

  //Finishes in < 1 second
  for ( int i = 0; i < 30; i++ )
  {
    GetMethod getMethod = new GetMethod( "https://localhost:8443/home" );
    client.executeMethod( getMethod );
    String entity = getMethod.getResponseBodyAsString();
    getMethod.releaseConnection();
  }
}



泽西客户端挂起

@Test
public void testJerseyClient() throws Exception

  HostnameVerifier hv = getHostnameVerifier();
  ClientConfig config = new DefaultClientConfig();
  SSLContext ctx = getSslContext();
  config.getProperties().put( HTTPSProperties.PROPERTY_HTTPS_PROPERTIES,
      new HTTPSProperties( hv, ctx ) );

  Client jerseyClient = Client.create( config );

  //Finishes in < 1 second
  for ( int i = 0; i < 30; i++ )
  {
    WebResource wr = jerseyClient.resource( "https://www.google.com" );
    ClientResponse cr = wr.get( ClientResponse.class );
    String entity = cr.getEntity( String.class );
    cr.close();
  }

  /* Pauses for 10 seconds after the 10th request, and subsequently after every 9th request.
     Debugging shows waiting at line 'ClientResponse cr = ...'
   */
  for ( int i = 0; i < 30; i++ )
  {
    WebResource wr = jerseyClient.resource( "https://localhost:8443/home" );
    ClientResponse cr = wr.get( ClientResponse.class );  //10 second pause after requests 9, 18, 27
    String entity = cr.getEntity( String.class ); //This is triggering the problem
    cr.close();
  }

  //Finishes in < 1 second
  for ( int i = 0; i < 30; i++ )
  {
    WebResource wr = jerseyClient.resource( "https://localhost:8443/home" );
    ClientResponse cr = wr.get( ClientResponse.class );
    cr.close();
  }
}

ClientResponse 检索实体似乎会触发问题,但仅限于HTTPS 模式并针对我的Web 服务器(不是google, facebook, etc)运行。我在Jetty 上运行了Jersey ServletContainer and Struts ActionServlet,两者都出现了问题。我还在 subnet 上的不同机器上运行 Web 服务器,并在多台机器上进行了单元测试。



Jersey HTTPS 类

private HostnameVerifier getHostnameVerifier() {
  HostnameVerifier hv = new HostnameVerifier() {
    public boolean verify( String arg0, SSLSession arg1 ) { return true; }
  };
  return hv;
}

private SSLContext getSslContext() throws Exception {      
  private final SSLContext sslContext = SSLContext.getInstance( "SSL" );
  sslContext.init( null, new TrustManager[] { new X509TrustManager() {
        public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
        public void checkClientTrusted( X509Certificate[] certs, String authType ) {}
        public void checkServerTrusted( X509Certificate[] certs, String authType ) {}
      }
    }, new SecureRandom()
  );
  return sslContext;
}



码头 SSL 连接器。如果我使用 SelectChannelConnector 并使用 HTTP 进行单元测试,问题就会消失。

<Call name="addConnector">
  <Arg>
    <New class="org.mortbay.jetty.security.SslSocketConnector">
      <Set name="allowRenegotiate">true</Set>
      <Set name="Port">8443</Set>
      <Set name="reuseAddress">true</Set>
      <Set name="Host">mgmt-int</Set>
      <Set name="maxIdleTime">30000</Set>
      <Set name="handshakeTimeout">2000</Set>
      <Set name="keystore"><SystemProperty name="jetty.home" default="/usr/app/web-app"/>/conf/keystore.jetty</Set>
      <Set name="password">OBF:1vaaaaaaaaaaaaaaaaaaaa111111111v</Set>
      <Set name="keyPassword">OBF:1vaaaaaaaaaaaaaaaaaaaa111111111v</Set>
    </New>
  </Arg>
</Call>

【问题讨论】:

  • 你的握手超时时间太低了,只有 2000 毫秒。将其更改为至少 10-15 秒。
  • 我把handshakeTimeout增加到15000ms,问题依旧。
  • 你能创建一个线程转储,例如在客户端挂起时使用kill -3 pid,看看这会发生在哪里?

标签: java https jersey


【解决方案1】:

这些在什么操作系统上运行?

只是一个猜测,但它可能与缓慢的 /dev/random 实现有关(通过 SecureRandom)。

相关讨论在这里:

How to solve performance problem with Java SecureRandom?

由于我无法始终控制 Web 应用程序中的 JVM 参数,因此我一直使用它作为解决方法:

static {
    System.setProperty("java.security.egd", "file:///dev/urandom");
}

不知道这是否是严格推荐的(但它为我解决了问题)。

【讨论】:

  • 我在 Linux 3.0.0-12-generic 和 2.6.32.49 上运行了 Jetty。我将静态块添加到使用 Jetty 加载的 Java 类中,但没有帮助。 Apache HTTPS 客户端适用于我的 Jetty 服务器,而 Jersey 客户端适用于 https google。只有当 Jersey 客户端针对我的 Jetty 服务器运行时才会出现问题。
【解决方案2】:

尝试稍微修改你的 trustManager 实现:

TrustManager[] trustAllCerts = new TrustManager[] {
        new X509TrustManager() {
            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }

            public void checkClientTrusted(X509Certificate[] certs, String authType) {
                // Trust always
            }

            public void checkServerTrusted(X509Certificate[] certs, String authType) {
                // Trust always
            }
        }
    };

另外,不要忘记在 getSSLContext() 方法结束时调用 HttpsURLConnection.setDefaultSocketFactory:

HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());

【讨论】:

  • 我没有尝试过,但我最终切换到对我来说很好的 Apache 客户端。
猜你喜欢
  • 2011-01-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-12
  • 2018-03-26
  • 1970-01-01
  • 2017-07-28
相关资源
最近更新 更多