2

我将 SPARQL 查询作为异步请求发送到 SPARQL 端点,目前DBpedia使用dotNetRDF 库。虽然更简单的查询通常可以工作,但更复杂的查询有时会导致超时。

我正在寻找一种通过在发生超时时捕获某些事件来处理超时的方法。

我使用的异步QueryWithResultSet重载之一发送我的查询。SparqlRemoteEndpoint

如前所述,如果异步请求失败SparqlResultsCallbackstate对象将被替换为AsyncError实例。这确实表明存在超时,但它似乎仅在请求发送后10 分钟才这样做。例如,当我的超时时间是 30 秒时,我想在 30 秒后知道请求是否成功。(35 秒也可以,但你明白了。)

这是一个发送两个请求的示例应用程序,第一个请求非常简单,很可能在超时(这里设置为 120 秒)内成功,而第二个请求相当复杂,很容易在 DBpedia 上失败:

using System;
using System.Collections.Concurrent;

using VDS.RDF;
using VDS.RDF.Query;

public class TestTimeout
{
    private static string FormatResults(SparqlResultSet results, object state)
    {
        var result = new System.Text.StringBuilder();

        result.AppendLine(DateTime.Now.ToLongTimeString());

        var asyncError = state as AsyncError;
        if (asyncError != null) {
            result.AppendLine(asyncError.State.ToString());
            result.AppendLine(asyncError.Error.ToString());
        } else {
            result.AppendLine(state.ToString());
        }

        if (results == null) {
            result.AppendLine("results == null");
        } else {
            result.AppendLine("results.Count == " + results.Count.ToString());
        }

        return result.ToString();
    }

    public static void Main(string[] args)
    {
        Console.WriteLine("Launched ...");
        Console.WriteLine(DateTime.Now.ToLongTimeString());

        var output = new BlockingCollection<string>();

        var ep = new SparqlRemoteEndpoint(new Uri("http://dbpedia.org/sparql"));
        ep.Timeout = 120;

        Console.WriteLine("Server == " + ep.Uri.AbsoluteUri);
        Console.WriteLine("HTTP Method == " + ep.HttpMode);
        Console.WriteLine("Timeout == " + ep.Timeout.ToString());

        string query = "SELECT DISTINCT ?a\n"
            + "WHERE {\n"
            + "  ?a <http://www.w3.org/2000/01/rdf-schema#label> ?b.\n"
            + "}\n"
            + "LIMIT 10\n";

        ep.QueryWithResultSet(query,
            (results, state) => {
                output.Add(FormatResults(results, state));
            },
            "Query 1");

        query = "SELECT DISTINCT ?v5 ?v8\n"
            + "WHERE {\n"
            + "  {\n"
            + "    SELECT DISTINCT ?v5\n"
            + "    WHERE {\n"
            + "      ?v6 ?v5 ?v7.\n"
            + "      FILTER(regex(str(?v5), \"[/#]c[^/#]*$\", \"i\")).\n"
            + "    }\n"
            + "    OFFSET 0\n"
            + "    LIMIT 20\n"
            + "  }.\n"
            + "  OPTIONAL {\n"
            + "    ?v5 <http://www.w3.org/2000/01/rdf-schema#label> ?v8.\n"
            + "    FILTER(lang(?v8) = \"en\").\n"
            + "  }.\n"
            + "}\n"
            + "ORDER BY str(?v5)\n";

        ep.QueryWithResultSet(query,
            (results, state) => {
                output.Add(FormatResults(results, state));
            },
            "Query 2");

        Console.WriteLine("Queries sent.");
        Console.WriteLine(DateTime.Now.ToLongTimeString());
        Console.WriteLine();

        string result = output.Take();
        Console.WriteLine(result);

        result = output.Take();
        Console.WriteLine(result);

        Console.ReadLine();
    }
}

当我运行它时,我可重复地得到如下输出:

13:13:23
Server == http://dbpedia.org/sparql
HTTP Method == GET
Timeout == 120
Queries sent.
13:13:25

13:13:25
Query 1
results.Count == 10

13:23:25
Query 2
VDS.RDF.Query.RdfQueryException: A HTTP error occurred while making an asynchron
ous query, see inner exception for details ---> System.Net.WebException: Der Rem
oteserver hat einen Fehler zurückgegeben: (504) Gatewaytimeout.
   bei System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
   bei VDS.RDF.Query.SparqlRemoteEndpoint.<>c__DisplayClass13.<QueryWithResultSe
t>b__11(IAsyncResult innerResult)
   --- Ende der internen Ausnahmestapelüberwachung ---
results == null

显然,确切的时间会有所不同,但关键是基于第二个查询的错误消息是在请求发送后大约10 分钟收到的,与超时设置的 2 分钟相差甚远。

我是否在这里错误地使用了 dotNetRDF,或者我是否有意运行一个额外的计时器来自己测量超时并自行做出反应,除非同时收到任何响应?

4

1 回答 1

2

不,您没有错误地使用 dotNetRDF,而是似乎存在一个错误,即在异步运行查询时,端点上设置的超时不会得到遵守。这已被归档为CORE-393

顺便说一句,即使修复了这个错误,您也不一定会在设置的超时时遇到硬超时。本质上是您为实例的Timeout属性设置的SparqlRemoteEndpoint值,该值用于设置Timeout.Net 的属性HttpWebRequestHttpWebRequest.Timeout的文档说明如下:

获取或设置 GetResponse 和 GetRequestStream 方法的超时值(以毫秒为单位)。

因此,您可以等待超时以建立POST与查询的连接,然后再次等待超时以开始接收响应。一旦您开始接收响应,超时就变得无关紧要,并且不会受到处理响应的代码的尊重。

因此,如果您想要一个硬超时,您最好自己实现它,从长远来看,这可能是我们可以添加到 dotNetRDF 中的东西,但实现起来更复杂,只需修复关于超时没有为 HTTP 请求兑现的错误。

于 2014-01-08T16:01:32.053 回答