16

为了从头开始测试 IIS/WCF 实现的许多令人头疼的问题,我构建了 HelloWorld 服务,并且客户端(非常好)在这里完成了。我为 net.tcp 添加了端点,并且该服务在其自己的称为 HWIIS 7.5的(在 Windows 7 上)下的两个绑定中端到端正常工作。ApplicationPool

我正在尝试使用已宣布的 AutoStart 和 Preload(或“预热缓存”)功能。我已经非常严格地遵循了此处此处列出的说明(彼此非常相似,但有第二个意见总是好的)。这意味着我

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 上还是在更广泛的网络上,关于此功能的讨论都非常少,而且这里的几个类似问题也很少受到关注,所有这些都敲响了一两个警钟。也许是不必要的——有哪位专家曾在这条路上走过一两次,愿意插话吗?(如果您能推荐一个托管它的好地方,很高兴提供整个解决方案。)


编辑:我尝试将方法中的路径重置Preload为相对App_Data文件夹(另一个 SO 答案建议),没关系。此外,我了解到该w3wp.exe过程会在简单浏览到 localhost 时触发。该过程消耗了令人印象深刻的 17MB 内存来提供其单个微小的 OperationContract,同时提供零预加载值的价格。17MB 的 ColdDeadCache。

4

4 回答 4

4

对于您的问题,这是一种略有不同的方法:

  1. 使用Windows Server AppFabric进行服务自动启动
  2. 使用 WCF 基础结构执行自定义启动代码

回复 1:Appfabric 自动启动功能应该开箱即用(如果您没有使用 MVC 的 ServiceRoute 来注册您的服务,则必须在 Web.configserviceActivations部分或使用物理*.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
{

}

这正是我们在大量服务中使用了相当长一段时间的确切方法。将 WCFServiceBehavior用于自定义启动代码(而不是依赖于 IIS 基础结构)的额外好处是它可以在任何托管环境(包括自托管)中工作,并且可以更容易地进行测试。

于 2012-05-15T11:42:48.350 回答
3

我知道这听起来很荒谬,但我遇到了同样的问题(在进行配置更改后 w3wp.exe 不会自动触发),这是因为我在编辑 applicationHost.config 文件时没有在管理员模式下运行文本编辑器。我犯了愚蠢的错误。

在我的辩护中,我使用的是 Notepad++,它告诉我它正在保存,而实际上它不是。

于 2013-08-14T17:45:36.893 回答
1

也许您使用的是 64 位系统?Windows 中有一个已知的“功能”,其中保存被重定向到 32 位文件夹,因此不会获取任何更改

(我已将我的评论转换为答案,因为答案可能更容易找到)

于 2014-12-16T09:51:04.477 回答
1

我也做过同样的事情。有用...

在预加载方法中,我从此处提供的一份不错的白皮书中复制了一些代码!

预加载方法看起来像......

 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;
                }
            }
        }
   }
于 2012-05-16T12:42:55.940 回答