Функции AutoStart/Pre-warm, не работающие в службе IIS 7.5/WCF - программирование
Подтвердить что ты не робот

Функции AutoStart/Pre-warm, не работающие в службе IIS 7.5/WCF

Для тестирования многих головных проблем реализации IIS/WCF с нуля я создал службу HelloWorld, и клиент прошел (очень красиво) здесь. Я добавил конечные точки для net.tcp, и служба работает нормально для обоих привязок в IIS 7.5 (в Windows 7) в своем собственном ApplicationPool под названием HW.

То, что я пытаюсь получить, - это объявленные функции AutoStart и Preload (или "pre-warm caching" ). Я следил за инструкциями здесь и здесь (очень похожи друг на друга, но всегда хорошо имеют второе мнение) очень тесно. Это означает, что я

1) Задайте пул приложений startMode...

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

2)... включил serviceAutoStart и установил указатель на мой serviceAutoStartProvider

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

3)... и назвал указанный провайдер, с GetType().AssemblyQualifiedName класса, указанного в полном объеме ниже

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

Увы, вся эта ручная конфигурация плюс пара iisreset вызывает, и я ничего не получаю. Нет процесса w3wp.exe, запускающегося в диспетчере задач (хотя я получаю его, если я запускаю HelloWorldClient), никакого текстового файла и, прежде всего, никакого удовлетворения.

Существует разочаровывающее количество обсуждений этой функции, как на SO, так и в более широкой сети, и несколько подобных вопросов здесь мало привлекают внимание, все из которых звонят в колокольчик или два. Может быть, бесполезно, хотя - какие-то эксперты, которые были на этом самом пути, время или два, чтобы перезвонить? (Счастлив предложить все решение, если вы можете предложить хорошее место для его размещения.)


EDIT: я попытался сбросить этот путь в методе Preload в относительную папку App_Data (другой ответ, предложенный SO), не имеет значения. Кроме того, я узнал, что процесс w3wp.exe запускается при простом просмотре на localhost. Этот процесс потребляет впечатляющую 17 МБ памяти, чтобы обслуживать свой единственный крошечный OperationContract, в то время как цена предлагала нулевое значение Preload. 17MB ColdDeadCache.

4b9b3361

Ответ 1

Это немного другой подход к вашей проблеме:

  • Используйте Windows Server AppFabric для автоматического запуска службы
  • Использовать инфраструктуру WCF для выполнения пользовательского кода запуска

Re 1: Функция Appfabric AutoStart должна просто работать из коробки (при условии, что вы не используете MVC ServiceRoute для регистрации ваших услуг, они ДОЛЖНЫ указываться либо в разделе Web.config serviceActivations, либо с использованием физических файлов *.svc.

Re 2: Чтобы добавить пользовательский код запуска в конвейер WCF, вы можете использовать такой атрибут:

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

.., который можно использовать следующим образом:

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

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

}

Точный подход, который мы использовали в течение довольно долгого времени и для большого количества сервисов. Дополнительное преимущество использования WCF ServiceBehavior для пользовательского кода запуска (в отличие от использования инфраструктуры IIS) заключается в том, что он работает в любой среде хостинга (включая самообслуживание) и может быть более легко протестирован.

Ответ 2

Я знаю, что это звучит абсурдно, но я столкнулся с той же проблемой (w3wp.exe не запускается автоматически после внесения изменений в конфигурацию), и это произошло потому, что я не запускал текстовый редактор в режиме администратора, когда редактировал applicationHost.config файл. Глупая ошибка с моей стороны.

В моей защите я использовал Notepad ++, который сказал мне, что он экономит, когда на самом деле это не так.

Ответ 3

Я сделал то же самое. он работает...

В методе предварительной загрузки у меня есть код, скопированный из красивой белой бумаги здесь!

Метод предварительной загрузки выглядит как...

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

Ответ 4

Возможно, вы на 64-битной системе? В Windows есть известная "функция" , в которой сохранение будет перенаправлено в 32-битную папку и, следовательно, никаких изменений не будет найдено

(Я преобразовал комментарий в ответ, поскольку ответы могут быть проще найти)