使用jersey ApacheHttpClient进行多次调用时无法获取Connection

时间:2011-01-10 15:20:10

标签: spring httpclient jersey

使用com.sun.jersey.client.apache.ApacheHttpClient和Spring调用rest api时遇到连接时遇到问题。几个电话都通过了,但我突然得到了一个:

DEBUG (org.apache.commons.httpclient.MultiThreadedHttpConnectionManager:501) : - Unable to get a connection, waiting..., hostConfig=HostConfiguration[host=www.dummyapi.com]`

就是这样。没什么了!

这是我的applicationContext.xml:

                      

<bean id="customHttpClient" class="com.sun.jersey.client.apache.ApacheHttpClient">
    <constructor-arg>
        <bean class="com.sun.jersey.client.apache.ApacheHttpClientHandler" >
            <constructor-arg>
                <bean class="org.apache.commons.httpclient.HttpClient" >
                    <constructor-arg>
                        <bean class="org.apache.commons.httpclient.MultiThreadedHttpConnectionManager" >
                            <property name="params">
                                <bean class="org.apache.commons.httpclient.params.HttpConnectionManagerParams">
                                    <property name="defaultMaxConnectionsPerHost" value="10" />
                                </bean>
                            </property>
                        </bean>
                    </constructor-arg>
                </bean>
            </constructor-arg>
        </bean>
    </constructor-arg>
    <constructor-arg ref="customClientConfiguration" />
</bean>

<bean id="customRestClient" class="com.custom.web.service.common.RestClient" >
    <constructor-arg ref="customHttpClient" />
    <constructor-arg value="${my.service.url}" />
</bean>

这些是我的课程:

import com.sun.jersey.client.apache.config.DefaultApacheHttpClientConfig;
import org.codehaus.jackson.jaxrs.JacksonJsonProvider;

public class CustomApacheHttpClientConfig extends DefaultApacheHttpClientConfig {

/**
 * Constructor with username and password.
 * @param username
 * @param password
 */
public CustomApacheHttpClientConfig(final String username, final String password) {
    getClasses().add(JacksonJsonProvider.class);
    getProperties().put(PROPERTY_PREEMPTIVE_AUTHENTICATION, Boolean.TRUE);
    getState().setCredentials(null, null, -1, username, password);
}
}

package com.custom.web.service.common;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import org.apache.log4j.Logger;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.ObjectMapper;

import javax.ws.rs.core.MediaType;
import java.io.IOException;
import java.net.URI;

public class RestClient {

private static final Logger LOG = Logger.getLogger(RestClient.class);

private final String baseUrl;
private final Client client;
private final ObjectMapper objectMapper;

public RestClient(final String baseUrl, final Client client) {
    this.baseUrl = baseUrl;
    this.client = client;
    this.objectMapper = new ObjectMapper();
    this.objectMapper.getDeserializationConfig().set(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}

public RestClient(final String baseUrl, final Client client, final ObjectMapper objectMapper) {
    this.baseUrl = baseUrl;
    this.client = client;
    this.objectMapper = objectMapper;
}

public String getBaseUrl() {
    return baseUrl;
}

public Client getClient() {
    return client;
}

public ObjectMapper getObjectMapper() {
    return objectMapper;
}
}

电话:

public void do() {
UriBuilder uriBuilder = UriBuilder.fromUri(
        String.format(
                this.customRestClient.getBaseUrl(),
               "SE")).
            path(SOME_UNIT_PATH_PARAM).
            path(id).
            path(SOME_OTHER_PATH_PARAM);

ClientResponse response = this.customRestClient.getClient().resource(uriBuilder.build()).get(ClientResponse.class);

    if (response.getClientResponseStatus().getStatusCode() != 200) {
        throw new CustomRestClientException(String.format("Something went wrong when getting stuff from rest api for id: %s %d %s ",
                id, response.getClientResponseStatus().getStatusCode(),
                " reasonPhrase: " + response.getClientResponseStatus().getReasonPhrase()));
    }

    String json = response.getEntity(String.class);

    Stuff[] stuff = null;

    try {
        stuff = this.customRestClient.getObjectMapper().readValue(json, Stuff[].class);
    } catch (IOException e) {
        throw new CustomRestClientException(
                String.format("Something went wrong when reading json value for stuff from api: %s %s %d %s ",
                        id, json, response.getClientResponseStatus().getStatusCode(), " reasonPhrase: " + e.getMessage()));
    }

    List<Stuff> stuffList = new ArrayList<Stuff>();
    stuffList.addAll(Arrays.asList(stuff));

}

正如您所看到的,我没有在每次请求后显式关闭连接,我将其留给ApacheHttpClientHandler。我试图增加defaultMaxConnectionsPerHost,但没有运气。我也浏览过网络,只找到了人们在代码中实例化客户端然后在那里销毁它的例子。请帮我解决这个问题,因为我已经碰到了这堵砖墙。

关心 雅各布!

2 个答案:

答案 0 :(得分:4)

找到我的问题的原因:

似乎我忘了设置soTimeout和connectionTimeout。 documentation明确指出:
soTimout:
定义默认套接字超时(SO_TIMEOUT),以毫秒为单位,即等待数据的超时。超时值为零被解释为无限超时。


connectionTimout:
确定在建立连接之前的超时。值为零表示不使用超时。默认值为零。

所以我所要做的就是定义这个:

<bean id="customHttpClient" class="com.sun.jersey.client.apache.ApacheHttpClient">
        <constructor-arg>
            <bean class="com.sun.jersey.client.apache.ApacheHttpClientHandler" >
                <constructor-arg>
                    <bean class="org.apache.commons.httpclient.HttpClient" >
                        <constructor-arg>
                            <bean class="org.apache.commons.httpclient.MultiThreadedHttpConnectionManager" >
                                <property name="params">
                                    <bean class="org.apache.commons.httpclient.params.HttpConnectionManagerParams">
                                        <property name="defaultMaxConnectionsPerHost" value="30" />
                                        <property name="maxTotalConnections" value="30" />
                                        <property name="soTimeout" value="60000" />
                                        <property name="connectionTimeout" value="5000" />
                                    </bean>
                                </property>
                            </bean>
                        </constructor-arg>
                    </bean>
                </constructor-arg>
            </bean>
        </constructor-arg>
        <constructor-arg ref="customClientConfiguration" />
    </bean>

瞧!一切都像魅力一样! 谢谢skaffman你指出我正确的方向!

答案 1 :(得分:0)

您仍然需要关闭连接,连接管理器不会为您执行此操作。经理只负责汇集连接,它不知道你何时完成使用它们。

如果你不关闭它们,那么连接管理器仍然认为你正在使用它们,它会很快耗尽。