WCF、MSMQ和2个队列上的独立事务



我已经构建了一个处理MSMQ的WCF服务,让我们调用服务QueueService。合同如下:

// Each call to the service will be dispatched separately, not grouped into sessions.
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class QueueServiceContract : IQueueServiceContract
{
[OperationBehavior(TransactionScopeRequired = true)]
public void QueueItems(List<Item> items)     // really should be called 'HandleQueueItems
{
//  Early in the processing I do:
Transaction qTransaction = Transaction.Current;
...
// I then check if the destination database is available.
if(DB.IsAvailable)
... process the data
else
qTransaction.Rollback;
...
}

IQueueServiceContract如下所示:

// The contract will be session-less.  Each post to the queue from the client will create a single message on the queue.
[ServiceContract(SessionMode = SessionMode.NotAllowed, Namespace = "MyWebService")]
public interface IQueueServiceContract
{
[OperationContract(IsOneWay = true)]
void QueueItems(List<Item> items);
}

队列服务的App.config的相关部分如下所示。

<services>
<service name="QueueService.QueueServiceContract">
<endpoint address="net.msmq://localhost/private/MyQueueServiceQueue" binding="netMsmqBinding" contract="QueueService.IQueueServiceContract">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
...
<netMsmqBinding>
<binding exactlyOnce="true" maxRetryCycles="1000" receiveRetryCount="1"
retryCycleDelay="00:10:00" timeToLive="7.00:00:00" useActiveDirectory="false">
</binding>
</netMsmqBinding>

这一切都很好。当DB不可用时,回滚会将队列条目放入重试子队列中,我已将其配置为每10分钟重试一次,持续7天。它的一切都很有效,并且已经生产了6个月左右。

现在我将日志记录添加到服务中。QueueService将把日志条目排队到另一个队列中,我们将称之为:LogQueue。要求是,无论qTransaction是否回滚,都应向LogQueue发送一条消息,指示请求的状态。

在QueueService app.config中,我添加了:

<client>
<endpoint address="net.msmq://localhost/private/MyLogQueue"
binding="netMsmqBinding" bindingConfiguration="NetMsmqBinding_ILogContract"
contract="LogServiceReference.ILogContract" name="NetMsmqBinding_ILogContract">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
</client>
...
<binding name="NetMsmqBinding_ILogContract" timeToLive="7.00:00:00">
<security mode="None" />
</binding>

在LogService app.config中,我有:

<service name="LogService.LogContract">
<endpoint address="net.msmq://localhost/private/MyLogQueue" binding="netMsmqBinding" contract="LogService.ILogContract">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
... 
<netMsmqBinding>
<binding exactlyOnce="true" maxRetryCycles="1000" receiveRetryCount="1" retryCycleDelay="00:10:00" timeToLive="7.00:00:00"  useActiveDirectory="false">
</binding>
</netMsmqBinding>
...

然后,在QueueItems方法的末尾,我执行以下操作:

LogContractClient proxy = new LogContractClient();
proxy.LogTransaction(myLoggingInformation);             // This queues myLoggingInformation to the LogQueue.

这一切也很好。。。直到数据库不可用,事务被回滚。

回滚将在调用代理之前进行。LogTransaction和我将得到:

System.ServiceModel.CommunicationException: 'An error occurred while sending to the queue: The transaction specified cannot be enlisted. (-1072824232, 0xc00e0058).Ensure that MSMQ is installed and running. If you are sending to a local queue, ensure the queue exists with the required access mode and authorization.'

如果我移动代理。在qTransaction之前的LogTransaction。回滚日志条目从不放入LogQueue中。

我的工作原理是WCF将两个队列上的操作(从QueueService队列读取和写入LogQueue(视为单个事务。因此,如果我试图在回滚之后写入LogQueue,则事务已经结束,但如果我在调用回滚之前写入LogQueue则对队列的写入也会回滚。

是否有任何方法可以在不同时回滚LogService事务的情况下保留回滚queueService事务的能力?

我认为可以通过使用TransactionScopeOption将对日志队列客户端的调用封装在事务作用域中来解决此问题。抑制集。这将强制在环境事务之外执行操作。

类似于:

using (var scope = new TransactionScope (TransactionScopeOption.Suppress))
{
// Call to enqueue log message
}

你的理论很有道理。因为您使用的是事务队列,WCF通过在DTC事务中登记消息处理程序内的所有内容来确保事务一致性。这显然包括日志记录消息的排队,如果不需要的话,这是预期的行为。

最新更新