2012-05-06 8 views
16

Per testare i numerosi problemi dell'implementazione di IIS/WCF da zero, ho creato il servizio HelloWorld e il client ha attraversato (molto piacevolmente) here. Ho aggiunto endpoint per net.tcp e il servizio funziona correttamente end-to-end per entrambi i binding in IIS 7.5 (su Windows 7) nel proprio ApplicationPool chiamato HW.Funzioni AutoStart/Pre-warm non funzionanti con IIS 7.5/servizio WCF

Quello che sto cercando di ottenere è la funzionalità AutoStart e Preload (o "pre-warm caching") annunciate. Ho seguito le istruzioni presentate here e here (abbastanza simili tra loro, ma sempre bene avere una seconda opinione) molto da vicino. Il che significa che

1) Impostare il pool di applicazioni startMode ...

<applicationPools> 
    <!-- ... --> 
    <add name="HW" managedRuntimeVersion="v4.0" startMode="AlwaysRunning" /> 
</applicationPools> 

2) ... abilitato serviceAutoStart e impostare un puntatore alla mia serviceAutoStartProvider

<site name="HW" id="2"> 
    <application path="/" applicationPool="HW" serviceAutoStartEnabled="true" serviceAutoStartProvider="PreWarmMyCache" /> 
    <!-- ... --> 
</site> 

3) ... e chiamato il provider indicato, con il GetType().AssemblyQualifiedName della classe elencata nella sua interezza sotto

<serviceAutoStartProviders> 
    <add name="PreWarmMyCache" type="MyWCFServices.Preloader, HelloWorldServer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> 
</serviceAutoStartProviders> 

using System; 

namespace MyWCFServices 
{ 
    public class Preloader : System.Web.Hosting.IProcessHostPreloadClient 
    { 
     public void Preload(string[] parameters) 
     { 
      System.IO.StreamWriter sw = new System.IO.StreamWriter(@"C:\temp\PreloadTest.txt"); 
      sw.WriteLine("Preload executed {0:G}", DateTime.Now); 
      sw.Close(); 
     } 
    } 
} 

Purtroppo, tutto questo la configurazione manuale, oltre a un paio di iisreset le chiamate, e non ottengo niente. No processo w3wp.exe sparando in Task Manager (anche se ottengo se lancio il HelloWorldClient), nessun file di testo e, soprattutto, nessuna soddisfazione.

C'è una quantità frustrante di discussione su questa funzione, sia su SO o sul web più ampio, e le poche domande simili qui hanno poca attenzione, tutte suonano un allarme o due. Forse, inutilmente, tutti gli esperti là fuori che sono stati su questa stessa strada un momento o due si preoccupano di cantare? (Felice di offrire l'intera soluzione, se si può suggerire un buon posto per ospitarlo.)


EDIT: Ho provato a ripristinare quel percorso nel metodo Preload alla cartella relativa App_Data (un altro SO risposta suggerita quello), non importava. Inoltre, ho appreso che il processo w3wp.exe si attiva in una semplice ricerca sull'host locale. Il processo consuma ben 17 MB di memoria per servire il suo unico piccolo OperationContract, mentre per il prezzo offre zero valore di precarico. 17 MB di ColdDeadCache.

+0

sono ci sono indizi nel registro eventi? Qualsiasi eccezione generata dovrebbe apparire lì. – Addys

+0

No, niente. Non sono sicuro del motivo per cui ti aspetteresti un'eccezione, se (come già detto) il servizio funziona correttamente. – downwitch

+0

Alcune cose che puoi controllare: - L'ID del tuo sito 2? - È corretto che il nome del tuo sito e del pool di applicazioni siano gli stessi? - Hai specificato più attributi rispetto agli esempi, solo specificando quelli negli esempi fai la differenza? –

risposta

4

Si tratta di un approccio leggermente diverso per il vostro problema:

  1. Usa Windows Server AppFabric per il servizio di auto-start
  2. infrastrutture
  3. Usa WCF per eseguire codice personalizzato di avvio

Re 1: Il Appfabric AutoStart feature dovrebbe basta lavorare fuori dalla scatola (a condizione che tu non stia utilizzando ServiceRoute di MVC per registrare i tuoi servizi, DEVONO essere specificati nella sezione serviceActivations di Web.config o utilizzando lo standard file.

Re 2: Per iniettare codice di avvio personalizzato nella pipeline WCF si potrebbe usare un attributo come questo:

using System; 
using System.ServiceModel; 
using System.ServiceModel.Description; 

namespace WCF.Extensions 
{ 
    /// <summary> 
    /// Allows to specify a static activation method to be called one the ServiceHost for this service has been opened. 
    /// </summary> 
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] 
    public class ServiceActivatorAttribute : Attribute, IServiceBehavior 
    { 
     /// <summary> 
     /// Initializes a new instance of the ServiceActivatorAttribute class. 
     /// </summary> 
     public ServiceActivatorAttribute(Type activatorType, string methodToCall) 
     { 
      if (activatorType == null) throw new ArgumentNullException("activatorType"); 
      if (String.IsNullOrEmpty(methodToCall)) throw new ArgumentNullException("methodToCall"); 

      ActivatorType = activatorType; 
      MethodToCall = methodToCall; 
     } 

     /// <summary> 
     /// The class containing the activation method. 
     /// </summary> 
     public Type ActivatorType { get; private set; } 

     /// <summary> 
     /// The name of the activation method. Must be 'public static void' and with no parameters. 
     /// </summary> 
     public string MethodToCall { get; private set; } 


     private System.Reflection.MethodInfo activationMethod; 

     #region IServiceBehavior 
     void IServiceBehavior.AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) 
     { 
     } 

     void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) 
     { 
      serviceHostBase.Opened += (sender, e) => 
       { 
        this.activationMethod.Invoke(null, null); 
       }; 
     } 

     void IServiceBehavior.Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) 
     { 
      // Validation: can get method 
      var method = ActivatorType.GetMethod(name: MethodToCall, 
          bindingAttr: System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public, 
          callConvention: System.Reflection.CallingConventions.Standard, 
          types: Type.EmptyTypes, 
          binder: null, 
          modifiers: null); 
      if (method == null) 
       throw new ServiceActivationException("The specified activation method does not exist or does not have a valid signature (must be public static)."); 

      this.activationMethod = method; 
     } 
     #endregion 
    } 
} 

..che può essere utilizzato in questo modo:

public static class ServiceActivation 
{ 
    public static void OnServiceActivated() 
    { 
     // Your startup code here 
    } 
} 

[ServiceActivator(typeof(ServiceActivation), "OnServiceActivated")] 
public class YourService : IYourServiceContract 
{ 

} 

Questo è l'esatto approccio che usiamo da un bel po 'e su un gran numero di servizi. Il vantaggio extra dell'utilizzo di un WCF ServiceBehavior per il codice di avvio personalizzato (anziché fare affidamento sull'infrastruttura IIS) è che funziona in qualsiasi ambiente di hosting (incluso l'hosting automatico) e può essere più facilmente testato.

+0

Divertente, abbiamo già guidato la direzione AppFabric, quindi questa è una bella coda di rondine. Adoro il tuo codice, anche non testato, e può vedere dove vuole andare. Bounty è tuo, ma devi tenere il passo se ho più domande nei commenti;) – downwitch

+0

@ServiceGuy Volevo solo chiarire che i tuoi due punti dipendono, (io uso AppFabric ** AND ** aggiungi un hook all'infrastruttura WCF) . In altre parole, il punto 2 non risolve il problema nel contesto di hosting di AutoStart IIS. È corretto? – pamphlet

+0

@pamphlet: Sì, 1 e 2 sarebbero necessari, (1) per avviare realmente il servizio, (2) per eseguire la logica OnStart personalizzata. – mthierba

1

Ho fatto lo stesso. funziona ...

Nel metodo di precaricamento ho un po 'di codice copiato da un bel white paper disponibile here!

metodo di precaricamento sembra ...

public void Preload(string[] parameters) 
{  
     bool isServceActivated = false; 
     int attempts = 0; 
     while (!isServceActivated && (attempts <10)) 
     { 
      Thread.Sleep(1 * 1000); 
      try 
      { 
       string virtualPath = "/Test1/Service1.svc"; 
       ServiceHostingEnvironment.EnsureServiceAvailable(virtualPath); 
       isServceActivated = true; 
      } 
      catch (Exception exception) 
      { 
       attempts++; 
       //continue on these exceptions, otherwise fail fast 
       if (exception is EndpointNotFoundException || 
        exception is ServiceActivationException || 
        exception is ArgumentException) 
       { 
        //log 
       } 
       else 
       { 
        throw; 
       } 
      } 
     } 
    } 
3

So che sembra assurdo, ma ho affrontato lo stesso problema (w3wp.exe non sparare automaticamente dopo aver apportato le modifiche di configurazione) ed è stato perché non avevo eseguire l'editor di testo in modalità Admin quando stavo modificando il file applicationHost.config. Stupido errore da parte mia.

Nella mia difesa stavo usando Notepad ++ che mi diceva che stava salvando quando effettivamente non lo era.

+2

Forse sei su un sistema a 64 bit? C'è una "funzionalità" conosciuta in cui il salvataggio viene reindirizzato alla cartella a 32 bit :-) http://stackoverflow.com/a/17595896/90033 – Konstantin

+1

OMG, ho perso metà della mia giornata proprio a questa stessa cosa. Un preventivo per te! :-) –

+1

@ Konstantin ....... che in realtà dovrebbe essere la risposta corretta. Questo è così f *** ed. Grazie per il suggerimento, mi ha salvato la giornata. –

1

Forse sei su un sistema a 64 bit? C'è un noto "feature" in Windows in cui il salvataggio viene reindirizzato alla cartella a 32 bit e quindi nessuna modifica sarà raccolto

(Ho convertito il mio comment ad una risposta come risposte potrebbero essere più facile da trovare)

Problemi correlati