Веб-задания Azure/очередь — DecoderFallbackException

Я использую функцию Azure Web Jobs. Вот что я делаю.

Шаг 1

  • У меня есть проект Web Api, который всегда получает одну и ту же строку из одного и того же клиентского приложения.
  • У меня есть простой класс с именем SimpleClass, который имеет только одно свойство с всегда одной и той же строкой из одного и того же клиентского приложения. (Строка APA91bELkr6CyBmqLbWomwkI2zw_GkXGVsblYH60l4hERXw9ZkCcXufjJM_7IZXI5_Ry9aze6AhYRVzBfl6CYq0kxrdV4ViPkW5hK2Rd2HlsZCDfhnOc3PGLt_SzIMjfbMRug_eK_di2YbJTA6weczoTyb-dKuvnwg)
  • Я сериализую его с помощью JsonConvert в строку JSON.
  • Я записываю это в очередь хранилища Azure.

Шаг 2

  • Я создал очень простое демонстрационное задание Azure Web Job для обработки очереди.
  • Работа десериализует объект

Но иногда у меня было исключение (только иногда, это отстой)

Unhandled Exception: System.Text.DecoderFallbackException: Unable to translate bytes [FF] at index 4 from specified code page to Unicode.
at System.Text.DecoderExceptionFallbackBuffer.Throw(Byte[] bytesUnknown, Int32 index)
at System.Text.DecoderExceptionFallbackBuffer.Fallback(Byte[] bytesUnknown, Int32 index)
at System.Text.DecoderFallbackBuffer.InternalFallback(Byte[] bytes, Byte* pBytes)
at System.Text.UTF8Encoding.GetCharCount(Byte* bytes, Int32 count, DecoderNLS baseDecoder)
at System.String.CreateStringFromEncoding(Byte* bytes, Int32 byteLength, Encoding encoding)
at System.Text.UTF8Encoding.GetString(Byte[] bytes, Int32 index, Int32 count)
at Microsoft.WindowsAzure.Storage.Queue.CloudQueueMessage.get_AsString()
at Microsoft.Azure.Jobs.QueueCausalityHelper.GetOwner(CloudQueueMessage msg)
at Microsoft.Azure.Jobs.Host.Runners.Worker.GetFunctionInvocation(FunctionDefinition func, RuntimeBindingProviderContext context, CloudQueueMessage msg)
at Microsoft.Azure.Jobs.Host.Runners.Worker.MyInvoker.Microsoft.Azure.Jobs.ITriggerInvoke.OnNewQueueItem(CloudQueueMessage msg, QueueTrigger trigger, RuntimeBindingProviderContext context)
at Microsoft.Azure.Jobs.Host.Triggers.PollQueueCommand.TryExecute()
at Microsoft.Azure.Jobs.LinearSpeedupTimerCommand.Execute()
at Microsoft.Azure.Jobs.IntervalSeparationTimer.RunTimer(Object state)
at System.Threading.TimerQueueTimer.CallCallbackInContext(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.TimerQueueTimer.CallCallback()
at System.Threading.TimerQueueTimer.Fire()
at System.Threading.TimerQueue.FireQueuedTimerCompletion(Object state)
at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
at System.Threading.ThreadPoolWorkQueue.Dispatch()
at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

Вот мой код

Контроллер веб-API

public class TestController : ApiController
{
    public HttpResponseMessage Post(Model model)
    {
        // Retrieve storage account from connection string.
        CloudStorageAccount storageAccount = CloudStorageAccount.Parse(
            CloudConfigurationManager.GetSetting("StorageConnectionString"));

        // Create the queue client.
        CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();

        // Retrieve a reference to a queue.
        CloudQueue queue = queueClient.GetQueueReference("webjobsqueue");

        // Create the queue if it doesn't already exist.
        queue.CreateIfNotExists();

        // Create a message and add it to the queue.
        CloudQueueMessage message = new CloudQueueMessage(JsonConvert.SerializeObject(new SimpleClass { SimpleStringProperty = Model.Value }));
        queue.AddMessage(message);

        return new HttpResponseMessage(HttpStatusCode.OK);
    }
}

Веб-задание

class Program
{
    static void Main(string[] args)
    {
        var host = new JobHost();
        host.RunAndBlock();
    }

    public static void WaitForMessageInQueue([QueueTrigger("webjobsqueue")]
                                       string message)
    {
        var simpleClass = JsonConvert.DeserializeObject<SimpleClass >(message);
        Console.Out(simpleClass.SimpleStringProperty );
    }
}

###На форуме MSDN есть сообщение с той же проблемой, но без решения Отзыв о веб-заданиях


person dknaack    schedule 23.07.2014    source источник


Ответы (2)


Не каждая последовательность байтов является допустимым текстом UTF-8. Если вы получаете допустимую случайную строку Unicode/UTF-8, все должно работать правильно. Но если вы получаете случайные байты (не текст UTF-8), такое поведение ожидается. CloudQueueMessage, API-интерфейс Azure Storage SDK для работы с сообщениями в очереди, любезно проверяет, имеет ли он действительный текст, прежде чем передать его вам. По-видимому, некоторые другие классы не выполняют такую ​​проверку и вместо этого молча игнорируют недопустимый текст.

Я бы предложил проверить Model.Value в вашем действии веб-API, чтобы убедиться, что это допустимый текст Unicode, прежде чем добавлять сообщение в очередь. Я бы предложил использовать результат от model.Value.Normalize(), в котором говорится, что он выдаст исключение, если строка содержит недопустимые данные Unicode.

person dmatson    schedule 23.07.2014
comment
Спасибо за ваш ответ. Проблема в том, что строка, которую я получаю, всегда одна и та же и из одного и того же источника. Я обновляю свой вопрос. - person dknaack; 23.07.2014
comment
Может ли быть другое сообщение, которое продолжает проходить через вашу очередь? Если сообщение в очереди не может быть обработано, оно становится невидимым на 10 минут, а затем снова появляется. Если вы остановите свое веб-задание на 15 минут, а затем используете обозреватель хранилища для проверки очереди, увидите ли вы там какое-либо другое сообщение? - person dmatson; 24.07.2014
comment
Я сделал это. Других сообщений нет. Я считаю, что это ошибка. Веб-задания Azure по-прежнему доступны в предварительной версии. Я попробую то же самое в рабочей роли. - person dknaack; 24.07.2014

Я использую последнюю версию SDK Webjob, и все работает нормально используя ваш код:

Я создал простую модель:

public class SimpleClass
{
    public string SimpleStringProperty { get; set; }
}

Функция класса:

public class Function
{
    public void WaitForMessageInQueue([QueueTrigger("webjobsqueue")] string message)
    {
        Console.Out.WriteLine(message);
    }
}

А вот мой пример программы веб-работы:

class Program
{
    // Please set the following connection strings in app.config for this WebJob to run:
    // AzureWebJobsDashboard and AzureWebJobsStorage
    static void Main()
    {

        // Retrieve storage account from connection string.
        CloudStorageAccount storageAccount = CloudStorageAccount.Parse(
            CloudConfigurationManager.GetSetting("MyConnectionString"));

        // Create the queue client.
        CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();

        // Retrieve a reference to a queue.
        CloudQueue queue = queueClient.GetQueueReference("webjobsqueue");

        // Create the queue if it doesn't already exist.
        queue.CreateIfNotExists();

        // Create a message and add it to the queue.
        CloudQueueMessage message = new CloudQueueMessage(JsonConvert.SerializeObject(new SimpleClass { SimpleStringProperty = "APA91bELkr6CyBmqLbWomwkI2zw_GkXGVsblYH60l4hERXw9ZkCcXufjJM_7IZXI5_Ry9aze6AhYRVzBfl6CYq0kxrdV4ViPkW5hK2Rd2HlsZCDfhnOc3PGLt_SzIMjfbMRug_eK_di2YbJTA6weczoTyb-dKuvnwg" }));
        queue.AddMessage(message);

        var host = new JobHost();
        host.RunAndBlock();
    }
}
person Thomas    schedule 15.12.2015