自动启动/预热功能在IIS 7.5/WCF服务中不起作用
为了从头开始测试IIS/WCF实现中的许多令人头痛的问题,我构建了HelloWorld服务,客户机也进行了演练(非常好)。我为net.tcp添加了端点,并且该服务在其自己的名为HW的自动启动/预热功能在IIS 7.5/WCF服务中不起作用,wcf,configuration,iis-7.5,application-pool,autostart,Wcf,Configuration,Iis 7.5,Application Pool,Autostart,为了从头开始测试IIS/WCF实现中的许多令人头痛的问题,我构建了HelloWorld服务,客户机也进行了演练(非常好)。我为net.tcp添加了端点,并且该服务在其自己的名为HW的应用程序池中的IIS 7.5(在Windows 7上)下的两个绑定端到端正常工作 我正在努力实现的是宣布的AutoStart和Preload(或“预热缓存”)功能。我已经非常仔细地遵循了列出的说明(彼此非常相似,但有第二个意见总是很好的)。也就是说我 1) 设置应用程序池startMode <applicati
应用程序池中的IIS 7.5(在Windows 7上)下的两个绑定端到端正常工作
我正在努力实现的是宣布的AutoStart和Preload(或“预热缓存”)功能。我已经非常仔细地遵循了列出的说明(彼此非常相似,但有第二个意见总是很好的)。也就是说我
1) 设置应用程序池startMode
<applicationPools>
<!-- ... -->
<add name="HW" managedRuntimeVersion="v4.0" startMode="AlwaysRunning" />
</applicationPools>
3) …并命名为所述提供者,下面完整列出了类的GetType().AssemblyQualifiedName
<serviceAutoStartProviders>
<add name="PreWarmMyCache" type="MyWCFServices.Preloader, HelloWorldServer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</serviceAutoStartProviders>
唉,所有这些手动配置,加上几个iisreset
调用,我什么也得不到。没有w3wp.exe
进程在任务管理器中启动(尽管我在启动HelloWorldClient时得到它),没有文本文件,最重要的是,没有满足感
令人沮丧的是,无论是在SO还是在更广泛的网络上,关于该功能的讨论都很少,这里的几个类似问题几乎没有得到关注,所有这些都敲响了警钟。也许是不必要的——有没有哪位专家在这条路上走过一两段时间,愿意插话?(如果您能建议一个好的托管地点,我们很乐意提供整个解决方案。)
EDIT:我尝试在Preload
方法中将该路径重置为相对的App\u Data
文件夹(另一个SO答案表明),这无关紧要。此外,我还通过简单的本地主机浏览了解了w3wp.exe
进程。这个过程消耗了令人印象深刻的17MB内存来完成它的一个微小的操作契约,同时提供零预加载值的价格。17MB的ColdDeadCache。对于您的问题,这是一种稍微不同的方法:
用于服务自动启动
使用WCF基础结构执行自定义启动代码
Re 1:应该是开箱即用的(如果您没有使用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
{
}
这正是我们在大量服务上使用了相当长一段时间的方法。使用WCFServiceBehavior
定制启动代码(而不是依赖IIS基础设施)的额外好处是,它可以在任何托管环境中工作(包括自托管)而且更容易测试。我也这么做了。它很有效
在预加载方法,我有一些代码从一个很好的白皮书复制可用
预加载方法看起来像
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;
}
}
}
}
public void预加载(字符串[]参数)
{
bool isServceActivated=假;
int=0;
虽然(!isServceActivated&&(尝试我知道这听起来很荒谬,但我遇到了同样的问题(w3wp.exe在进行配置更改后不会自动启动),这是因为我在编辑applicationHost.config文件时没有在管理模式下运行文本编辑器。我犯了一个愚蠢的错误
在我的辩护中,我使用了记事本+,记事本告诉我它正在保存,而实际上它没有保存。也许你是在64位系统上?Windows中有一个已知的错误,保存会被重定向到32位文件夹,因此不会进行任何更改
(我已经将我的答案转换为一个答案,因为答案可能更容易找到)有趣的是,我们已经朝着AppFabric的方向前进了,所以这是一个很好的衔接。我喜欢你的代码,即使没有经过测试,也可以看到它想要去哪里。Bounty是你的,但如果我在评论中有更多问题,你必须跟上;)@ServiceGuy我只是想澄清你的两点是相互依赖的,(即使用AppFabric并向WCF基础设施添加一个钩子)。换句话说,第2点并不能解决AutoStart IIS托管上下文中的问题。对吗?@小册子:是的,1和2是必要的,(1)实际自动启动你的服务,(2)执行自定义OnStart逻辑。也许您是在64位系统上?有一个已知的“功能”可以将存储重定向到32位文件夹:-)天哪,我刚刚因为同样的事情浪费了半天的时间。对您的支持:-)@康斯坦丁……事实上,这应该是正确的答案。这太糟糕了。谢谢你的提示,救了我一天。这帮助我解决了问题。谢谢克里斯。事件日志中有任何线索吗?抛出的任何异常都应该显示在那里。不,没有。不确定你为什么会期望异常,如果(如上所述)该服务运行良好。您可以检查以下几点:-站点2的id是否正确?-站点和应用程序池的名称是否相同?-您指定的属性比示例中的属性多,仅指定示例中的属性是否会有所不同?
public static class ServiceActivation
{
public static void OnServiceActivated()
{
// Your startup code here
}
}
[ServiceActivator(typeof(ServiceActivation), "OnServiceActivated")]
public class YourService : IYourServiceContract
{
}
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;
}
}
}
}