Partilhar via


Correlação de resolução de problemas

A correlação é usada para relacionar mensagens de serviço de workflow entre si e com a instância correta do workflow, mas se não estiver configurada corretamente, as mensagens não serão recebidas e as aplicações não funcionarão corretamente. Este tópico apresenta uma visão geral de vários métodos para resolver problemas de correlação e também lista alguns problemas comuns que podem ocorrer quando se utiliza correlação.

Tratar o evento UnknownMessageReceived

O UnknownMessageReceived evento ocorre quando uma mensagem desconhecida é recebida por um serviço, incluindo mensagens que não podem ser correlacionadas com uma instância existente. Para serviços auto-hospedados, este evento pode ser manipulado na aplicação anfitriã.

host.UnknownMessageReceived += delegate(object sender, UnknownMessageReceivedEventArgs e)
{
    Console.WriteLine("Unknown Message Received:");
    Console.WriteLine(e.Message);
};

Para serviços hospedados na Web, este evento pode ser tratado derivando uma classe de WorkflowServiceHostFactory e sobrescrevendo CreateWorkflowServiceHost.

class CustomFactory : WorkflowServiceHostFactory
{
    protected override WorkflowServiceHost CreateWorkflowServiceHost(Activity activity, Uri[] baseAddresses)
    {
        // Create the WorkflowServiceHost.
        WorkflowServiceHost host = new WorkflowServiceHost(activity, baseAddresses);

        // Handle the UnknownMessageReceived event.
        host.UnknownMessageReceived += delegate(object sender, UnknownMessageReceivedEventArgs e)
        {
            Console.WriteLine("Unknown Message Received:");
            Console.WriteLine(e.Message);
        };

        return host;
    }
}

Este personalizado WorkflowServiceHostFactory pode então ser especificado no ficheiro svc do serviço.

<% @ServiceHost Language="C#" Service="OrderServiceWorkflow" Factory="CustomFactory" %>

Quando este manipulador é invocado, a mensagem pode ser recuperada usando a Message propriedade da UnknownMessageReceivedEventArgs, e irá assemelhar-se à seguinte mensagem.

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <To s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://localhost:8080/OrderService</To>
    <Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://tempuri.org/IService/AddItem</Action>
  </s:Header>
  <s:Body>
    <AddItem xmlns="http://tempuri.org/">
      <Item>Books</Item>
    </AddItem>
  </s:Body>
</s:Envelope>

A inspeção das mensagens enviadas ao UnknownMessageReceived manipulador pode fornecer pistas sobre porque é que a mensagem não se correlacionou com uma instância do serviço de workflow.

Use o acompanhamento para monitorizar o progresso do fluxo de trabalho

O acompanhamento oferece uma forma de monitorizar o progresso de um fluxo de trabalho. Por padrão, são emitidos registos de rastreamento para eventos do ciclo de vida do fluxo de trabalho, eventos do ciclo de vida da atividade, propagação de falhas e retomada de marcadores. Além disso, podem ser emitidos registos personalizados de rastreamento por atividades personalizadas. Ao resolver problemas de correlação, os registos de rastreio de atividade, os registos de retomada dos favoritos e os registos de propagação de falhas são os mais úteis. Os registos de monitorização de atividade podem ser usados para determinar o progresso atual do fluxo de trabalho e ajudar a identificar que atividade de mensagens está atualmente à espera de mensagens. Os registos de retomada dos favoritos são úteis porque indicam que uma mensagem foi recebida pelo fluxo de trabalho, e os registos de propagação de falhas fornecem um registo de quaisquer falhas no fluxo de trabalho. Para permitir o rastreamento, especifique o desejado TrackingParticipant em WorkflowExtensions do WorkflowServiceHost. No exemplo seguinte, o ConsoleTrackingParticipant (do exemplo de Rastreio Personalizado ) é configurado usando o perfil de rastreio predefinido.

host.WorkflowExtensions.Add(new ConsoleTrackingParticipant());

Um participante de rastreamento, como o ConsoleTrackingParticipant, é útil para serviços de fluxo de trabalho autogeridos que têm uma janela de consola. Para um serviço hospedado na Web, deve ser utilizado um participante de rastreamento que regista a informação de rastreio num armazenamento duradouro, como o incorporado EtwTrackingParticipant, ou um participante personalizado que regista a informação num ficheiro.

Para mais informações sobre o rastreio e a configuração do rastreio para um serviço de workflow hospedado na Web, consulte Workflow Tracking and Tracing, Configuring Tracking for a Workflow e os exemplos de Tracking [WF Samples].

Utilize o Rastreamento WCF

O rastreio WCF fornece o rastreio do fluxo de mensagens para e de um serviço de fluxo de trabalho. Esta informação de rastreio é útil na resolução de problemas de correlação, especialmente para correlação baseada em conteúdo. Para permitir o rastreamento, especifique os ouvintes de rastreamento desejados na secção system.diagnostics do ficheiro web.config se o serviço de fluxo de trabalho estiver hospedado na Web, ou no ficheiro app.config se o serviço de fluxo de trabalho for hospedado localmente. Para incluir o conteúdo das mensagens no ficheiro de rastreamento, especifique true para logEntireMessage no elemento messageLogging da secção diagnostics de system.serviceModel. No exemplo seguinte, a informação de rastreio, incluindo o conteúdo das mensagens, está configurada para ser escrita num ficheiro chamado service.svclog.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.diagnostics>
    <sources>
      <source name="System.ServiceModel" switchValue="Information" propagateActivity="true">
        <listeners>
          <add name="corr"/>
        </listeners>
      </source>
      <source name="System.ServiceModel.MessageLogging">
        <listeners>
          <add name="corr"/>
        </listeners>
      </source>
    </sources>

    <sharedListeners>
      <add name="corr" type="System.Diagnostics.XmlWriterTraceListener" initializeData="c:\logs\service.svclog">
      </add>
    </sharedListeners>
  </system.diagnostics>

  <system.serviceModel>
    <diagnostics>
      <messageLogging logEntireMessage="true" logMalformedMessages="false"
         logMessagesAtServiceLevel="false" logMessagesAtTransportLevel="true" maxSizeOfMessageToLog="2147483647">
      </messageLogging>
    </diagnostics>
  </system.serviceModel>
</configuration>

Para visualizar a informação de rastreio contida em service.svclog, utiliza-se a ferramenta Service Trace Viewer (SvcTraceViewer.exe). Isto é especialmente útil ao resolver problemas de correlação baseada em conteúdo, pois pode visualizar o conteúdo da mensagem e ver exatamente o que está a ser transmitido, e se corresponde à CorrelationQuery correlação baseada no conteúdo. Para mais informações sobre rastreamento WCF, consulte Service Trace Viewer Tool (SvcTraceViewer.exe),Configurar o Rastreamento e Usar Rastreamento para Diagnosticar a Sua Aplicação.

Questões Frequentes de Correlação na Troca de Contexto

Certos tipos de correlação exigem que seja utilizado um tipo específico de ligação para que a correlação funcione corretamente. Exemplos incluem correlação pedido-resposta, que requer uma ligação bidirecional como BasicHttpBinding, e correlação de troca de contexto, que requer uma ligação baseada no contexto como BasicHttpContextBinding. A maioria das ligações suporta operações bidirecionais, pelo que isto não é um problema comum para a correlação pedido-resposta, mas existem apenas algumas ligações baseadas em contexto, incluindo BasicHttpContextBinding, WSHttpContextBinding, e NetTcpContextBinding. Se uma destas ligações não for utilizada, a chamada inicial a um serviço de workflow terá sucesso, mas as chamadas subsequentes falharão com o seguinte FaultException.

There is no context attached to the incoming message for the service
and the current operation is not marked with "CanCreateInstance = true".
In order to communicate with this service check whether the incoming binding
supports the context protocol and has a valid context initialized.

A informação de contexto usada para correlação de contexto pode ser devolvida pela SendReply à Receive atividade que inicializa a correlação de contexto ao utilizar uma operação bidirecional, ou pode ser especificada pelo chamador se a operação for unidirecional. Se o contexto não for enviado pelo chamador nem devolvido pelo serviço de workflow, então o mesmo FaultException descrito anteriormente será devolvido quando uma operação subsequente for invocada.

Questões comuns de correlação Pedido-Resposta

A correlação solicitação-resposta é usada com um Receive/SendReply par para implementar uma operação bidirecional em um serviço de fluxo de trabalho e com um Send/ReceiveReply par que invoca uma operação bidirecional em outro serviço Web. Ao invocar uma operação bidirecional num serviço WCF, o serviço pode ser um serviço WCF tradicional baseado em código imperativo ou um serviço de fluxo de trabalho. Para usar a correlação pedido-resposta, deve ser usada uma ligação bidirecional, como BasicHttpBinding, e as operações devem ser bidirecionais.

Se o serviço de workflow tiver operações bidirecionais em paralelo ou pares Receive/SendReply ou Send/ReceiveReply sobrepostos, então o manejo implícito de alças de correlação fornecido por WorkflowServiceHost pode não ser suficiente, especialmente em cenários de alto stress, e as mensagens podem não ser corretamente encaminhadas. Para evitar que este problema ocorra, recomendamos que especifique sempre explicitamente a CorrelationHandle ao usar a correlação pedido-resposta. Ao usar os modelos SendAndReceiveReply e ReceiveAndSendReply da secção de Mensagens da Toolbox no designer de fluxos de trabalho, um CorrelationHandle é explicitamente configurado por defeito. Ao construir um fluxo de trabalho usando código, o CorrelationHandle é especificado na CorrelationInitializers da primeira atividade do par. No exemplo seguinte, uma Receive atividade é configurada com um CorrelationHandle especificado explicitamente no RequestReplyCorrelationInitializer.

Variable<CorrelationHandle> RRHandle = new Variable<CorrelationHandle>();

Receive StartOrder = new Receive
{
    CanCreateInstance = true,
    ServiceContractName = OrderContractName,
    OperationName = "StartOrder",
    CorrelationInitializers =
    {
        new RequestReplyCorrelationInitializer
        {
            CorrelationHandle = RRHandle
        }
    }
};

SendReply ReplyToStartOrder = new SendReply
{
    Request = StartOrder,
    Content = ... // Contains the return value, if any.
};

// Construct a workflow using StartOrder and ReplyToStartOrder.

A persistência não é permitida entre um conjunto Receive/SendReply ou um conjunto Send/ReceiveReply. Uma zona de não persistência é criada e permanece até que ambas as atividades sejam concluídas. Se uma atividade, como uma atividade de atraso, estiver nesta zona de não persistência e fizer com que o fluxo de trabalho fique inativo, o fluxo de trabalho não persistirá mesmo que o anfitrião esteja configurado para persistir fluxos de trabalho quando estes ficam inativos. Se uma atividade, como uma atividade persistente, tentar persistir explicitamente na zona sem persistência, é lançada uma exceção fatal, o fluxo de trabalho aborta e um FaultException é devolvido ao chamador. A mensagem fatal de exceção é "System.InvalidOperationException: As atividades persistentes não podem ser contidas em blocos de persistência.". Esta exceção não é devolvida ao chamador, mas pode ser observada se o rastreamento estiver ativado. A mensagem devolvida ao chamador é "A operação não pôde ser realizada porque a WorkflowInstance '5836145b-7da2-49d0-a052-a49162adeab6' foi concluída". FaultException

Para mais informações sobre a correlação pedido-resposta, consulte Pedido-Resposta.

Questões Comuns de Correlação de Conteúdo

A correlação baseada no conteúdo é usada quando um serviço de fluxo de trabalho recebe múltiplas mensagens e um dado nas mensagens trocadas identifica a instância desejada. A correlação baseada no conteúdo utiliza estes dados na mensagem, como o número de cliente ou o ID da encomenda, para encaminhar mensagens para a instância correta do fluxo de trabalho. Esta secção descreve vários problemas comuns que podem ocorrer ao utilizar correlação baseada no conteúdo.

Garantir que os dados identificativos são únicos

Os dados usados para identificar a instância são transformados numa chave de correlação. Deve ter-se cuidado para garantir que os dados usados para correlação são únicos, caso contrário colisões na chave hashada podem ocorrer e causar o encaminhamento incorreto das mensagens. Por exemplo, uma correlação baseada apenas no nome de um cliente pode causar uma colisão porque pode haver vários clientes com o mesmo nome. Os dois-pontos (:) não devem ser usados como parte dos dados utilizados para correlacionar a mensagem, pois já são empregados para delimitar a chave e o valor da consulta da mensagem, formando assim a cadeia de caracteres que é subsequentemente transformada em hash. Se a persistência estiver a ser utilizada, certifique-se de que os dados identificativos atuais não foram usados por uma instância previamente persistida. Desativar temporariamente a persistência pode ajudar a identificar este problema. O rastreamento WCF pode ser usado para visualizar a chave de correlação calculada e é útil para depurar este tipo de problema.

Condições da Corrida

Existe um pequeno intervalo de tempo entre o serviço receber uma mensagem e a correlação ser efetivamente inicializada, durante o qual as mensagens de seguimento serão ignoradas. Se um serviço de workflow inicializar a correlação baseada no conteúdo usando dados passados pelo cliente numa operação unidirecional, e o interlocutor enviar mensagens de seguimento imediatas, essas mensagens serão ignoradas durante este intervalo. Isto pode ser evitado usando uma operação bidirecional para inicializar a correlação, ou usando um TransactedReceiveScope.

Questões de Consultas de Correlação

As consultas de correlação são usadas para especificar que dados numa mensagem são usados para correlacioná-la. Estes dados são especificados através de uma consulta XPath. Se as mensagens para um serviço não estiverem a ser enviadas, mesmo que tudo pareça estar correto, uma estratégia para resolução de problemas é especificar um valor literal que corresponda ao valor dos dados da mensagem em vez de uma consulta XPath. Para especificar um valor literal, use a string função. No exemplo seguinte, a MessageQuerySet foi configurada para usar um valor literal de 11445 para o OrderId e a consulta XPath foi comentada.

MessageQuerySet = new MessageQuerySet
{
    {
        "OrderId",
        //new XPathMessageQuery("sm:body()/tempuri:StartOrderResponse/tempuri:OrderId")
        new XPathMessageQuery("string('11445')")
    }
}

Se uma consulta XPath estiver configurada incorretamente de modo que não sejam recuperados dados de correlação, uma falha é devolvida com a seguinte mensagem: "Uma consulta de correlação produziu um conjunto de resultados vazio. Por favor, certifique-se de que as consultas de correlação para o endpoint estão corretamente configuradas." Uma forma rápida de resolver isto é substituir a consulta XPath por um valor literal, como descrito na secção anterior. Este problema pode ocorrer se usar a ferramenta de construção de consultas XPath nas caixas de diálogo Add Correlation Initializers ou Definição de CorrelatesOn e o seu serviço de fluxo de trabalho usar contratos de mensagem. No exemplo seguinte, é definida uma classe de contrato de mensagem.

[MessageContract]
public class AddItemMessage
{
    [MessageHeader]
    public string CartId;

    [MessageBodyMember]
    public string Item;
}

Este contrato de mensagem é utilizado por uma Receive atividade num fluxo de trabalho. O CartId no cabeçalho da mensagem é usado para correlacionar a mensagem com a instância correta. Se a consulta XPath que recupera o CartId for criada usando os diálogos de correlação no designer do workflow, é gerada a seguinte consulta XPath incorreta.

sm:body()/xg0:AddItemMessage/xg0:CartId

Esta consulta XPath estaria correta se a Receive atividade usasse parâmetros para os dados, mas como está a usar um contrato de mensagem, está incorreta. A consulta XPath seguinte é a consulta XPath correta para recuperar o CartId do cabeçalho.

sm:header()/tempuri:CartId

Isto pode ser confirmado examinando o corpo da mensagem.

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://tempuri.org/IService/AddItem</Action>
    <h:CartId xmlns:h="http://tempuri.org/">80c95b41-c98d-4660-a6c1-99412206e54c</h:CartId>
  </s:Header>
  <s:Body>
    <AddItemMessage xmlns="http://tempuri.org/">
      <Item>Books</Item>
    </AddItemMessage>
  </s:Body>
</s:Envelope>

O exemplo seguinte mostra uma Receive atividade configurada para uma AddItem operação que utiliza o contrato de mensagem anterior para receber dados. A consulta XPath está corretamente configurada.

<Receive CorrelatesWith="[CCHandle] OperationName="AddItem" ServiceContractName="p:IService">
  <Receive.CorrelatesOn>
    <XPathMessageQuery x:Key="key1">
      <XPathMessageQuery.Namespaces>
        <ssx:XPathMessageContextMarkup>
          <x:String x:Key="xg0">http://schemas.datacontract.org/2004/07/MessageContractWFService</x:String>
        </ssx:XPathMessageContextMarkup>
      </XPathMessageQuery.Namespaces>sm:header()/tempuri:CartId</XPathMessageQuery>
  </Receive.CorrelatesOn>
  <ReceiveMessageContent DeclaredMessageType="m:AddItemMessage">
    <p1:OutArgument x:TypeArguments="m:AddItemMessage">[AddItemMessage]</p1:OutArgument>
  </ReceiveMessageContent>
</Receive>