Проблема с производительностью HTTPS-клиента клиента
Я тестирую JUnit
с JERSEY Client + HTTPS
, чтобы проверить secure web service
на Jetty
. Один из моих вызовов wr.get( ClientResponse.class)
зависает в течение 10 секунд после каждого блока 9-10 requests
. Эквивалентный код 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
и работает против моего веб-сервера (не google, facebook, etc
). Я запустил Jersey ServletContainer and Struts ActionServlet
в Jetty
, и проблема возникает с обоими. Я также запускаю веб-сервер на разных машинах на моем subnet
и модуле, проверенном с нескольких компьютеров.
Джерси 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;
}
Соединитель Jetty SSL. Если я использую SelectChannelConnector и unit test с 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>
Ответы
Ответ 1
В какой ОС они работают?
Просто догадаться, но это может быть связано с медленной реализацией /dev/random
(через SecureRandom
).
Связанное обсуждение здесь:
Как решить проблему производительности с помощью Java SecureRandom?
Поскольку я не могу всегда контролировать параметры JVM в веб-приложении, я использовал это как обходной путь:
static {
System.setProperty("java.security.egd", "file:///dev/urandom");
}
Не знаю, рекомендовано ли это строго (но это решает проблему для меня).
Ответ 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
}
}
};
Кроме того, не забудьте вызвать HttpsURLConnection.setDefaultSocketFactory в конце вашего метода getSSLContext():
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());