1

我有一个由多个客户使用的应用程序(其中一个带有他/她的客户证书)。该应用程序应该通过 HTTPS 与 Web 服务(使用 HttpClient)进行通信。要在此 Web 服务中进行身份验证,需要提供客户端证书。如前所述,每个客户端都有不同的客户端证书,我需要使用当前客户端的证书。我有以下代码正在执行所描述的操作。

X509TrustManager trustManager = new X509TrustManager() {

    public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException {
    }

    public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException {
    }

    public X509Certificate[] getAcceptedIssuers() {
        return null;
    }
};
SSLContext sslcontext = SSLContext.getInstance("TLS");
KeyManager[] managers = /* Code that get the current client's KeyManager[] */;

sslcontext.init(managers, trustManager, null);
SSLSocketFactory socketFactory = new SSLSocketFactory(sslcontext);
socketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

HttpParams params = new BasicHttpParams();
params.setBooleanParameter(ClientPNames.HANDLE_REDIRECTS, true);
HttpClient client = new DefaultHttpClient(params);
Scheme sch = new Scheme("https", socketFactory, 443);

client.getConnectionManager().getSchemeRegistry().register(sch);
HttpPost post = /* Code that get the POST */ 

HttpResponse response = client.execute(post, new BasicHttpContext());

好的,它正在工作。但是有很多线程向这个服务发送请求。有时它给我一些问题。因此,我发现很多人告诉每个应用程序只有一个 HttpClient 更好,并且可以通过以下方式完成:

SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
schemeRegistry.register(new Scheme("https", 443, SSLSocketFactory.getSocketFactory()));

PoolingClientConnectionManager cm = new PoolingClientConnectionManager(schemeRegistry);
// Increase max total connection to 200
cm.setMaxTotal(200);
// Increase default max connection per route to 20
cm.setDefaultMaxPerRoute(20);
// Increase max connections for localhost:80 to 50
HttpHost localhost = new HttpHost("localhost", 80);
cm.setMaxPerRoute(new HttpRoute(localhost), 50);

httpClient = new DefaultHttpClient(cm, params);

这样我就有了一个带有连接池的 HttpClient 。但是如何使用这种方法并为特定请求选择客户端证书?提前致谢。

编辑:

我将尝试解释我为什么要尝试这个。我们有时会遇到问题。服务器停止与 Web 服务器的通信,一段时间后,它又开始工作。需要注意的是,在调用 Web 服务之前已经完成了很多处理。因此,有一个池 Executor 创建线程来执行所有处理。工作完成后,该线程创建另一个与 Web 服务通信的线程。因此,第一个线程加入第二个线程超时(thread.join(timeout))。当第一个线程唤醒时,它会中断尝试与 web 服务通信的线程(如果尚未完成)。我不是实现这个的人,但它应该给请求一个超时来完成。在我的开发机器中,我创建了一个 JMeter 测试,它模拟了许多使用此服务的客户端并连接了 JConsole 以查看发生了什么。在完成 300 个任务(其中一些失败)后,Executor 的池创建的线程死亡。但是很多线程仍然活着,它们在 10 分钟后就死了。当我查看堆栈跟踪时,我看到它与 HttpClient 相关。然后,我开始搜索,发现有人说整个应用程序最好只使用一个 HttpClient ,使其汇集连接。我认为 HttpClient 在搜索要连接的端口时被锁定并被第一个线程中断。然后,不知何故,它锁定了很长时间。Executor 池创建的线程死亡。但是很多线程仍然活着,它们在 10 分钟后就死了。当我查看堆栈跟踪时,我看到它与 HttpClient 相关。然后,我开始搜索,发现有人说整个应用程序最好只使用一个 HttpClient ,使其汇集连接。我认为 HttpClient 在搜索要连接的端口时被锁定并被第一个线程中断。然后,不知何故,它锁定了很长时间。Executor 池创建的线程死亡。但是很多线程仍然活着,它们在 10 分钟后就死了。当我查看堆栈跟踪时,我看到它与 HttpClient 相关。然后,我开始搜索,发现有人说整个应用程序最好只使用一个 HttpClient ,使其汇集连接。我认为 HttpClient 在搜索要连接的端口时被锁定并被第一个线程中断。然后,不知何故,它锁定了很长时间。使其汇集连接。我认为 HttpClient 在搜索要连接的端口时被锁定并被第一个线程中断。然后,不知何故,它锁定了很长时间。使其汇集连接。我认为 HttpClient 在搜索要连接的端口时被锁定并被第一个线程中断。然后,不知何故,它锁定了很长时间。

编辑:

这就是我在 JConsole 中看到的。这一次,我只启动了 100 个任务,并且有 15 个线程处于这种状态:

Name: Thread-228
State: RUNNABLE
Total blocked: 0  Total waited: 0

Stack trace: 
 java.net.SocketInputStream.socketRead0(Native Method)
java.net.SocketInputStream.read(SocketInputStream.java:129)
com.sun.net.ssl.internal.ssl.InputRecord.readFully(InputRecord.java:293)
com.sun.net.ssl.internal.ssl.InputRecord.read(InputRecord.java:331)
com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:798)
   - locked java.lang.Object@7c1975
com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1138)
   - locked java.lang.Object@16b53a6
com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1165)
com.sun.net.ssl.internal.ssl.SSLSocketImpl.getSession(SSLSocketImpl.java:1916)
org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:91)
org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:572)
org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:692)
org.apache.http.conn.scheme.SchemeSocketFactoryAdaptor.connectSocket(SchemeSocketFactoryAdaptor.java:65)
org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:180)
org.apache.http.impl.conn.ManagedClientConnectionImpl.open(ManagedClientConnectionImpl.java:294)
org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:640)
org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:479)
org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:906)
org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:805)
sun.reflect.GeneratedMethodAccessor3671.invoke(Unknown Source)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
java.lang.reflect.Method.invoke(Method.java:597)
org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoCachedMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:229)
org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:52)
org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:128)
br.com.fibonacci.webservice.InvocacaoDeServico$_monteChamada_closure8.doCall(InvocacaoDeServico.groovy:441)
sun.reflect.GeneratedMethodAccessor3667.invoke(Unknown Source)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
java.lang.reflect.Method.invoke(Method.java:597)
org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite$PogoCachedMethodSiteNoUnwrapNoCoerce.invoke(PogoMetaMethodSite.java:266)
org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite.callCurrent(PogoMetaMethodSite.java:51)
org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:150)
br.com.fibonacci.webservice.InvocacaoDeServico$_monteChamada_closure8.call(InvocacaoDeServico.groovy)
sun.reflect.GeneratedMethodAccessor3662.invoke(Unknown Source)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
java.lang.reflect.Method.invoke(Method.java:597)
org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite$PogoCachedMethodSiteNoUnwrapNoCoerce.invoke(PogoMetaMethodSite.java:266)
org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite.call(PogoMetaMethodSite.java:63)
org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:124)
br.com.fibonacci.ChamadaComTimeout$_execute_closure1.doCall(ChamadaComTimeout.groovy:19)
sun.reflect.GeneratedMethodAccessor3657.invoke(Unknown Source)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
java.lang.reflect.Method.invoke(Method.java:597)
org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite$PogoCachedMethodSiteNoUnwrapNoCoerce.invoke(PogoMetaMethodSite.java:266)
org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite.callCurrent(PogoMetaMethodSite.java:51)
org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:150)
br.com.fibonacci.ChamadaComTimeout$_execute_closure1.doCall(ChamadaComTimeout.groovy)
sun.reflect.GeneratedMethodAccessor3655.invoke(Unknown Source)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
java.lang.reflect.Method.invoke(Method.java:597)
org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)
groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:234)
groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1061)
groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:910)
groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:892)
groovy.lang.Closure.call(Closure.java:279)
groovy.lang.Closure.call(Closure.java:274)
groovy.lang.Closure.run(Closure.java:355)
java.lang.Thread.run(Thread.java:662)
4

2 回答 2

2

我认为这是不可能的,因为 IMO 它没有任何意义。
对纯文本重用连接是有意义的,但对于经过身份验证的加密连接则不然。 每个连接都属于一个用户,应该根据 SSL 参数终止,而不是在用户之间重复使用。

于 2012-10-16T20:40:35.967 回答
0

我发现这篇很棒的博客文章http://javaeesupportpatterns.blogspot.com.br/2011/04/javanetsocketinputstreamsocketread0.html解释了我的问题。它与 HttpClient 并没有真正的关系。问题在于超时实现。

于 2012-10-17T12:58:51.507 回答