我已经构建了一个处理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>
一切正常。当数据库不可用时,回滚会使队列条目放入我配置为每7分钟每10分钟重试一次的retry子队列中。一切正常,并且已经投入生产了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.
这一切也都可以正常工作...直到...数据库不可用并且事务被回滚。
回滚将发生在调用proxy.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.'
如果将proxy.LogTransaction移至qTransaction.Rollback之前,则日志条目永远不会放入LogQueue。
我的工作原理是WCF考虑对两个队列的操作:从QueueService队列读取并写入LogQueue,作为单个事务。因此,如果在回滚后尝试写入LogQueue,则事务已经结束,但是如果在调用回滚之前对LogQueue进行写入,则对队列的写入也会回滚。
有什么方法可以保留回滚queueService事务而不同时回滚LogService事务的能力?
答案 0 :(得分:1)
我认为您可以通过将调用包装到具有TransactionScopeOption.Suppress设置的事务范围中的日志队列客户端来解决此问题。这将迫使该动作发生在环境事务之外。
类似的东西:
db.getCollection('emps').aggregate([
{$group: {_id: null, Sal: {$avg: "$Sal"}}}
]).forEach(function(dd){
db.getCollection('emps').find({Sal: {$gte: dd.Sal}}, {Ename: 1, _id: -1}).forEach(function(emp_name){print(emp_name)})
})
您的理论很有意义。因为您使用的是事务队列,所以WCF通过在DTC事务中征服消息处理程序内的所有内容来确保事务一致性。显然,这包括日志消息的入队,并且如果不希望出现的话,则是预期的行为。