Уведомление о превышении времени ожидания для асинхронного запроса

Я отправляю запросы SPARQL в виде асинхронных запросов к конечной точке SPARQL, в настоящее время DBpedia с использованием библиотеки dotNetRDF. В то время как более простые запросы обычно работают, более сложные запросы иногда приводят к таймаутам.

Я ищу способ справиться с таймаутами, фиксируя некоторые события, когда они происходят.

Я отправляю свои запросы, используя один из асинхронных QueryWithResultSet перегрузки SparqlRemoteEndpoint класс

Как описано дляSparqlResultsCallback, state объект будет заменен на 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 неправильно, или это намеренно, что мне нужно запустить дополнительный таймер, чтобы самостоятельно измерить время ожидания и отреагировать самостоятельно, если пока что не получено никакого ответа?

1 ответ

Решение

Нет, вы не используете dotNetRDF неправильно, скорее всего, есть ошибка, связанная с тем, что тайм-ауты, установленные для конечной точки, не учитываются при асинхронном выполнении запросов. Это было подано как CORE-393

Кстати, даже если эта ошибка исправлена, вы не обязательно получите жесткий тайм-аут в установленное время. По сути, значение, которое вы установили для Timeout собственность SparqlRemoteEndpoint Экземпляр это значение используется для установки Timeout собственность.Net HttpWebRequest, Документация для HttpWebRequest. Timeout гласит следующее:

Получает или задает значение времени ожидания в миллисекундах для методов GetResponse и GetRequestStream.

Таким образом, вы можете подождать до истечения времени ожидания, чтобы установить соединение с POST запрос, а затем снова до истечения времени ожидания, чтобы начать получать ответ. Как только вы начинаете получать ответ, тайм-аут становится неактуальным и не учитывается кодом, который обрабатывает ответ.

Поэтому, если вам нужен жесткий тайм-аут, вам лучше реализовать его самостоятельно, в долгосрочной перспективе это может быть то, что мы можем добавить в dotNetRDF, но это более сложный способ реализации, который просто исправляет ошибку о том, что тайм-аут не учитывается для HTTP-запроса.

Другие вопросы по тегам