2012-05-06 21 views
16

Aby przetestować wiele problemów związanych z implementacją IIS/WCF od zera, zbudowałem usługę HelloWorld i klient przeszedł (bardzo ładnie) here. Dodałem punkty końcowe dla net.tcp, a usługa działa poprawnie od końca do końca dla obu wiązań pod numerem IIS 7.5 (w systemie Windows 7) we własnym ApplicationPool nazywanym HW.Funkcje AutoStart/Pre-warm nie działające w usługach IIS 7.5/WCF

To, co próbuję uzyskać, to zapowiedziane funkcje AutoStart i Preload (lub "pre-warm caching"). Postępowałem zgodnie z instrukcjami podanymi here i here (dość podobnymi do siebie, ale zawsze dobrze mieć drugą opinię) bardzo uważnie. Co oznacza, że ​​

1) Ustaw pulę aplikacji startMode ...

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

2) ... włączony serviceAutoStart i ustawić wskaźnik do moich serviceAutoStartProvider

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

3) ... i nazwano wspomnianego dostawcę, z GetType().AssemblyQualifiedName klasy wymienionej w całości poniżej

<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(); 
     } 
    } 
} 

Niestety, cała ta konfiguracja ręczna, plus kilka iisreset rozmowy, a ja nic. No w3wp.exe proces rozpalania w Menedżerze zadań (chociaż otrzymam go, gdy uruchomię HelloWorldClient), bez pliku tekstowego, a przede wszystkim bez satysfakcji.

Dyskusja na temat tej funkcji jest frustrująco niska, zarówno na SO, jak i na szerszą sieć, a kilka podobnych pytań nie ma zbyt wiele uwagi, a wszystkie dzwonki alarmowe lub dwa. Być może jednak niepotrzebnie - czy ci eksperci, którzy byli na tej drodze, dbają o to, by zadzwonić? (Chętnie oferują całą rozwiązanie jeśli można zasugerować dobre miejsce, aby go gościć.)


EDIT: Próbowałem zresetowanie tej ścieżki w metodzie Preload do względnego folderze App_Data (inny SO odpowiedź sugeruje to), nie miało znaczenia. Nauczyłem się również pożarów procesów w3wp.exe na prostym przejściu do hosta lokalnego. Proces zużywa imponujące 17 MB pamięci, aby obsłużyć pojedynczą niewielką operację OperationContract, a przy cenie oferującej zerową wartość wstępnego ładowania. 17 MB pamięci ColdDeadCache.

+0

są istnieją jakieś wskazówki w dzienniku zdarzeń? Wszelkie zgłaszane wyjątki powinny się tam pojawić. – Addys

+0

Nie, nic. Nie wiem, dlaczego spodziewałbyś się wyjątku, jeśli (jak podano) usługa działa poprawnie. – downwitch

+0

Kilka rzeczy, które możesz sprawdzić: - Czy identyfikator Twojej witryny 2? - Czy to prawda, że ​​nazwa Twojej witryny i puli aplikacji jest taka sama? - Podałeś więcej atrybutów niż w przykładach, czy właśnie te w przykładach robią różnicę? –

Odpowiedz

4

Jest to nieco inne podejście do problemu:

  1. Zastosowanie Windows Server AppFabric do serwisu auto-start
  2. infrastruktury
  3. użycie WCF wykonać kod niestandardowy Startup

Re 1: Appfabric AutoStart feature powinien po prostu pracuj po wyjęciu z pudełka (pod warunkiem, że nie korzystasz z usługi ServiceRoute firmy MVC w celu zarejestrowania swoich usług, MUSZĄ być one określone w sekcji Web.config: serviceActivations lub przy użyciu usługi fizycznej *.svc plików.

Re 2: Aby wprowadzić kod niestandardowy startowy do rurociągu WCF można użyć atrybutu takiego:

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

..which mogą być używane tak:

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

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

} 

To dokładna podejście, z którego korzystamy od dłuższego czasu i na dużej liczbie usług. Dodatkową zaletą korzystania z WCF ServiceBehavior dla niestandardowego kodu startowego (w przeciwieństwie do polegania na infrastrukturze IIS) jest to, że działa on w dowolnym środowisku hostingowym (w tym z własnym hostowaniem) i może być łatwiej testowany.

+0

Zabawne, już kierujemy się kierunkiem AppFabric, więc jest to ładny "jaskółczy ogon". Kochaj swój kod, nawet nieprzetestowany, i zobacz, gdzie chce iść. Bounty jest twoje, ale musisz nadążyć, jeśli mam więcej pytań w komentarzach;) – downwitch

+0

@ServiceGuy Właśnie chciałem wyjaśnić, że twoje dwa punkty są zależne, (tj. Użyj AppFabric ** AND ** dodaj hak do infrastruktury WCF) . Innymi słowy, punkt 2 nie rozwiązuje problemu w kontekście hostingu AutoStart IIS. Czy to jest poprawne? – pamphlet

+0

@phohlet: Tak, 1 i 2 będą konieczne, (1) aby faktycznie uruchomić usługę, (2) aby wykonać niestandardową logikę OnStart. – mthierba

1

Zrobiłem to samo. działa ...

W metodzie wstępnego ładowania mam kod skopiowany z ładnego białego papieru dostępnego here!

metoda Preload wygląda ...

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

Wiem, brzmi to absurd, ale w obliczu tego samego problemu (w3wp.exe nie wypalania automatycznie po dokonaniu zmian config) a to dlatego, że nie miał uruchom edytor tekstu w trybie administratora podczas edytowania pliku applicationHost.config. Głupi błąd z mojej strony.

W mojej obronie użyłem Notepad ++, który powiedział mi, że to było zapisywanie, kiedy tak naprawdę nie było.

+2

Może jesteś w systemie 64-bitowym? Znana jest "funkcja", w której zapis zostaje przekierowany do folderu 32-bitowego :-) http://pl.pojedyncze.google.com/a/17595896/90033 – Konstantin

+1

OMG, właśnie straciłem połowę dnia na to samo. Awans dla ciebie! :-) –

+1

@Konstantin ....... to właściwie powinna być poprawna odpowiedź. To jest tak oszalałe. Dzięki za napiwek, uratowałeś mój dzień. –

1

Być może używasz systemu 64-bitowego? Znany jest "feature" w systemie Windows, gdzie Zapisz zostaje przekierowany do folderu 32 bit a zatem żadne zmiany nie zostaną podniósł

(Mam konwertowane mój comment do odpowiedzi jako odpowiedzi może być łatwiejsze do znalezienia)

Powiązane problemy