最近的项目,需要用到WCF,在以前的工作中,经常是将WCF托管在IIS中,主要有几下几个原因:
第一:部署非常方便,和部署一个站点没什么区别;
第二:不受防火墙的影响,因为一般服务器上80端口都是开放的;
第三:IIS托管不需要程序编写托管代码。
以前WCF的绑定基本上都是用wsHttpBinding,但这次项目需要将WCF配置成MSMQ,于是我们就需要用到netMsmqBinding,在采用netMsmqBinding过程中遇到如下问题:
第一:netMsmqBinding绑定权限问题。
原来服务托管在控制台时,服务正常,但托管在windows服务时就会出现如下异常:
System.TypeInitializationException: “System.ServiceModel.Channels.Msmq”的类型初始值设定项引发异常。 ---> System.ServiceModel.MsmqException: 版本检查失败,错误为:“消息队列服务不可用 (-1072824309, 0xc00e000b)”。无法检测到 MSMQ 的版本。该队列通道上的所有操作都将失败。确保已安装 MSMQ 且 MSMQ 可用。
这条信息显示没有权限,但控制台是没问题的,原因如下:
创建消息队列是用登录帐户,而运行控制台程序也是登录帐户,所以没有权限问题,但windows服务在运行时的帐户不一定就是登录帐户,一般我们指定的是ServiceAccount.LocalSystem,于是将消息队列增加everyone权限,问题解决。
另外我将服务托管在IIS7上,总会不成功,不知道大家有没有成功过,如果有成功的,希望给个示例。队列权限为everyone,还加了个匿名用户。
第二:服务端与客户端配置不同导致不能通信,当采用默认生成的配置通信正常,采用程序编码形式编写配置,通信异常,最后发现是binding中的属性exactlyOnce值不同。这种配置主要是指服务端的配置,比如客户端的超时时间等设置就可以任意指定。
result.ExactlyOnce =false;
第三:服务托管方式问题。
由于本人还未将netMsmqBinding托管在IIS上,所以只能托管在windows服务中,本来托管在控制台也可以,但应用程序的开启比较容易受其它登录用户的影响。这托管成windows服务时遇到如下问题:
场景:我需要将同一服务程序,部署为N个不同端口不同服务名称的服务。
问题:当安装一个服务后,正常启动,在安装第二个服务时,第二个服务的执行地址会记成第一个服务的地址。
分析:有可能是windows服务在安装过程中有安装记录,即使指定了服务程序路径也会受影响,大家有啥好办法就请教。
解决方案:将服务的exe,config更改名称,例如ComputeAsyncTask_Hight_1.exe,ComputeAsyncTask_Hight_2.exe,ComputeAsyncTask_Hight_3.exe,这种情况下,安装后的执行路径就没有问题。
遗留问题:虽然上面方案能解决执行路径问题,但在如下情况下依然有问题:
当所有服务都正常安装后,再次执行安装,比如再次安装ComputeAsyncTask_Hight_1.exe,首先是服务删除,此时系统所识别的assemblypath依然是ComputeAsyncTask_Hight_3.exe,此时就会将ComputeAsyncTask_Hight_3删除,这种情况目前还未找到好的方法。
第四:如何快速彻底删除一个windows服务
当一个服务安装后,但由于某些BUG做了调整,在某种情况下就会出现无法删除服务的情况,这种问题经常消耗我大量时间去排查,我们可以借用用sc.exe这个Windows命令来解决,效果非常好,再也不用花精力去分析服务为什么不能删除的原因了。
开始——运行——cmd.exe,使用办法很简单:
sc delete "服务名" (如果服务名中间有空格,就需要前后加引号)
第五:在服务的安装类中不能引用app.config
如果将服务托管为windows服务,一般都会编写服务安装代码,这样我们就可以利用ManagedInstallerClass.InstallHelper来实现服务的自动安装,它主要是用来代替Installutil.exe手工操作。我在项目中需要将同一服务程序部署为不同端口不同服务名称的服务,所以我考虑将服务名称从app.config中指定,于是就是类似下面的代码:
service.ServiceName = ConfigurationManager.AppSettings["ServiceName"].Trim();
但在安装过程中总是报错,查明原因,是无法找到配置节,最后才知道当服务在安装时,并不是一个应用程序,理所当然就不会加载app.config,所以在安装类中我们不能调用app.config。
解决方案:将配置节从app.config中转移,比如新建一个xml文件,然后在安装时,服务名称等信息从xml配置文件中读取。
privatestring GetComputeLevel()
{
try
{
string strPath;
XmlDocument xmldoc =new XmlDocument();
XmlNode xmlnd;
strPath = System.Environment.CurrentDirectory +@"ComputeLevelConfig.xml";
strPath = AppDomain.CurrentDomain.BaseDirectory +@"ComputeLevelConfig.xml";
if (!File.Exists(strPath))
{
thrownew FileNotFoundException("未能找到配置文件"+strPath);
}
xmldoc.Load(strPath);
xmlnd = xmldoc.SelectSingleNode("ComputeLevelConfig");
if (null!= xmlnd)
{
return xmlnd.InnerText.Trim ();
}
else
{
thrownew Exception("没有指定计算优先级!"+ strPath);
}
}
catch (Exception ex)
{
throw ex;
}
}
以前在开发WCF时,大多都是基于默认配置,当一个服务写好之后,以后在开发新的服务,基本都是参考以前成功的案例,所以对服务端的配置文件未做过多了解。借这次机会,重 新整理下服务端配置文件的构造情况。
主体结构分为以下三个部分:
1:behaviors
默认情况会出现如下两个节点,如果想让服务端向客户端抛出详细错误,可以修改includeExceptionDetailInFaults,其它的节点我目前还未使用过。
指定绑定模式,我这里指定netMsmqBinding,下面的内容,可以补充一些与默认值不相同的内容,比如exactlyOnce,security,其它不需要额外指定的,就为默认值。
最重要的数endpoint以及host,endpoint理解ABC就行:
A:address,服务地址,如果是wshttpbing这种IIS托管形式,此处应该为空字符串,如果是非http绑定,例如netMsmqBinding,需要指定队列地址;
B:binding,绑定类型
C:contract,服务契约
host这个结点一般是针对非http绑定,用它来提供基地址,http绑定就不需要配置了。
补充1:
バインドの ExactlyOnce プロパティが true に設定されているのに、宛先キューが非トランザクション キューであるため、バインドの検証が失敗しました。サービス ホストを開けません。ExactlyOnce プロパティを false に設定するか、このバインド用のトランザクション キューを作成して、この競合を解決してください。
原因是:没有配置serviceBehaviors节点,只配置了services的
<serviceBehaviors>
<behavior name="MsmqServiceBehavior">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
<serviceThrottling maxConcurrentCalls="1"/>
</behavior>
</serviceBehaviors>
<services>
<service name="MsmqService" behaviorConfiguration="MsmqServiceBehavior">
<endpoint address="net.msmq://localhost/private/spc/MsmqService.svc" binding="netMsmqBinding" bindingConfiguration="MsmqBindingNonTransactionalNoSecurity" contract="IMsmqContract"/>
</service>
</services>