我有一个使用 NServiceBus 的消息处理程序,它需要在两个不同的数据库上执行 SQL 代码。 连接字符串具有不同的初始目录,但在其他方面是相同的。
拾取消息时,第一个 sql 连接成功打开,但第二个 sql 连接会导致在 时引发以下异常。称为打开。
分布式事务管理器 (MSDTC( 的网络访问已 禁用。请在安全中启用DTC进行网络访问 使用组件服务管理的 MSDTC 配置 工具。
我们不使用 MSDTC。
下面是失败的代码。 它将在connB.Open((上失败
public void Handle(MyMsgCmd message)
{
using (SqlConnection connA = new SqlConnection(myConnectionStringA))
{
connA.Open();
}
using (SqlConnection connB = new SqlConnection(myConnectionStringB))
{
connB.Open();
}
}
从命令行应用程序或 Web 应用程序运行时,相同的代码工作得很好。 仅当从 NServiceBus 调用异常时,才会引发异常。
这些连接中的每一个在第一次打开或自行打开时都会成功打开,但只要存在第二个连接,第二个连接将始终无法打开,即使已知良好,也会出现相同的异常。
使用 NServiceBus 按顺序打开多个连接是否需要其他配置?
默认情况下,NServiceBus 似乎将每个消息处理程序包装在一个事务中,这会导致对同一消息处理程序内不同数据库连接的查询失败,除非启用了 MSDTC
。我可以使用 BusConfiguration.Transactions(( 禁用它。DoNotWrapHandlersExecutionInATransactionScope((
您可以在 NServiceBus 文档中找到有关事务的更多信息。
这并不完全与 NServiceBus 相关,我们只是提供了连接到传输(如 MSMQ、Azure 服务总线等(、持久化器和你自己的数据库的不同方式。
但是,即使没有 NServiceBus,在连接到两个数据库时,您也需要分布式事务,或者确保事务不会升级到分布式事务。问题是,如果没有分布式事务,当一个事务成功提交时,另一个事务可能会失败。结果是您的两个数据库不再同步或一致。
如果存储了数据库 A 中的订单,并在数据库 B 中跟踪了库存,则可以从库存中扣除 1,但由于交易失败,订单可能永远不会存储。您需要在没有分布式事务的情况下自己补偿这一点。
这并不是说分布式交易总是要走的路。您可能没有使用它们,因为您的 DBA 不喜欢它们。MSDTC 始终将可序列化事务放在具有最重锁的数据上。将它们保持打开状态的时间越长,需要等待的并发运行的事务就越多。您的软件中可能存在巨大的性能问题。
另一方面,创建补偿交易可能非常非常困难。想想数据库A可能会失败,而DatabaseB可能会成功。但是消息会发生什么?它从队列中消失了吗?还是会保留在队列中并再次处理?数据库 B 是否会在重复数据的可能结果下再次成功?
幸运的是,您已经在使用 NServiceBus。您可能需要查看发件箱功能,该功能可以帮助解决其中一些问题。