Condividi tramite


Il presente articolo è stato tradotto automaticamente.

Smart Client

Creazione di applicazioni distribuite con NHibernate e Rhino Service Bus

Oren Eini

Per lungo tempo, discusso quasi esclusivamente nelle applicazioni Web. Quando viene spostata sulla creazione di un'applicazione smart client, inizialmente è stato alla perdita su come affrontare la creazione di un'applicazione. Modalità di gestione di accesso ai dati Come è possibile comunicare tra applicazioni smart client e server

Inoltre, già un investimento completo in un set di strumenti esistente ridotto drasticamente il tempo e i costi di sviluppo e desideravo veramente per poter continuare a utilizzare tali strumenti. Richiedeva più tempo per scoprire i dettagli per la soddisfazione e durante tale periodo kept pensare come molto più semplice è un'applicazione Web, ovvero se solo perché sapere come gestire tali applicazioni già.

Esistono vantaggi e svantaggi per le applicazioni smart client. Sul lato più smart client sono reattivi e promuovere l'interattività dell'utente. Anche ridurre il carico del server spostando l'elaborazione di un computer client e consentire agli utenti di lavorare anche quando sono disconnessi da sistemi back-end.

D'altra parte, sono i problemi inerenti la smart client, inclusi contesa con la velocità, la protezione e la larghezza di banda limitazioni di accesso ai dati su Internet o intranet. Si è inoltre responsabile della sincronizzazione dei dati tra sistemi front-end e back-end, distribuita di rilevamento delle modifiche e la gestione di problemi di funzionamento in un ambiente occasionalmente.

Un'applicazione smart client, come descritto in questo articolo può essere creata con Windows Presentation Foundation (WPF) o Silverlight. Poiché Silverlight espone un sottoinsieme di funzionalità WPF, le tecniche e gli approcci che illustrati qui sono applicabili a entrambi.

In questo articolo, è possibile avviare i processi di pianificazione e creazione di un'applicazione smart client utilizzando NHibernate per Rhino Service Bus e accesso ai dati per una comunicazione affidabile con il server. L'applicazione funzionerà come front-end per una libreria di percentuale in linea, che chiamato Alexandra. L'applicazione è suddivisa in due parti principali. Innanzitutto, è un server di applicazioni eseguendo una serie di servizi (in cui verranno collocati la maggior parte della logica aziendale), l'accesso al database tramite NHibernate. In secondo luogo, l'interfaccia utente del client intelligenti renderà esporre tali servizi utente semplice.

NHibernate è una struttura di mapping relazionale a oggetti (O/RM) rende come semplice lavorare con database relazionali, come nel caso di utilizzo dei dati in memoria. Rhino Service Bus è un'implementazione di bus del servizio open source basata su Microsoft .NET Framework, con particolare attenzione principalmente sulla facilità di sviluppo, distribuzione e utilizzo.

Distribuzione delle responsabilità

La prima attività di generazione della libreria di percentuale è decidere la distribuzione corretta delle responsabilità tra i sistemi front-end e back-end. Un percorso è concentrarsi principalmente l'applicazione dell'interfaccia utente in modo che la maggior parte dell'elaborazione viene eseguita sul computer client. In questo caso il back-end serve principalmente come archivio dati.

In sostanza, è sufficiente una ripetizione dell'applicazione client/server tradizionali, con il back-end funge da proxy semplice per l'archivio dati. Si tratta di una scelta valida se il sistema back-end è un repository di dati. Catalogo un Rubrica personale, ad esempio, potrebbe beneficiare tale architettura, poiché il comportamento dell'applicazione è limitato alla gestione dei dati per gli utenti con alcuna modifica dei dati sul lato server.

Per tali applicazioni, è consigliabile utilizzare WCF RIA Services o Services.If dati WCF da server back-end per esporre un'interfaccia CRUD per il mondo esterno, quindi sfruttare servizi di WCF RIA o WCF Data Services consente di ridurre drasticamente il tempo necessario per generare l'applicazione. Ma mentre entrambe le tecnologie consentono di aggiungere logica di business per l'interfaccia CRUD, qualsiasi tentativo di implementare il comportamento di applicazione significativi utilizzando questo approccio sarebbe probabilmente comportare un disordine unmaintainable, brittle.

È possibile non coprono la creazione di un'applicazione in questo articolo, ma Brad Adams è illustrato un approccio dettagliato per creare semplicemente un'applicazione utilizzando NHibernate e WCF RIA servizi sul suo blog all'indirizzo blogs.msdn.com/brada/archive/2009/08/06/business-apps-example-for-silverlight-3-rtm-and-net-ria-services-july-update-part-nhibernate.aspx .

Verrà completamente in altri casi, è possibile implementare la maggior parte del comportamento dell'applicazione sul back-end, lasciando il front end con puramente presentazione preoccupazioni. Mentre questo sembra ragionevole all'inizio, poiché si tratta di come in genere scrivere applicazioni basate su Web, significa che non è possibile accettare i vantaggi dell'esecuzione di un'applicazione reale sul lato client. Sarebbe più difficile la gestione dello stato. Essenzialmente si sta scrivendo un'applicazione Web, con tutte le complessità che questo comporta nuovamente. Non è possibile a turno l'elaborazione al computer client e non sarà in grado di gestire le interruzioni nella connettività.

Inoltre, dal punto di vista dell'utente, questo approccio implica presentare un'interfaccia utente più lenta poiché tutte le operazioni richiedono un'andata e ritorno al server.

Sono certo che essa non sorprendere che l'approccio che sto prendendo in questo esempio è un punto centrale. Ho intenzione di sfruttare al meglio le possibilità offerte da eseguire sul computer client, ma allo stesso tempo significative parti dell'applicazione eseguiti come servizi back-end, come illustrato in di Figura 1.

Figure 2 The Application's Architecture

Figura 1 di architettura dell'applicazione, il

La soluzione di esempio è composto da tre progetti che è possibile scaricare da github.com/ayende/alexandria . Alexandria.Backend è un'applicazione console che ospita il codice di back-end. Alexandria.Client contiene codice front-end e Alexandria.Messages contiene le definizioni di messaggi condivise tra di essi. Per eseguire l'esempio, è necessario eseguire Alexandria.Backend e Alexandria.Client.

Un vantaggio di ospitare il back-end di un'applicazione console è consente di simulare facilmente scenari disconnessi semplicemente arrestare l'applicazione console di back-end e avvio, in un secondo momento.

Fallacies di Distributed Computing

Con le nozioni di base dell'architettura in mano, let’s esaminare le implicazioni relative alla creazione di un'applicazione smart client. Sarà la comunicazione con il server back-end tramite una rete intranet o su Internet. Considerando il fatto che l'origine dati principale per le chiamate remote nella maggior parte delle applicazioni Web è un database o un'altra applicazione server situato nel centro dati stesso e spesso nel rack stesso, questa è una modifica bruschi con numerose implicazioni.

Connessioni Internet e Intranet interessata da problemi di velocità, limiti di larghezza di banda e protezione. La grande differenza dei costi di comunicazione dettare una struttura di comunicazione diversi da quello è necessario adottare se tutti i componenti principali dell'applicazione sono stati presenti nel centro dati stesso.

Tra i principali ostacoli che è necessario affrontare in applicazioni distribuite sono fallacies dell'elaborazione distribuita. Si tratta di una serie di ipotesi che gli sviluppatori in genere durante la creazione di applicazioni distribuite che in definitiva dimostrare false. Basandosi su questi presupposti false in genere produce funzionalità ridotte o un costo molto elevato per riprogettare e ricostruire il sistema. Esistono otto fallacies:

  • La rete è affidabile.
  • La latenza è zero.
  • Larghezza di banda è infinito.
  • La rete è protetta.
  • Topologia non cambia.
  • È presente un amministratore.
  • Il costo di trasporto è zero.
  • La rete è omogenea.

Le applicazioni distribuite non considerare questi fallacies verrà eseguito nel server di problemi. Deve affrontare head tali problemi in un'applicazione smart client. L'utilizzo della cache è un argomento di grande importanza in tali circostanze. Anche se non si desidera lavorare in modalità disconnessa, quasi sempre una cache è utile per aumentare la velocità di risposta dell'applicazione.

Un altro aspetto da considerare è il modello di comunicazione per l'applicazione. Può sembrare che il modello più semplice è un proxy di servizio standard che consente di eseguire chiamate di procedura remota (RPC), ma si tende a causare problemi la strada verso il basso. Genera codice più complesso per gestire uno stato disconnesso e richiede di gestire esplicitamente chiamate asincrone, se si desidera evitare il blocco nel thread UI.

Nozioni fondamentali di back-end

Successivamente, è presente un problema di come strutturare il back end dell'applicazione in modo che sia buone prestazioni e un grado di separazione dal modo in cui che l'interfaccia utente è strutturato.

Lo scenario ideale dal punto di vista delle prestazioni e tempi di risposta è rendere una singola chiamata al back-end per ottenere tutti i dati che necessari per la schermata presentata. Il problema verrà questa route è che si ottiene con un'interfaccia di servizio che rispecchia esattamente l'interfaccia utente del client intelligenti. È errato per un intero host dei motivi. Soprattutto, l'interfaccia utente è la parte più modificabile in un'applicazione. Collegare l'interfaccia di servizio all'interfaccia utente in questo modo viene modifiche frequenti al servizio, gestiti esclusivamente le modifiche dell'interfaccia utente.

Che, a sua volta, pertanto la distribuzione dell'applicazione appena ricevuto molto più difficile. È necessario distribuire contemporaneamente il front end e back end e tenta di supportare più versioni contemporaneamente è potrebbero causare la maggiore complessità. Inoltre, l'interfaccia del servizio non utilizzabile per creare interfacce utente aggiuntive come un'integrazione di terze parti o altri servizi.

Se si tenta di passare il ciclo di lavorazione, ovvero la creazione di un'interfaccia standard e specifica, head potrà eseguire su in fallacies (un'interfaccia capillare comporta un elevato numero di chiamate remote, causando problemi di latenza, affidabilità e larghezza di banda).

La risposta a questa sfida è interrotta dal comune modello RPC. Invece di esporre metodi da chiamare in remoto, let’s utilizzare una cache locale e un modello di comunicazione basata su messaggi.

Figura 2 viene illustrato come comprimere più richieste dal front end di back end. Ciò consente di creare una singola chiamata remota, ma conservare un modello di programmazione sul lato server non è strettamente collegato alle esigenze dell'interfaccia utente.

Figure 2 A Single Request to the Server Contains Several Messages

Figura 2 una singola richiesta al server contiene più messaggi

Per aumentare la velocità di risposta, è possibile includere una cache locale può rispondere immediatamente, alcune query iniziale per un'applicazione più reattiva.

Uno degli aspetti che è necessario considerare in questi scenari sia tipi di dati che è necessario e i requisiti di validità per i dati visualizzati. Nell'applicazione di Alexandria inclina pesantemente sulla cache locale perché è accettabile per mostrare i dati utente memorizzati nella cache durante l'applicazione richiede dati aggiornati dal sistema back-end. Altre applicazioni, stock commerciali, ad esempio, dovrebbe probabilmente visualizzare niente affatto anziché dati obsoleti.

Operazioni disconnesse

Il problema successivo a faccia è la Gestione scenari disconnessi. In molte applicazioni, è possibile specificare che una connessione è obbligatoria, ovvero che è possibile semplicemente visualizzare all'utente un errore se il server back-end non sono disponibili. Uno dei vantaggi di un'applicazione smart client è che il lavoro possibile in modalità disconnessa e l'applicazione di Alexandria si avvale di che.

Tuttavia, ciò significa che la cache diventa ancora più importante perché viene utilizzato sia per velocità di comunicazione e di fornire i dati dalla cache se il sistema back-end non è raggiungibile.

A questo punto, credo che avere una buona conoscenza delle problematiche coinvolte nella creazione di un'applicazione let’s spostare per informazioni su come risolvere tali problemi.

Le code sono una delle attività personali Preferiti

In Alexandria, non vi è alcuna comunicazione RPC tra il front-end e back-end. Invece, come illustrato in di Figura 3, tutte le comunicazioni viene gestita tramite messaggi unidirezionali attraverso le code.

Figure 3 The Alexandria Communication Model

Figura 3 del modello di comunicazione Alexandria

Code consentono di risolvere i problemi di comunicazione precedentemente identificati in modo piuttosto elegante. Anziché comunicare direttamente tra il front end e back end (ovvero supportano scenari disconnessi è duro ), sarà possibile lasciare il sottosistema Accodamento gestire tutto ciò.

Utilizzo delle code è piuttosto semplice. Richiesto il sottosistema Accodamento locale per inviare un messaggio alla coda di alcuni. Il sottosistema Accodamento assume la proprietà del messaggio e assicura che giunge a destinazione in un determinato momento. Tuttavia, l'applicazione non attesa il messaggio raggiungere la destinazione e può eseguire sul proprio lavoro.

Se la coda di destinazione non è disponibile, il sottosistema Accodamento verrà attendere fino a quando la coda di destinazione è nuovamente disponibile, quindi recapitare il messaggio. Il sottosistema Accodamento permanente in genere il messaggio sul disco finché non consegnato, in modo che i messaggi in sospeso verrà ancora arrivano a destinazione anche se il computer di origine è stato riavviato.

Quando si utilizzano code, è facile pensare in termini di messaggi e destinazioni. Un messaggio arriva a un sistema back-end attiverà un'azione, quindi può comportare una risposta inviata al mittente originale. Si noti che nessun blocco su entrambe le estremità, poiché ogni sistema è completamente indipendente.

Accodamento dei sottosistemi includono MSMQ, ActiveMQ, RabbitMQ e altri. L'Alexandria applicazione utilizza Rhino code ( github.com/rhino-queues/rhino-queues ), un open source, sottosistema Accodamento distribuzione xcopy. Ho scelto Rhino code per il semplice motivo che non richiede alcuna installazione o amministrazione, ideale per l'utilizzo negli esempi e nelle applicazioni da distribuire su più computer. Vale inoltre la pena notare che ho scritto Rhino code. Mi auguro che come tale.

Inserimento di code di lavoro

Vedere Let’s come gestire il recupero di dati per la schermata principale utilizzando le code. Ecco la routine di inizializzazione ApplicationModel:

protected override void OnInitialize() {
  bus.Send(
    new MyBooksQuery { UserId = userId },
    new MyQueueQuery { UserId = userId },
    new MyRecommendationsQuery { UserId = userId },
    new SubscriptionDetailsQuery { UserId = userId });
}

Sta inviando al server un batch di messaggi richiede diversi tipi di informazioni. Esistono numerose operazioni da notare qui. La granularità dei messaggi inviati è elevata. Anziché inviare un unico messaggio generale, ad esempio MainWindowQuery, inviati numerosi messaggi, (MyBooksQuery, MyQueueQuery e così via), ciascuno per un'informazione molto specifica. Come descritto in precedenza, in questo modo è possibile trarre vantaggio dalla inviare più messaggi in un singolo batch (riduzione delle comunicazioni di rete) e riducendo la correlazione tra il front-end e back-end.

RPC è contrario Enemy

Uno degli errori più comuni nella creazione di un'applicazione distribuita viene ignorata l'aspetto di distribuzione dell'applicazione. WCF, ad esempio, consente di ignorare il fatto che si desidera apportare un metodo chiamato in rete. Che è un modello di programmazione molto semplice, pertanto che è necessario prestare particolare attenzione non violare una fallacies dell'elaborazione distribuita.

In effetti, è il fatto parte del modello di programmazione offerti dal Framework come WCF è pertanto simile a quello utilizzato per chiamare i metodi sul computer locale che conduce di tali ipotesi false.

Un'API RPC standard implica il blocco quando si effettua una chiamata attraverso la rete, il costo superiore per ogni chiamata di metodo remota e la possibilità di errore se il server back-end non è disponibile. È certamente possibile creare una valida applicazione distribuita su questa base, ma richiede maggiore attenzione.

Un altro approccio conduce a un modello di programmazione basato su scambi di messaggi espliciti (in contrapposizione a di scambi di messaggi implicita comuni in più stack di RPC basato su SOAP). Tale modello potrebbe apparire strani all'inizio e richiede di spostare un po' di pensare, ma pare che effettuando il turno notevolmente ridurre la quantità di complessità preoccuparsi complessiva.

Esempio Alexandria applicazione si basa su una piattaforma di messaggistica unidirezionale e semplifica l'uso completo di questa piattaforma in modo che l'applicazione è a conoscenza del fatto viene distribuito ed effettivamente sfrutta che.

Si noti che tutti i messaggi terminano con il termine query. Si tratta di una semplice convenzione che consente di contrassegnare i messaggi di query pure non modificare lo stato e prevedono una sorta di risposta.

Si noti infine che non sembrano di ricevere qualsiasi tipo di risposta dal server. Poiché sto utilizzando le code, la modalità di comunicazione è incendio e dimentichi. Generato un messaggio (o un batch di messaggi) ora e affrontare le risposte in una fase successiva.

Prima di esaminare come front end riguarda le risposte, vedere let’s come i messaggi che appena processi back-end inviati. Figura 4 Mostra come server back-end utilizza una query per i libri. E, per la prima volta, è possibile visualizzare come si utilizza NHibernate e Rhino Service Bus.

Figura 4 utilizzo di una query sul sistema back-end

public class MyBooksQueryConsumer : 
  ConsumerOf<MyBooksQuery> {

  private readonly ISession session;
  private readonly IServiceBus bus;

  public MyBooksQueryConsumer(
    ISession session, IServiceBus bus) {

    this.session = session;
    this.bus = bus;
  }

  public void Consume(MyBooksQuery message) {
    var user = session.Get<User>(message.UserId);
    
    Console.WriteLine("{0}'s has {1} books at home", 
      user.Name, user.CurrentlyReading.Count);

    bus.Reply(new MyBooksResponse {
      UserId = message.UserId,
      Timestamp = DateTime.Now,
      Books = user.CurrentlyReading.ToBookDtoArray()
    });
  }
}

Ma prima di entrare nel codice effettivo che gestisce il messaggio in merito, let’s discutere la struttura in cui viene eseguito il codice.

È tutto sui messaggi

Rhino Service Bus (hibernatingrhinos.com/open-source/rhino-service-bus ) è unsurprisingly, un'implementazione del servizio bus. È una struttura di comunicazione basata su uno scambio di messaggi in coda unidirezionale, ampiamente ispirato da NServiceBus (nservicebus.com ).

Un messaggio inviato sul bus arriveranno alla coda di destinazione in cui verrà richiamato un consumer di messaggi. Tale messaggio in di Figura 4 è MyBooksQueryConsumer. Un consumer di messaggi è una classe che implementa ConsumerOf <TMsg> e viene richiamato il metodo utilizza con l'istanza di messaggio appropriato per gestire il messaggio.

È probabilmente possibile surmise dal costruttore MyBooksQueryConsumer che si sta utilizzando un contenitore di inversione del controllo (IoC) per fornire le dipendenze per il consumer di messaggi. Nel caso di MyBooksQueryConsumer, tali dipendenze sono bus stesso e la sessione NHibernate.

Il codice effettivo per l'utilizzo del messaggio è semplice. Ottenere l'utente dalla sessione NHibernate e inviare una risposta al mittente del messaggio con i dati richiesti.

Il front-end è anche un consumer di messaggi. Il consumer non MyBooksResponse:

public class MyBooksResponseConsumer : 
  ConsumerOf<MyBooksResponse> {

  private readonly ApplicationModel applicationModel;

  public MyBooksResponseConsumer(
    ApplicationModel applicationModel) {
    this.applicationModel = applicationModel;
  }

  public void Consume(MyBooksResponse message) {
    applicationModel.MyBooks.UpdateFrom(message.Books);
  }
}

Il modello di applicazione semplicemente aggiornati con i dati del messaggio. Occorre notare una cosa però: il metodo utilizza è chiamato nel thread UI. Viene invece chiamato su un thread in background. Il modello di applicazione è associato all'interfaccia utente, tuttavia così aggiornandolo deve avvengano nel thread UI. Il metodo UpdateFrom è a conoscenza che e passerà al thread UI per aggiornare il modello di applicazione nel thread corretto.

Il codice per gestire i messaggi sul server back-end e front-end è simile. Questa comunicazione è puramente asincrona. In nessun momento aspettare una risposta dal server back-end e non si utilizzano API asincrona di .NET Framework. Invece, è necessario uno scambio di messaggi espliciti, che in genere accade quasi istantaneamente, ma anche possibile estendere un periodo più lungo se si sta lavorando in modalità disconnessa.

Precedenti, quando si invia le query per il back end, detto semplicemente il bus per inviare i messaggi ma non dico dove inviare loro. Di Figura 4, denominato solo risposta nuovamente non specifica in cui deve essere inviato il messaggio. Come il bus sapere dove inviare i messaggi?

In caso di invio di messaggi per il server back-end, la risposta è: configurazione. In App.config, troverete la seguente configurazione:

<messages>
  <add name="Alexandria.Messages"
    endpoint="rhino.queues://localhost:51231/alexandria_backend"/>
</messages>

In questo modo il bus che tutti i messaggi il cui spazio dei nomi inizia con Alexandria.Messages devono essere inviati all'endpoint alexandria_backend.

Nella gestione dei messaggi di sistema back-end, la chiamata di risposta significa semplicemente inviare il messaggio alla relativa origine.

Questa configurazione consente di specificare la proprietà di un messaggio, a cui inviare il messaggio quando viene posizionato sul bus e dove inviare richiedere una sottoscrizione in modo che potrà essere inclusi nella lista di distribuzione quando vengono pubblicati i messaggi di questo tipo. Non si sta utilizzando pubblicazione messaggi nell'applicazione Alexandria, in modo che non riguardano che.

Gestione delle sessioni

Si è visto come meccanismo di comunicazione funziona correttamente, ma esistono problemi di infrastruttura all'indirizzo prima di procedere. Come in qualsiasi applicazione NHibernate occorre alcune modalità di gestione del ciclo di vita della sessione e la gestione delle transazioni correttamente.

L'approccio standard per le applicazioni Web consiste nel creare una sessione per ogni richiesta, in modo che ogni richiesta ha una propria sessione. Per un'applicazione di messaggistica, il comportamento è quasi identico. Anziché una sessione per ogni richiesta, si dispone di una sessione per ogni batch di messaggi.

Ho scoperto che questo sia gestito quasi completamente dall'infrastruttura. Figura 5 viene illustrato il codice di inizializzazione per il sistema back-end.

Figura 5 inizializzazione sessioni di messaggistica

public class AlexandriaBootStrapper : 
  AbstractBootStrapper {

  public AlexandriaBootStrapper() {
    NHibernateProfiler.Initialize();
  }

  protected override void ConfigureContainer() {
    var cfg = new Configuration()
      .Configure("nhibernate.config");
    var sessionFactory = cfg.BuildSessionFactory();

    container.Kernel.AddFacility(
      "factory", new FactorySupportFacility());

    container.Register(
      Component.For<ISessionFactory>()
        .Instance(sessionFactory),
      Component.For<IMessageModule>()
        .ImplementedBy<NHibernateMessageModule>(),
      Component.For<ISession>()
        .UsingFactoryMethod(() => 
          NHibernateMessageModule.CurrentSession)
        .LifeStyle.Is(LifestyleType.Transient));

    base.ConfigureContainer();
  }
}

Avvio automatico è un concetto esplicito in Rhino Service Bus, implementate dalle classi che derivano da AbstractBootStrapper. Il programma di avvio automatico ha lo stesso processo di file Global.asax in una tipica applicazione Web. Figura 5 innanzitutto necessario generare i NHibernate factory di sessione, quindi impostare contenitore (castello Windsor) per fornire la sessione NHibernate dalla NHibenrateMessageModule.

Un modulo di messaggio ha lo stesso scopo di un modulo HTTP in un'applicazione Web: Per gestire problemi trasversale tra tutte le richieste. Utilizzo di NHibernateMessageModule per gestire la durata delle sessioni, come illustrato in di Figura 6.

Figura 6 Gestione sessione durata

public class NHibernateMessageModule : IMessageModule {
  private readonly ISessionFactory sessionFactory;
  [ThreadStatic]
  private static ISession currentSession;

  public static ISession CurrentSession {
    get { return currentSession; }
  }

  public NHibernateMessageModule(
    ISessionFactory sessionFactory) {

    this.sessionFactory = sessionFactory;
  }

  public void Init(ITransport transport, 
    IServiceBus serviceBus) {

    transport.MessageArrived += TransportOnMessageArrived;
    transport.MessageProcessingCompleted 
      += TransportOnMessageProcessingCompleted;
  }

  private static void 
    TransportOnMessageProcessingCompleted(
    CurrentMessageInformation currentMessageInformation, 
    Exception exception) {

    if (currentSession != null)
        currentSession.Dispose();
    currentSession = null;
  }

  private bool TransportOnMessageArrived(
    CurrentMessageInformation currentMessageInformation) {

    if (currentSession == null)
        currentSession = sessionFactory.OpenSession();
    return false;
  }
}

Il codice è piuttosto semplice: registrare gli eventi appropriati, creare ed eliminare la sessione in appropriato luoghi e sarà terminato.

Un'interessante implicazione di questo approccio è che tutti i messaggi in un batch condivideranno la stessa sessione, significa che in molti casi è possibile usufruire della cache di primo livello del NHibernate.

Gestione delle transazioni

Questo è tutto per la gestione della sessione, ma cosa sulle transazioni?

Consigliata per NHibernate è che tutte le interazioni con il database devono essere gestite tramite le transazioni. Ma non si utilizza transazioni del NHibernate qui. Perché?

La risposta è che le transazioni vengono gestite da Rhino Service Bus. Anziché rendere ogni consumer a gestire le proprie transazioni, Rhino Service Bus adotta un approccio diverso. Esso viene utilizzato System.Transactions.TransactionScope per creare un'unica transazione che comprende tutti i consumer di messaggi nel batch.

Ciò significa che tutte le azioni eseguite in risposta a un messaggio batch di (as opposed to a single message) fanno parte della stessa transazione. NHibernate verrà automaticamente integrare una sessione nella transazione di ambiente, in modo che quando si utilizza Rhino Service Bus non è necessario affrontare in modo esplicito le transazioni.

La combinazione di una singola sessione e una singola transazione consente di combinare più operazioni in una singola unità transazionale. Inoltre, che è possibile sfruttare direttamente dalla cache di primo livello del NHibernate. Ad esempio, ecco il codice appropriato per gestire MyQueueQuery:

public void Consume(MyQueueQuery message) {
  var user = session.Get<User>(message.UserId);

  Console.WriteLine("{0}'s has {1} books queued for reading",
    user.Name, user.Queue.Count);

  bus.Reply(new MyQueueResponse {
    UserId = message.UserId,
    Timestamp = DateTime.Now,
    Queue = user.Queue.ToBookDtoArray()
  });
}

Il codice per la gestione di un MyQueueQuery e MyBooksQuery è quasi identico. Cos'è dunque, implicazioni sulle prestazioni di una singola transazione per ogni sessione per il codice seguente?

bus.Send(
  new MyBooksQuery {
    UserId = userId
  },
  new MyQueueQuery {
    UserId = userId
  });

A prima vista sembra sarebbero quattro query per raccogliere tutte le informazioni richieste. In MyBookQuery una query per ottenere l'utente appropriato e un altro per caricare manuali dell'utente. Lo stesso sembra essere il caso di MyQueueQuery: una query per l'utente e un'altra per caricare la coda dell'utente.

L'utilizzo di una singola sessione per intero batch, tuttavia, indica che si sta utilizzando effettivamente la cache di primo livello per evitare inutili query, come si vede nell'output di NHibernate Profiler (nhprof.com ) di Figura 7.

image: The NHibnerate Profiler View of Processing Requests
Figura 7 di Visualizza il profiler NHibnerate di elaborazione delle richieste

Supporto occasionally connected scenari

Come sta, l'applicazione non genera un errore se non è possibile raggiungere il server back-end, ma non sarebbe molto utile sia.

La fase successiva nell'evoluzione di questa applicazione è talvolta trasformare in un reale connessione client introducendo una cache che consente all'applicazione di continuare a funzionare anche se il server back-end non risponde. Tuttavia, non utilizzare l'architettura di caching tradizionale in cui il codice dell'applicazione effettua chiamate esplicite nella cache. Invece, applicherà la cache a livello di infrastruttura.

Figura 8 è illustrata la sequenza di operazioni quando la cache viene implementata come parte dell'infrastruttura di messaggistica quando si invia un messaggio singolo richiede dati sui libri dell'utente.

image: Using the Cache in Concurrent Messaging Operations

Figura 8 utilizzo della cache in simultanea delle operazioni di messaggistica

Il client invia un messaggio MyBooksQuery. Il messaggio viene inviato al bus while, allo stesso tempo, la cache viene eseguita una query per verificare se dispone di risposta di per questa richiesta. Se la cache contiene la risposta per la richiesta precedente, la cache provoca immediatamente il messaggio memorizzato nella cache essere utilizzati come se appena arrivato sul bus.

All'arrivo della risposta dal sistema back-end. Il messaggio viene consumato normalmente e viene inoltre inserito nella cache. Nell'area di questo approccio sembra complicato, ma risulta efficace comportamento di memorizzazione nella cache e consente di ignorare completamente caching problemi nel codice dell'applicazione. Cache persistente (uno survives riavvii dell'applicazione), è possibile utilizzare l'applicazione in modo completamente indipendente senza richiedere alcun dato dal server back-end.

Ora let’s implementare questa funzionalità. Presuppone una cache persistente (il codice di esempio fornisce un'implementazione semplice che utilizza la serializzazione binaria per salvare i valori su disco) e definire le seguenti convenzioni:

  • Un messaggio può memorizzare nella cache se è parte di uno scambio di messaggi di richiesta/risposta.
  • Messaggi di richiesta e di risposta contengono la chiave di cache per lo scambio di messaggi.

Lo scambio di messaggi è definito da un'interfaccia ICacheableQuery con una singola proprietà di chiave e un'interfaccia ICacheableResponse con proprietà Key e Timestamp.

Per implementare questa convenzione, è possibile scrivere un CachingMessageModule eseguirà il front end intercettano i messaggi in ingresso e in uscita. Figura 9 Mostra i messaggi in arrivo come vengono gestiti.

Figura 9 di connessioni in ingresso nella cache

private bool TransportOnMessageArrived(
  CurrentMessageInformation
  currentMessageInformation) {

  var cachableResponse = 
    currentMessageInformation.Message as 
    ICacheableResponse;
  if (cachableResponse == null)
    return false;

  var alreadyInCache = cache.Get(cachableResponse.Key);
  if (alreadyInCache == null || 
    alreadyInCache.Timestamp < 
    cachableResponse.Timestamp) {

    cache.Put(cachableResponse.Key, 
      cachableResponse.Timestamp, cachableResponse);
  }
  return false;
}

Non c'è molto succede, se il messaggio è una risposta inseribile nella cache, è possibile inserire nella cache. Ma c'è una cosa da notare: È possibile gestire il caso di messaggi-ordine, i messaggi con un timestamp precedente che arriva dopo i messaggi con timestamp successivo. Ciò garantisce che solo le informazioni più recenti sono memorizzate nella cache.

Gestione dei messaggi in uscita e invio dei messaggi dalla cache è più interessante, come si può vedere in di Figura 10.

Figura 10 esecuzione messaggi

private void TransportOnMessageSent(
  CurrentMessageInformation 
  currentMessageInformation) {

  var cacheableQuerys = 
    currentMessageInformation.AllMessages.OfType<
    ICacheableQuery>();
  var responses =
    from msg in cacheableQuerys
    let response = cache.Get(msg.Key)
    where response != null
    select response.Value;

  var array = responses.ToArray();
  if (array.Length == 0)
    return;
  bus.ConsumeMessages(array);
}

È possibile raccogliere le risposte nella cache dalla cache e chiamare ConsumeMessages su di essi. Che provoca il bus richiamare la logica di chiamate normali messaggi, sembra che il messaggio è arrivato nuovamente.

Si noti che anche se è presente una risposta memorizzata nella cache, è comunque inviare il messaggio. Il ragionamento è che è possibile fornire all'utente una risposta rapida (nella cache) e aggiornare le informazioni visualizzate all'utente quando il back-end risponde ai nuovi messaggi.

Operazioni successive

Ho trattato i blocchi predefiniti di base di un'applicazione smart client: come strutturare il back-end e la modalità di comunicazione tra applicazioni smart client e il server back-end. Il secondo è importante quanto la scelta della modalità di comunicazione errata può causare fallacies dell'elaborazione distribuita. Toccato anche in modalità batch e la memorizzazione nella cache, due approcci molto importanti per migliorare le prestazioni di un'applicazione smart client.

Sui server back-end si è visto come gestire le transazioni e la sessione NHibernate, come utilizzare e rispondere ai messaggi dal client e come tutto ciò che viene fornito assieme nel programma di avvio automatico.

In questo articolo concentra principalmente sui problemi dell'infrastruttura, nella prossima puntata si parlerà consigliate per l'invio di dati tra server back-end e smart client dell'applicazione e i modelli per la gestione delle modifiche distribuite.

Oren Eini (Chi opera sotto la pseudonym ayende Rahien) è un membro attivo di diversi progetti open source (NHibernate e castello tra di essi) ed è il fondatore di molti altri (Rhino Mocks, NHibernate Query Analyzer e Commons Rhino tra di essi). Eini è inoltre responsabile del profiler NHibernate (nhprof.com), un debugger visual per NHibernate. È possibile seguire il lavoro del Eini ayende.com/blog .