Workflow Service con AppFabric in ambiente cluster e load balance

Lo scenario più comune di servizi pensati per un alto numero di richieste è quello di avere un cluster con annesso sistema di LoadBalance che garantisce lo smistamento delle richieste al nodo meno carico.a

Nonostante si configurino i vari nodi dell’ambiente speculare, quindi stessa configurazione dei servizi, stessa configurazione di AppFabric e stessa configurazione di IIS (binding, app pool, ecc), possono sorgere problemi durante l’utilizzo di Workflow Service che contengono più coppie di activity Recive/Replay correlate tra loro.

Facciamo un semplice esempio: un Workflow Service con una prima coppia Recive/Replay di init (quindi con l’attributo CanCreateInstance a true) e poi una seconda coppia Recive/Replay contenuta in un DoWhile, la cui condizione è posta a TRUE per comodità.

image021

Workflow Service di esempio

Bene, se ora creiamo un client ed aggiungiamo un proxy al servizio tramite il classico “Add Service Reference…” di Visual Studio 2010 (ma anche attraverso svcutils) che punta al cluster, tutto funziona come ci aspetteremmo e viene generato quanto occorre per invocare le varie operation del servizio.

Qual è dunque il problema?

Bene, il problema si verifica quando il nostro client crea decine di thread per invocare Operation1 (ovvero l’activity Recive all’interno del DoWhile) in modo parallelo, ad esempio per effettuare un test di carico o funzionale.

In questo caso può capitare  (ed è giusto che sia così) che l’LB smisti la richiesta ad un nodo diverso da quello verso cui inizialmente il proxy è stato creato (passando attraverso il cluster) e il sistema ci presenta un’eccezione tutt’altro che chiara (come sempre!):

There was no channel that could accept the message with action 'http://myservice.mysite.it/Operation1'

visto che il servizio è attivo su entrambi i nodi.

La prima soluzione a cui si pensa è quella di ricreare il proxy all’interno del metodo invocato dal thread ed inglobarlo in uno using, in modo da distruggerlo appena non se ne ha più bisogno:

void MyThread(){

using(var proxy = new ServiceCliend())

{

proxy.Operation1()

}

……

}

Ma il risultato resta “stranamente” lo stesso. Il fatto è che dalla versione 3.5 di dotNet (in realtà timidamente dalla 3.0sp1), è stato adottato un meccanismo di cache most recently uderd (MRU) per il ChannelFactory (accessibile dal proxy attraverso l’omonima property), allo scopo di abbattere i tempi necessari a creare un nuovo channel.

Questo è il nostro problema! Anche se rifaccio la new del proxy, l’engine del framework mi riassocia (se piò e se è valido) lo stesso channel contenuto in cache.

Per aggirare la cosa (perché permettere di settare un attributo di cache enable, come sempre, era chiedere troppo) è convivente creare un helper, ovvero una classe statica, che ha il solo compito di creare il proxy, e prima di ritornarlo effettuare un accesso (una semplice lettura) alla proprietà ChannelFactory, il che ne inibisce la persistenza in cache.

public static ServiceClient GetProxy()

{

ServiceClient proxy = new ServiceClient ();

var prev = proxy.ChannelFactory; //lettura per disattivare la cache del proxy

return proxy;

}

 

That’s all!

Per approfondimenti:

http://msdn.microsoft.com/en-us/magazine/gg535671.aspx

http://blogs.msdn.com/b/wenlong/archive/2007/10/27/performance-improvement-of-wcf-client-proxy-creation-and-best-practices.aspx

WF4 Best Practice

Base Classes

  1. Limitare l’uso del Designer per le Activity/WF di cui si desidera ottenere una rappresentazione visiva (grafica);
  2. Utilizzare la derivazione direttamente dalla classe Activity quando si desiderano implementare concetti non direttamente supportati a livello di Designer. Ad esempio: ActivityDelegate, child activities e validation;
  3. Quando si ha la necessità di scrivere le proprie Custom Activity by code (imperative code), derivare la nuova Activity da CodeActivity se si vuole: 
    1. gestire l’esecuzione in un unico punto (metodo Execute) 
    2. non si necessità di schedulare ulteriori Activity 
    3. non si richiedono funzionalità avanzate del WF Runtime 
    4. non si richiede l’esecuzione asincrona 
  4. Quando si ha la necessità di scrivere le proprie Custom Activity by code (imperative code), derivare la nuova Activity da AsyncCodeActivity se si ha la necessità di eseguire la stessa in modo asincrono in aggiunta alle caratteristiche del punto 3; 
  5. Quando si ha la necessità di sfruttare tutte le potenzialità del WF Runtime, superando i limiti del punto 3, derivare la nuova Activity da NativeActivity; 
  6. Non derivare una Custom Activity da NativeActivity se non si ha la necessità di utilizzare le feauture avanzate del WF Runtime; 
  7. Favorire sempre il Riuso delle Activity già esistenti, creando nuove Activity frutto dell’orchestrazione di quelle esistenti (Composition); 
  8. Creare nuove Activitiy derivate da Code/NativeActivity solo se non è possibile implementarle facilmente attraverso Composition; 
  9. Utilizzare la base class Activity<TResult>/CodeActivity<TResult> (invece che Activity/CodeActivity) se l’Activity deve ritornare un valore.

Da C# a XAMLX

Interessante TIP: chi ha deciso di sviluppare il proprio workflow (WF4) direttamente da codice, può convertire rapidamente il proprio lavoro in XAMLX in modo da vederlo nel designer.

L’istruzione da usare è la seguente: XamlServices.Save(@"..\..\demo.xaml", workflow), dove workflow è definito come, ad esempio, nello spezzone di codice seguente:

var workflow = new Sequence();

workflow.Activities.Add(new WriteLine() { Text = "Hello workflow." });

workflow.Activities.Add(new Persist());

workflow.Activities.Add(new If()

{

Condition = new VisualBasicValue<bool>("System.DateTime.Now.Hour < 12"),

Then = new WriteLine() { Text = "Good morning" },

Else = new WriteLine() { Text = "Good afternoon" }

});

workflow.Activities.Add(new WriteLine()

{

Text = new VisualBasicValue<string>("\"The current time is: \" & System.DateTime.Now.ToLongTimeString()")

});

Attenzione all'approccio TopDown o BottomUp nello sviluppo

Nel caso in cui si pensi di adottare un approccio TopDown o BottomUp in maniera disinvolta nello sviluppo di soluzioni basate su WF4, è necessario ricredersi.

Infatti la suddivisione del workflow principale in sub-flow (Activity) può causare una lentezza esasperante del designer e di Visual Studio 2010 in generale.  Se l’Activity è particolarmente complessa, il sistema di sviluppo è pressoché inutilizzabile, poiché la sola apertura del workflow primario (che ingloba gli altri) impiega più di 2 minuti!

Ciò rende problematico uno sviluppo articolato costringendo ad inutili replicazioni di sequenze comuni che, altrimenti, potrebbero essere raggruppate, riutilizzate e, soprattutto, manutenute in modo più efficiente.

 

E’ da precisare che il problema si verifica per le Activity ma non per le CodeActivity o le NativeActivity.

WF4, Risorse hardware

Per utilizzare WF4 bisogna dotarsi di un hardware di ultima generazione e dotato, soprattutto, di un quantitativo di 8GB di Ram.

Anche se la quantità di Ram può sembrare eccessiva, vi assicuro che con l’aumentare della complessità del flusso, il designer si rallenta in modo snervante rendendo, praticamente, impossibile le modifiche.

Il consiglio, inoltre, è quello di suddividere i Flow troppo complessi in sub-flow (Activity e CodeActivity) e utilizzare quello principale per orchestrarne il funzionamento.

agileiot logo  ac2 logodac dac dacdac dac psmii psmii safe cal1 less certazure fundamentals
mvp reconnect

Free Joomla templates by Ltheme