WCF(16): 如何使用消息队列 (MSMQ - 微软的消息队列)
最编程
2024-01-21 18:05:57
...
[索引页]
[×××]
作者:webabcd
介绍
WCF(Windows Communication Foundation) - 消息队列(MSMQ - MicroSoft Message Queue):
netMsmqBinding的binding属性配置如下:
·ExactlyOnce - 确保消息只被投递一次。只能应用于事务型队列,默认值 ture
·Durable - 消息是否需要持久化。默认值 enabled,如果设置为disable,当MSMQ服务重启后,先前保存在MSMQ中的消息将会丢失
·TimeToLive - 消息过期并且从原有的队列移动到死信队列的时间。默认值 1.00:00:00 (1天)
·ReceiveRetryCount - 配置队列管理器在一定重试间隔中,尝试重新投递消息的次数,也就是将消息传输到重试队列前尝试发送该消息的最大次数(每隔RetryCycleDelay的时间重试ReceiveRetryCount次)。缺省值 5
·MaxRetryCycles - 配置队列管理器重新投递消息的重试间隔数(执行RetryCycleDelay的次数),也就是重试最大周期数。缺省值 2
·RetryCycleDelay - 表示两次重试之间的间隔时间,也就是重试周期之间的延迟。缺省值 00:30:00
·ReceiveErrorHandling - 指定如何处理错误的消息。Fault、Drop、Reject或Move(具体说明查MSDN)
·DeadLetterQueue - 指定所使用的死信队列的类型。None、System、或Custom(具体说明查MSDN)
·CustomDeadLetterQueue - 本地自定义死信队列的URI
示例
1、服务
IMSMQ.cs
[×××]
化零为整WCF(16) - 消息队列(MSMQ - MicroSoft Message Queue)
作者:webabcd
介绍
WCF(Windows Communication Foundation) - 消息队列(MSMQ - MicroSoft Message Queue):
netMsmqBinding的binding属性配置如下:
·ExactlyOnce - 确保消息只被投递一次。只能应用于事务型队列,默认值 ture
·Durable - 消息是否需要持久化。默认值 enabled,如果设置为disable,当MSMQ服务重启后,先前保存在MSMQ中的消息将会丢失
·TimeToLive - 消息过期并且从原有的队列移动到死信队列的时间。默认值 1.00:00:00 (1天)
·ReceiveRetryCount - 配置队列管理器在一定重试间隔中,尝试重新投递消息的次数,也就是将消息传输到重试队列前尝试发送该消息的最大次数(每隔RetryCycleDelay的时间重试ReceiveRetryCount次)。缺省值 5
·MaxRetryCycles - 配置队列管理器重新投递消息的重试间隔数(执行RetryCycleDelay的次数),也就是重试最大周期数。缺省值 2
·RetryCycleDelay - 表示两次重试之间的间隔时间,也就是重试周期之间的延迟。缺省值 00:30:00
·ReceiveErrorHandling - 指定如何处理错误的消息。Fault、Drop、Reject或Move(具体说明查MSDN)
·DeadLetterQueue - 指定所使用的死信队列的类型。None、System、或Custom(具体说明查MSDN)
·CustomDeadLetterQueue - 本地自定义死信队列的URI
示例
1、服务
IMSMQ.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace WCF.ServiceLib.Message
{
/// <summary>
/// 演示MSMQ的接口
/// </summary>
/// <remarks>
/// DeliveryRequirements - 指定绑定必须提供给服务或客户端实现的功能要求
/// QueuedDeliveryRequirements - 指定服务的绑定是否必须支持排队协定
/// QueuedDeliveryRequirementsMode.Allowed - 允许排队传送
/// QueuedDeliveryRequirementsMode.Required - 要求排队传送
/// QueuedDeliveryRequirementsMode.NotAllowed - 不允许排队传送
/// </remarks>
[ServiceContract]
[DeliveryRequirements(QueuedDeliveryRequirements = QueuedDeliveryRequirementsMode.Required)]
public interface IMSMQ
{
/// <summary>
/// 将字符串写入文本文件
/// </summary>
/// <param name="str">需要写入文本文件的字符串</param>
/// <remarks>
/// 如果要使用 MSMQ 的话,则必须配置IsOneWay = true
/// </remarks>
[OperationContract(IsOneWay = true)]
void Write(string str);
}
}
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace WCF.ServiceLib.Message
{
/// <summary>
/// 演示MSMQ的接口
/// </summary>
/// <remarks>
/// DeliveryRequirements - 指定绑定必须提供给服务或客户端实现的功能要求
/// QueuedDeliveryRequirements - 指定服务的绑定是否必须支持排队协定
/// QueuedDeliveryRequirementsMode.Allowed - 允许排队传送
/// QueuedDeliveryRequirementsMode.Required - 要求排队传送
/// QueuedDeliveryRequirementsMode.NotAllowed - 不允许排队传送
/// </remarks>
[ServiceContract]
[DeliveryRequirements(QueuedDeliveryRequirements = QueuedDeliveryRequirementsMode.Required)]
public interface IMSMQ
{
/// <summary>
/// 将字符串写入文本文件
/// </summary>
/// <param name="str">需要写入文本文件的字符串</param>
/// <remarks>
/// 如果要使用 MSMQ 的话,则必须配置IsOneWay = true
/// </remarks>
[OperationContract(IsOneWay = true)]
void Write(string str);
}
}
MSMQ.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace WCF.ServiceLib.Message
{
/// <summary>
/// 演示MSMQ的类
/// </summary>
public class MSMQ : IMSMQ
{
/// <summary>
/// 将字符串写入文本文件
/// </summary>
/// <param name="str">需要写入文本文件的字符串</param>
public void Write(string str)
{
System.IO.StreamWriter sw = new System.IO.StreamWriter(@"C:\WCF_Log_MSMQ.txt", true);
sw.Write(str);
sw.WriteLine();
sw.Close();
}
}
}
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace WCF.ServiceLib.Message
{
/// <summary>
/// 演示MSMQ的类
/// </summary>
public class MSMQ : IMSMQ
{
/// <summary>
/// 将字符串写入文本文件
/// </summary>
/// <param name="str">需要写入文本文件的字符串</param>
public void Write(string str)
{
System.IO.StreamWriter sw = new System.IO.StreamWriter(@"C:\WCF_Log_MSMQ.txt", true);
sw.Write(str);
sw.WriteLine();
sw.Close();
}
}
}
2、宿主
MSMQ.cs
MSMQ.cs
// 队列名
// 只能使用.\private$\YourPrivateMSMQName来访问本机的私有MSMQ队列
string queueName = @".\private$\SampleMSMQ";
// 没有queueName队列,则创建queueName队列
if (!System.Messaging.MessageQueue.Exists(queueName))
{
// 第二个参数为是否创建事务性队列
System.Messaging.MessageQueue.Create(queueName, true);
}
using (ServiceHost host = new ServiceHost(typeof(WCF.ServiceLib.Message.MSMQ)))
{
host.Open();
Console.WriteLine("服务已启动(WCF.ServiceLib.Message.MSMQ)");
Console.WriteLine("按<ENTER>停止服务");
Console.ReadLine();
}
// 只能使用.\private$\YourPrivateMSMQName来访问本机的私有MSMQ队列
string queueName = @".\private$\SampleMSMQ";
// 没有queueName队列,则创建queueName队列
if (!System.Messaging.MessageQueue.Exists(queueName))
{
// 第二个参数为是否创建事务性队列
System.Messaging.MessageQueue.Create(queueName, true);
}
using (ServiceHost host = new ServiceHost(typeof(WCF.ServiceLib.Message.MSMQ)))
{
host.Open();
Console.WriteLine("服务已启动(WCF.ServiceLib.Message.MSMQ)");
Console.WriteLine("按<ENTER>停止服务");
Console.ReadLine();
}
App.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<!--name - 提供服务的类名-->
<!--behaviorConfiguration - 指定相关的行为配置-->
<service name="WCF.ServiceLib.Message.MSMQ" behaviorConfiguration="MessageBehavior">
<!--address - 服务地址-->
<!--binding - 通信方式-->
<!--contract - 服务契约-->
<!--bindingConfiguration - 指定相关的绑定配置-->
<endpoint address="" binding="netMsmqBinding" contract="WCF.ServiceLib.Message.IMSMQ" bindingConfiguration="MSMQBindingConfiguration" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:12345/Message/MSMQ"/>
<add baseAddress="net.msmq://localhost/private/SampleMSMQ"/>
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="MessageBehavior">
<!--httpGetEnabled - 指示是否发布服务元数据以便使用 HTTP/GET 请求进行检索,如果发布 WSDL,则为 true,否则为 false,默认值为 false-->
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<netMsmqBinding>
<binding name="MSMQBindingConfiguration">
<security>
<!--msmqAuthenticationMode - 指示 MSMQ 传输必须采用什么方式对消息进行身份验证,默认值 WindowsDomain -->
<!--MsmqAuthenticationMode.None - 不使用任何安全性-->
<!--MsmqAuthenticationMode.WindowsDomain - 通过 Kerberos 进行身份验证,客户端和服务器必须连接到受信任域-->
<!--MsmqAuthenticationMode.Certificate - 客户端通过 X.509 证书进行身份验证,客户端证书必须显示在服务器的证书存储区中-->
<!--msmqProtectionLevel - 保护级别,设置与 MsmqAuthenticationMode 相关联的 ProtectionLevel,默认值 Sign -->
<!--ProtectionLevel.None - 只做身份验证-->
<!--ProtectionLevel.Sign - 对数据做签名,以确保所传输数据的完整性-->
<!--ProtectionLevel.EncryptAndSign - 对数据做加密和签名,以确保所传输数据的保密性和完整性-->
<transport msmqAuthenticationMode="None" msmqProtectionLevel="None" />
<!--clientCredentialType - 客户端用以进行身份验证的凭据的类型,默认值 UserName -->
<!--BasicHttpMessageCredentialType.UserName - 使用用户名凭据对客户端进行身份验证-->
<!--BasicHttpMessageCredentialType.Certificate - 使用证书对客户端进行身份验证-->
<message clientCredentialType="UserName" />
</security>
</binding>
</netMsmqBinding>
</bindings>
</system.serviceModel>
</configuration>
<configuration>
<system.serviceModel>
<services>
<!--name - 提供服务的类名-->
<!--behaviorConfiguration - 指定相关的行为配置-->
<service name="WCF.ServiceLib.Message.MSMQ" behaviorConfiguration="MessageBehavior">
<!--address - 服务地址-->
<!--binding - 通信方式-->
<!--contract - 服务契约-->
<!--bindingConfiguration - 指定相关的绑定配置-->
<endpoint address="" binding="netMsmqBinding" contract="WCF.ServiceLib.Message.IMSMQ" bindingConfiguration="MSMQBindingConfiguration" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:12345/Message/MSMQ"/>
<add baseAddress="net.msmq://localhost/private/SampleMSMQ"/>
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="MessageBehavior">
<!--httpGetEnabled - 指示是否发布服务元数据以便使用 HTTP/GET 请求进行检索,如果发布 WSDL,则为 true,否则为 false,默认值为 false-->
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<netMsmqBinding>
<binding name="MSMQBindingConfiguration">
<security>
<!--msmqAuthenticationMode - 指示 MSMQ 传输必须采用什么方式对消息进行身份验证,默认值 WindowsDomain -->
<!--MsmqAuthenticationMode.None - 不使用任何安全性-->
<!--MsmqAuthenticationMode.WindowsDomain - 通过 Kerberos 进行身份验证,客户端和服务器必须连接到受信任域-->
<!--MsmqAuthenticationMode.Certificate - 客户端通过 X.509 证书进行身份验证,客户端证书必须显示在服务器的证书存储区中-->
<!--msmqProtectionLevel - 保护级别,设置与 MsmqAuthenticationMode 相关联的 ProtectionLevel,默认值 Sign -->
<!--ProtectionLevel.None - 只做身份验证-->
<!--ProtectionLevel.Sign - 对数据做签名,以确保所传输数据的完整性-->
<!--ProtectionLevel.EncryptAndSign - 对数据做加密和签名,以确保所传输数据的保密性和完整性-->
<transport msmqAuthenticationMode="None" msmqProtectionLevel="None" />
<!--clientCredentialType - 客户端用以进行身份验证的凭据的类型,默认值 UserName -->
<!--BasicHttpMessageCredentialType.UserName - 使用用户名凭据对客户端进行身份验证-->
<!--BasicHttpMessageCredentialType.Certificate - 使用证书对客户端进行身份验证-->
<message clientCredentialType="UserName" />
</security>
</binding>
</netMsmqBinding>
</bindings>
</system.serviceModel>
</configuration>
3、客户端
MSMQ.cs
MSMQ.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ServiceModel;
namespace Client2.Message
{
/// <summary>
/// 演示Message.MSMQ的类
/// </summary>
public class MSMQ
{
/// <summary>
/// 用于测试 MSMQ 的客户端
/// </summary>
/// <param name="str">需要写入文本文件的字符串</param>
public void HelloMSMQ(string str)
{
using (var proxy = new MessageSvc.MSMQ.MSMQClient())
{
proxy.Write(str);
}
}
}
}
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ServiceModel;
namespace Client2.Message
{
/// <summary>
/// 演示Message.MSMQ的类
/// </summary>
public class MSMQ
{
/// <summary>
/// 用于测试 MSMQ 的客户端
/// </summary>
/// <param name="str">需要写入文本文件的字符串</param>
public void HelloMSMQ(string str)
{
using (var proxy = new MessageSvc.MSMQ.MSMQClient())
{
proxy.Write(str);
}
}
}
}
App.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<client>
<!--address - 服务地址-->
<!--binding - 通信方式-->
<!--contract - 服务契约-->
<!--bindingConfiguration - 指定相关的绑定配置-->
<endpoint address="net.msmq://localhost/private/SampleMSMQ" binding="netMsmqBinding"
contract="MessageSvc.MSMQ.IMSMQ" bindingConfiguration="MSMQBindingConfiguration" />
</client>
<bindings>
<netMsmqBinding>
<binding name="MSMQBindingConfiguration">
<security>
<!--msmqAuthenticationMode - 指示 MSMQ 传输必须采用什么方式对消息进行身份验证,默认值 WindowsDomain -->
<!--MsmqAuthenticationMode.None - 不使用任何安全性-->
<!--MsmqAuthenticationMode.WindowsDomain - 通过 Kerberos 进行身份验证,客户端和服务器必须连接到受信任域-->
<!--MsmqAuthenticationMode.Certificate - 客户端通过 X.509 证书进行身份验证,客户端证书必须显示在服务器的证书存储区中-->
<!--msmqProtectionLevel - 保护级别,设置与 MsmqAuthenticationMode 相关联的 ProtectionLevel,默认值 Sign -->
<!--ProtectionLevel.None - 只做身份验证-->
<!--ProtectionLevel.Sign - 对数据做签名,以确保所传输数据的完整性-->
<!--ProtectionLevel.EncryptAndSign - 对数据做加密和签名,以确保所传输数据的保密性和完整性-->
<transport msmqAuthenticationMode="None" msmqProtectionLevel="None" />
<!--clientCredentialType - 客户端用以进行身份验证的凭据的类型,默认值 UserName -->
<!--BasicHttpMessageCredentialType.UserName - 使用用户名凭据对客户端进行身份验证-->
<!--BasicHttpMessageCredentialType.Certificate - 使用证书对客户端进行身份验证-->
<message clientCredentialType="UserName" />
</security>
</binding>
</netMsmqBinding>
</bindings>
</system.serviceModel>
</configuration>
<configuration>
<system.serviceModel>
<client>
<!--address - 服务地址-->
<!--binding - 通信方式-->
<!--contract - 服务契约-->
<!--bindingConfiguration - 指定相关的绑定配置-->
<endpoint address="net.msmq://localhost/private/SampleMSMQ" binding="netMsmqBinding"
contract="MessageSvc.MSMQ.IMSMQ" bindingConfiguration="MSMQBindingConfiguration" />
</client>
<bindings>
<netMsmqBinding>
<binding name="MSMQBindingConfiguration">
<security>
<!--msmqAuthenticationMode - 指示 MSMQ 传输必须采用什么方式对消息进行身份验证,默认值 WindowsDomain -->
<!--MsmqAuthenticationMode.None - 不使用任何安全性-->
<!--MsmqAuthenticationMode.WindowsDomain - 通过 Kerberos 进行身份验证,客户端和服务器必须连接到受信任域-->
<!--MsmqAuthenticationMode.Certificate - 客户端通过 X.509 证书进行身份验证,客户端证书必须显示在服务器的证书存储区中-->
<!--msmqProtectionLevel - 保护级别,设置与 MsmqAuthenticationMode 相关联的 ProtectionLevel,默认值 Sign -->
<!--ProtectionLevel.None - 只做身份验证-->
<!--ProtectionLevel.Sign - 对数据做签名,以确保所传输数据的完整性-->
<!--ProtectionLevel.EncryptAndSign - 对数据做加密和签名,以确保所传输数据的保密性和完整性-->
<transport msmqAuthenticationMode="None" msmqProtectionLevel="None" />
<!--clientCredentialType - 客户端用以进行身份验证的凭据的类型,默认值 UserName -->
<!--BasicHttpMessageCredentialType.UserName - 使用用户名凭据对客户端进行身份验证-->
<!--BasicHttpMessageCredentialType.Certificate - 使用证书对客户端进行身份验证-->
<message clientCredentialType="UserName" />
</security>
</binding>
</netMsmqBinding>
</bindings>
</system.serviceModel>
</configuration>
运行结果:
客户端调用时,如果没有启动服务端,那么消息会进入到消息队列中。等到服务端启动后,会执行消息队列中的所有消息。
OK
[×××]
客户端调用时,如果没有启动服务端,那么消息会进入到消息队列中。等到服务端启动后,会执行消息队列中的所有消息。
OK
[×××]
上一篇: C#扫荡局域网:找出所有IP与端口
推荐阅读
-
趣谈留言队列,搞清楚留言队列到底是什么!-说到消息队列,洪觉大概能猜到人们听到消息队列的反应,大致可以分为以下几类人。 第一类人,懵懵懂懂,刚上大学接触编程,还没用过消息队列,甚至还以为消息队列就是代码里面要新建一个List之类的;第二类人,听过消息队列,了解消息队列,但具体是什么还不是太明白,只知道一说到消息队列,脑海里马上出现了三组词,削峰、异步、解耦;第三类人,用过消息队列,对它有一定了解,但不知道为什么要这样设计,消息队列有什么样的前世今生,是如何演化到现在的模式的?**第四类人,已经对消息队列有了足够的了解,可以阅读本帖作为复习和温习。**你属于哪一类?无论你对消息队列了解多少,读完这篇文章后,我相信你都会有所收获。 什么是消息队列?我们为什么要使用消息队列?真的只是因为它看起来很勉强、很常用吗?当然不是,一项技术的出现往往是为了解决某种痛点,我们就从这个痛点出发,看看消息队列到底是为了解决什么问题而诞生的。 相信大家在工作之前,或者工作中接触单片机的次数会多一点,不管什么业务都一股脑塞进一个系统里,这种情况下接触消息队列的场景会比较少。但随着业务的增长,量上去了,单机系统就很难维护了,也扛不住并发量的增长,就需要把原来的单体应用拆分成多个服务。例如,牛奇网采用分布式架构,将原来的单体系统拆分成用户服务、题库服务、求职服务、论坛服务等,每个分布式节点都有一个集群,保证高可用性。 那虽然在这样的微服务架构下,如果某个核心业务并发量过大,系统就扛不住了。比如淘宝、淘票票、拼多多、京东等电商场景中的支付场景,你在某宝下单并支付后,调用支付服务,完成支付后,还需要更新订单的状态,这个时候就需要调用订单服务,那我们平时也下单,除了简单完成这些操作外,还会给你相应的积分;商家也会收到订单消息,并给您发送旺旺消息,确认订单无误;同时,也会给您发送消息,确认订单无误。确认订单无误;同时您还可以查看您的物流状态;还有系统为了给您推荐更适合您的商品,会根据您的订单做类似的推荐等等,我说的这些都是当我们下单后,肉眼可以感知到系统所做的动作。 **一个支付动作如果还需要调用那么多服务,等他们响应成功,最后再告诉用户你支付成功了,用户在系统中的整个体验会非常糟糕。**设想一下,假设请求服务+处理请求+响应总共需要 50ms,我们上面列出的场景:支付服务、订单服务、积分服务、商家服务、物流服务、推荐服务,总共需要 300ms。
-
WCF(16): 如何使用消息队列 (MSMQ - 微软的消息队列)
-
必知必会!WCF分布式开发中的MSMQ消息队列技术详解(上)