Rozwiązywanie problemów z korelacją

Korelacja jest używana do powiązania komunikatów dotyczących usługi przepływu pracy między sobą oraz z właściwą instancją przepływu pracy, ale jeśli nie jest poprawnie skonfigurowana, komunikaty nie zostaną odebrane, a aplikacje nie będą działać poprawnie. Ten temat zawiera omówienie kilku metod rozwiązywania problemów z korelacją, a także zawiera listę niektórych typowych problemów, które mogą wystąpić podczas korzystania z korelacji.

Obsługuj zdarzenie UnknownMessageReceived

Zdarzenie UnknownMessageReceived występuje, gdy usługa odbiera nieznany komunikat, w tym komunikaty, których nie można skorelować z istniejącym wystąpieniem. W przypadku usług hostowanych samodzielnie to zdarzenie można obsłużyć w aplikacji hosta.

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

W przypadku usług hostowanych w sieci Web to zdarzenie może być obsługiwane przez dziedziczenie klasy z WorkflowServiceHostFactory i przesłonięcie 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;
    }
}

Ten niestandardowy element WorkflowServiceHostFactory można następnie określić w pliku svc dla usługi.

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

Po wywołaniu tej obsługi można pobrać komunikat przy użyciu Message właściwości UnknownMessageReceivedEventArgs, który będzie wyglądać podobnie do następującego komunikatu.

<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>

Analizowanie komunikatów wysłanych do UnknownMessageReceived obsługi może wskazać, dlaczego wiadomość nie skorelowała się z instancją usługi procesów roboczych.

Monitorowanie postępu przepływu pracy za pomocą śledzenia

Śledzenie umożliwia monitorowanie postępu przepływu pracy. Domyślnie rekordy śledzenia są generowane dla zdarzeń cyklu życia przepływu pracy, zdarzeń cyklu życia działań, propagacji błędów i wznowienia zakładek. Ponadto niestandardowe rekordy śledzenia mogą być emitowane przez działania niestandardowe. Podczas rozwiązywania problemów z korelacją rekordy śledzenia aktywności, rekordy wznowienia zakładki i rekordy propagacji błędów są najbardziej przydatne. Rekordy śledzenia aktywności mogą służyć do określania bieżącego postępu przepływu pracy i mogą pomóc określić, które działania obsługi komunikatów oczekują obecnie na komunikaty. Rekordy wznowienia zakładek są przydatne, ponieważ wskazują one, że komunikat został odebrany przez przepływ pracy, a rekordy propagacji błędów zapewniają rekord wszelkich błędów w przepływie pracy. Aby włączyć śledzenie, określ żądany TrackingParticipant w WorkflowExtensionsWorkflowServiceHost. W poniższym przykładzie parametr ConsoleTrackingParticipant (z przykładu Custom Tracking ) jest skonfigurowany przy użyciu domyślnego profilu śledzenia.

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

Uczestnik śledzenia, taki jak ConsoleTrackingParticipant, jest przydatny w przypadku samoobsługowych usług przepływu pracy, które mają okno konsoli. W przypadku usługi hostowanej w Sieci Web należy użyć uczestnika śledzenia, który rejestruje informacje śledzenia w magazynie trwałym, na przykład wbudowanego EtwTrackingParticipantuczestnika śledzenia lub niestandardowego uczestnika śledzenia, który rejestruje informacje w pliku.

Aby uzyskać więcej informacji na temat śledzenia i konfigurowania śledzenia dla usługi przepływu pracy hostowanej w sieci Web, zobacz Śledzenie i śledzenie przepływu pracy, Konfigurowanie śledzenia przepływu pracy oraz przykłady śledzenia [przykłady WF].

Użyj śledzenia WCF

Śledzenie WCF zapewnia śledzenie przepływu komunikatów do i z usługi przepływu pracy. Te informacje dotyczące śledzenia są przydatne podczas rozwiązywania problemów z korelacją, szczególnie w przypadku korelacji opartej na zawartości. Aby włączyć śledzenie, określ żądane odbiorniki śledzenia w system.diagnostics sekcji web.config pliku, jeśli usługa przepływu pracy jest hostowana w Sieci Web lub app.config plik, jeśli usługa przepływu pracy jest hostowana samodzielnie. Aby uwzględnić zawartość komunikatów w pliku śledzenia, określ true dla logEntireMessage w elemencie messageLogging sekcji diagnostics w system.serviceModel. W poniższym przykładzie informacje śledzenia, w tym zawartość komunikatów, są skonfigurowane do zapisywania w pliku o nazwie 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>

Aby wyświetlić informacje śledzenia zawarte w programie service.svclog, używane jest narzędzie podglądu śledzenia usługi (SvcTraceViewer.exe). Jest to szczególnie przydatne podczas rozwiązywania problemów z korelacją opartą na zawartości, ponieważ można wyświetlić zawartość komunikatu i zobaczyć dokładnie to, co jest przekazywane, oraz czy pasuje CorrelationQuery do korelacji opartej na zawartości. Aby uzyskać więcej informacji na temat śledzenia WCF, zobacz Service Trace Viewer Tool (SvcTraceViewer.exe), Configuring Tracing (Konfigurowanie śledzenia) i Using Tracing to Troubleshoot Your Application (Rozwiązywanie problemów z aplikacją przy użyciu śledzenia).

Typowe problemy z korelacją wymiany kontekstu

Niektóre typy korelacji wymagają użycia określonego typu powiązania, aby korelacja działała prawidłowo. Przykłady obejmują korelację request-reply, która wymaga powiązania dwukierunkowego, takiego jak BasicHttpBinding, i korelacja wymiany kontekstu, która wymaga powiązania opartego na kontekście, takiego jak BasicHttpContextBinding. Większość powiązań obsługuje operacje dwukierunkowe, więc nie jest to typowy problem związany z korelacją żądań i odpowiedzi, ale istnieje tylko kilka powiązań opartych na kontekście, w tym BasicHttpContextBinding, WSHttpContextBindingi NetTcpContextBinding. Jeśli jedno z tych powiązań nie zostanie użyte, początkowe wywołanie usługi przepływu pracy powiedzie się, ale kolejne wywołania zakończą się niepowodzeniem z następującym 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.

Informacje kontekstowe, które są używane do korelacji kontekstu, mogą być zwracane przez SendReplyReceive do działania, które inicjuje korelację kontekstu podczas korzystania z operacji dwukierunkowej lub mogą być określone przez obiekt wywołujący, jeśli operacja jest jednokierunkowa. Jeśli kontekst nie jest wysyłany przez obiekt wywołujący lub zwracany przez usługę przepływu pracy, wówczas po wywołaniu kolejnej operacji zostanie zwrócona ta sama FaultException opisana wcześniej.

Typowe problemy z korelacją Request-Reply

Korelacja żądań-odpowiedzi jest używana z parą Receive/SendReply do implementacji dwukierunkowej operacji w usłudze przepływu pracy oraz z parą Send/ReceiveReply wywołującą operację dwukierunkową w innej usłudze internetowej. Podczas wywoływania dwukierunkowej operacji w usłudze WCF usługa może być tradycyjną usługą WCF opartą na kodzie imperatywnej lub może być usługą przepływu pracy. Aby można było używać korelacji żądań-odpowiedzi, należy użyć powiązania dwukierunkowego, takiego jak BasicHttpBinding, a operacje muszą być dwukierunkowe.

Jeśli usługa przepływu pracy posiada równoległe operacje dwukierunkowe lub nakładające się pary Receive/SendReply lub Send/ReceiveReply, to niejawne zarządzanie uchwytami korelacji zapewniane przez WorkflowServiceHost może nie być wystarczające, zwłaszcza w scenariuszach o wysokim obciążeniu, a komunikaty mogą być niewłaściwie kierowane. Aby zapobiec wystąpieniu tego problemu, zalecamy, aby zawsze jawnie określić metodę CorrelationHandle podczas korzystania z korelacji żądań i odpowiedzi. W przypadku korzystania z szablonów SendAndReceiveReply i ReceiveAndSendReply z sekcji Komunikacja Przybornika w projektancie przepływu pracy, element CorrelationHandle jest domyślnie jawnie skonfigurowany. Podczas tworzenia przepływu pracy przy użyciu kodu komponent CorrelationHandle określany jest w CorrelationInitializers pierwszej aktywności w parze. W poniższym przykładzie Receive aktywność jest konfigurowana z jawnie CorrelationHandle określonym w pliku 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.

Trwałość nie jest dozwolona między parą Receive/SendReply lub parą Send/ReceiveReply. Zostanie utworzona strefa nietrwała, która trwa do momentu ukończenia obu działań. Jeśli działanie, takie jak działanie opóźnienia, znajduje się w strefie bez utrwalania i spowoduje, że przepływ pracy stanie się bezczynnym, przepływ pracy nie będzie się utrwalać, nawet jeśli host jest skonfigurowany do utrwalania przepływów pracy, gdy staną się bezczynne. Jeśli akcja, taka jak akcja trwała, próbuje jawnie utrwalić się w strefie bez utrwalania, zgłaszany jest wyjątek krytyczny, przepływ pracy zostaje przerwany, a wynik FaultException jest zwracany do wywołującego. Komunikat o wyjątku krytycznym to "System.InvalidOperationException: Nie można zawierać działań trwałych w żadnych blokach trwałości". Ten wyjątek nie jest zwracany wywołującemu, ale może być zaobserwowany, jeśli śledzenie jest włączone. Komunikat zwrócony do obiektu wywołującego FaultException to "Nie można wykonać operacji, ponieważ WorkflowInstance '5836145b-7da2-49d0-a052-a49162adeab6' została ukończona".

Aby uzyskać więcej informacji na temat korelacji żądań i odpowiedzi, zobacz Request-Reply.

Typowe problemy z korelacją zawartości

Korelacja oparta na zawartości jest używana, gdy usługa przepływu pracy odbiera wiele komunikatów, a część danych w wymienianych komunikatach identyfikuje żądane wystąpienie. Korelacja oparta na zawartości wykorzystuje dane zawarte w komunikacie, takie jak numer klienta lub identyfikator zamówienia, aby kierować komunikaty do właściwego wystąpienia przepływu pracy. W tej sekcji opisano kilka typowych problemów, które mogą wystąpić podczas korzystania z korelacji opartej na zawartości.

Upewnij się, że dane identyfikujące są unikatowe

Dane używane do identyfikowania wystąpienia są szyfrowane w kluczu korelacji. Należy zachować ostrożność, aby zagwarantować, że dane używane do korelacji są unikatowe lub inne kolizje w kluczu skrótu mogą wystąpić i spowodować błędne przekierowanie komunikatów. Na przykład korelacja oparta tylko na nazwie klienta może spowodować kolizję, ponieważ może istnieć wiele klientów, którzy mają taką samą nazwę. Dwukropek (:) nie powinien być używany jako część danych używanych do korelowania komunikatu, ponieważ jest już używany do oddzielenia klucza i wartości zapytania komunikatu w celu utworzenia ciągu, który następnie jest haszowany. Jeśli jest używana persistencja, zweryfikuj, że bieżące dane identyfikacyjne nie zostały wykorzystane przez wcześniej zapisane instancje. Czasowe wyłączenie persistencji może pomóc w zidentyfikowaniu tego problemu. Śledzenie WCF może służyć do wyświetlania obliczonego klucza korelacji i jest przydatne do debugowania tego rodzaju problemu.

Warunki wyścigu

Istnieje niewielka luka czasowa między odbiorem komunikatu przez usługę a faktycznym zainicjowaniem korelacji, podczas której wiadomości uzupełniające zostaną zignorowane. Jeśli usługa przepływu pracy inicjuje korelację opartą na zawartości przy użyciu danych przekazywanych z klienta za pośrednictwem operacji jednokierunkowej, a obiekt wywołujący wysyła natychmiastowe wiadomości uzupełniające, te wiadomości zostaną zignorowane w tym czasie. Można tego uniknąć za pomocą operacji dwukierunkowej w celu zainicjowania korelacji lub przy użyciu elementu TransactedReceiveScope.

Problemy z zapytaniem korelacji

Zapytania korelacji służą do określania, jakie dane w komunikacie są używane do korelowania komunikatu. Te dane są określane przy użyciu zapytania XPath. Jeśli komunikaty do usługi nie są wysyłane, mimo że wszystko wydaje się być poprawne, jedną ze strategii rozwiązywania problemów jest określenie wartości literalnej odpowiadającej danym komunikatu zamiast zapytania XPath. Aby określić wartość literału, użyj funkcji string. W poniższym przykładzie MessageQuerySet jest skonfigurowane do używania literału wartości 11445 dla OrderId, a zapytanie XPath jest oznaczone jako komentarz.

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

Jeśli zapytanie XPath jest niepoprawnie skonfigurowane tak, że żadne dane korelacji nie są pobierane, zostanie zwrócony błąd z następującym komunikatem: "Zapytanie korelacji zwróciło pusty zestaw wyników. Upewnij się, że zapytania korelacji dla punktu końcowego są poprawnie skonfigurowane. Jednym z szybkich sposobów rozwiązywania problemów jest zastąpienie zapytania XPath wartością literału zgodnie z opisem w poprzedniej sekcji. Ten problem może wystąpić, jeśli używasz konstruktora zapytań XPath w oknach dialogowych Dodawanie inicjatorów korelacji lub Definicja KorelujNa, a usługa przepływu pracy używa kontraktów komunikatów. W poniższym przykładzie zdefiniowano klasę kontraktu komunikatów.

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

    [MessageBodyMember]
    public string Item;
}

Ta umowa wiadomości Receive jest używana przez działanie w ramach przepływu pracy. Element CartId w nagłówku wiadomości służy do powiązania wiadomości z właściwym wystąpieniem. Jeśli zapytanie XPath, które pobiera obiekt CartId, zostanie utworzone przy użyciu dialogów korelacji w projektancie przepływu pracy, zostanie wygenerowane następujące nieprawidłowe zapytanie XPath.

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

To zapytanie XPath byłoby poprawne, gdyby akcja Receive używała parametrów dla danych, ale ponieważ używa kontraktu wiadomości, jest niepoprawne. Poniższe zapytanie XPath jest poprawnym zapytaniem XPath, które ma pobrać element CartId z nagłówka.

sm:header()/tempuri:CartId

Można to potwierdzić, sprawdzając treść wiadomości.

<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>

W poniższym przykładzie pokazano Receive aktywność skonfigurowaną dla operacji AddItem, która używa poprzedniego kontraktu wiadomości do odbioru danych. Zapytanie XPath jest poprawnie skonfigurowane.

<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>