spring data neo4j 查询超时问题

使用sdn查询时,当数据量较大并且需要做全库扫描时,很容易出现超时问题。抛出如下异常:

Exception in thread "main" com.sun.jersey.api.client.ClientHandlerException: java.net.SocketTimeoutException: Read timed out
    at com.sun.jersey.client.urlconnection.URLConnectionClientHandler.handle(URLConnectionClientHandler.java:149)
    at org.neo4j.rest.graphdb.UserAgent$1.handle(UserAgent.java:68)
    at com.sun.jersey.api.client.filter.HTTPBasicAuthFilter.handle(HTTPBasicAuthFilter.java:81)
    at com.sun.jersey.api.client.Client.handle(Client.java:648)
    at com.sun.jersey.api.client.WebResource.handle(WebResource.java:670)
    at com.sun.jersey.api.client.WebResource.access$200(WebResource.java:74)
    at com.sun.jersey.api.client.WebResource$Builder.post(WebResource.java:553)
    at org.neo4j.rest.graphdb.ExecutingRestRequest.post(ExecutingRestRequest.java:145)
    at org.neo4j.rest.graphdb.RestAPIImpl.query(RestAPIImpl.java:486)

于是考虑加入超时时间,形式如:

System.setProperty("org.neo4j.rest.read_timeout", "80");

但是此时,查询sql调用的形式是,Repository+注解,该System的属性设置对于Spring托管的client并没有生效,此时查询仍然会出现超时问题,接下来会对该问题从源码上做分析。如果此时设置超时时间后,新new出一个client,这是超时时间设置就生效了,具体代码如:

System.setProperty("org.neo4j.rest.read_timeout", "80");
RestAPI restAPIFacade1 = buildClient(address1, userName1, password1);
CypherResult cr = restAPIFacade1.query("xxx", new HashMap<String, Object>());
System.out.println("cost time: " + (System.currentTimeMillis() - ss));
System.out.println(cr.asMap().get("data"));

为何这样设置就会生效,我们可以跟进看一下源码:
RestAPI restAPIFacade1 = buildClient(address1, userName1, password1) 其实调用的是:
restAPIFacade = new RestAPIImpl(address, userName, password);
接下来:

public RestAPIImpl(String uri, String user, String password) {
        this.entityRefetchTimeInMillis = TimeUnit.SECONDS.toMillis(1000L);
        this.entityCache = new RestEntityCache(this);
        this.restEntityExtractor = new RestEntityExtractor(this);
        this.restRequest = this.createRestRequest(uri, user, password);
        this.restIndexAPI = new RestAPIIndexImpl(this);
    }

继续到 this.createRestRequest(uri, user, password):

protected RestRequest createRestRequest(String uri, String user, String password) {
        return new ExecutingRestRequest(uri, user, password);
    }

继续:

public ExecutingRestRequest(String baseUri, String username, String password) {
        this.userAgent = new UserAgent();
        this.baseUri = this.uriWithoutSlash(baseUri);
        this.client = this.createClient();
        this.addAuthFilter(username, password);
    }

继续createClient内部为:

protected Client createClient() {
        Client client = Client.create();
        client.setConnectTimeout(Integer.valueOf(Config.getConnectTimeout()));
        client.setReadTimeout(Integer.valueOf(Config.getReadTimeout()));
        client.setChunkedEncodingSize(Integer.valueOf(8192));
        this.userAgent.install(client);
        if(Config.useLoggingFilter()) {
            client.addFilter(new LoggingFilter());
        }

        return client;
    }

我们注意到,此时client在构造过程中,会有 设置timeout的步骤,继续查看Config.getReadTimeout(),整个Config类不长,贴上全部代码:

package org.neo4j.rest.graphdb.util;

import java.util.concurrent.TimeUnit;

public class Config {
    public static final String CONFIG_PREFIX = "org.neo4j.rest.";
    public static final String CONFIG_STREAM = "org.neo4j.rest.stream";
    public static final String CONFIG_BATCH_TRANSACTION = "org.neo4j.rest.batch_transaction";
    public static final String CONFIG_LOG_REQUESTS = "org.neo4j.rest.logging_filter";
    public static final String WRITE_THREADS = "write_threads";

    public Config() {
    }

    public static int getConnectTimeout() {
        return getTimeout("connect_timeout", 30);
    }

    public static int getReadTimeout() {
        return getTimeout("read_timeout", 30);
    }

    public static boolean streamingIsEnabled() {
        return Boolean.parseBoolean(System.getProperty("org.neo4j.rest.stream", "true"));
    }

    public static boolean useBatchTransactions() {
        return System.getProperty("org.neo4j.rest.batch_transaction", "false").equalsIgnoreCase("true");
    }

    public static boolean useLoggingFilter() {
        return System.getProperty("org.neo4j.rest.logging_filter", "false").equalsIgnoreCase("true");
    }

    private static int getTimeout(String param, int defaultValue) {
        return (int)TimeUnit.SECONDS.toMillis((long)Integer.parseInt(System.getProperty("org.neo4j.rest." + param, "" + defaultValue)));
    }

    public static int getWriterThreads() {
        return Integer.parseInt(System.getProperty("org.neo4j.rest.write_threads", "10"));
    }
}

很简单,在这里我们看到client生成时,超时时间设置的整个过程,在未设置系统参数时,超时时间取的缺省值30s(从代码中也可观察到为什么我们设置运行环境变量的key为 org.neo4j.rest.read_timeout)。那么接下来我们如何设置超时时间呢?这个也很简单,jvm可选参数中做如下设置:

-Dorg.neo4j.rest.read_timeout=150

通常该参数在idea或eclipse中的VM options中。
在有些情况下,我们可能需要直接在tomcat中设置,比如在beta或者生产环境的服务器上。我们可以tomcat的启动参数脚本中设置,通常在tomcat/bin下的catalina.cmd,或catalina.sh,以及startenv.sh等脚本中设置即可。
如果有多台服务器,并且可能部署在虚拟容器上,此时如果生成环境时,就需要额外在tomcat上做上述设置,也是相当的繁琐的。我们上面遇到的问题是,通过spring容器生成的neo4j client 超时时间设置不生效。其实问题在于spring生成实例时,该系统属性并未设置。所以,可以借助spring提供的工具来完成环境参数的设置。
在springContext.xml中做如下配置:

<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="targetClass" value="java.lang.System" />
        <property name="targetMethod" value="setProperty" />
        <property name="arguments">
            <list>
                <value>org.neo4j.rest.read_timeout</value>
                <value>${neo4j.read.timeout}</value>
            </list>
        </property>
    </bean>

注意,根据spring bean的生成顺序,该bean必须在添加在neo4j client 的bean 配置之前方能生效。该方法是较为优雅的解决方法了。
另外关于org.springframework.beans.factory.config.MethodInvokingFactoryBean 的使用,可以另外再做整理和总结了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值