COM+事务管理深入解析
立即解锁
发布时间: 2025-08-24 01:52:24 阅读量: 1 订阅数: 4 

# COM+ 事务管理与补偿资源管理器详解
在软件开发领域,事务管理是确保数据一致性和完整性的关键环节。对于分布式系统而言,高效且可靠的事务处理尤为重要。COM+ 作为一种基础设施,在事务管理方面提供了强大的支持,下面我们将深入探讨其相关机制。
## 1. 资源管理器(RM)
资源管理器(Resource Manager,RM)是 COM+ 中用于访问和修改资源持久状态的软件组件。它对特定类型的资源(如关系型数据库)有深入了解,并在事务影响下跟踪资源的变化。若事务中止,RM 可将资源恢复到原始状态。常见的商用 RM 包括适用于 Microsoft SQL Server、Oracle、IBM DB2、Informix 和 Sybase 等数据库的版本,模拟程序中使用的 MSDE 数据库也提供了自己的 RM。
当客户端实例化 RM 时,会获得一个 RM 代理,如 OLE DB 驱动程序和 ODBC 驱动程序。RM 代理提供访问 RM 的 API,通常为 COM 接口,但并非强制要求。此外,RM 代理通常作为资源分配器(Resource Dispenser,RD)的一部分实现,RD 负责管理资源的非持久状态,如资源的连接数量。
## 2. 分布式事务协调器(DTC)
分布式事务协调器(Distributed Transaction Coordinator,DTC)用于协调可能分布在网络中的事务,它管理资源管理器,并根据事务结果通知参与的 RM 是提交还是中止对资源的更改。每个需要使用事务的系统都必须安装 DTC,安装 MS SQL Server 或 MSDE 时会自动安装 MS-DTC。
非事务性客户端可使用 SDK API `DtcGetTransactionManager` 获取 DTC 并显式请求开始新事务,示例代码如下:
```cpp
CComPtr<ITransactionDispenser> spTxDisp;
HRESULT hr = DtcGetTransactionManager(
NULL, // host name
NULL, // TM name
__uuidof(ITransactionDispenser), // interface
0, // reserved
0, // reserved
0, // reserved
(void**) &spTxDisp); // [out] pointer
CComPtr<ITransaction> spTx;
hr = spTxDisp->BeginTransaction(
NULL, // outer component
ISOLATIONLEVEL_ISOLATED, // Isolation level
ISOFLAG_RETAIN_DONTCARE, // Isolation flag
NULL, // Options
&spTx); // [out] pointer
... // Enlist RMs and perform resource updates
if (bSuccess) {
spTx->Commit(0, XACTTC_SYNC_PHASEONE, 0);
}else {
spTx->Abort(NULL, 0, FALSE);
}
```
这种让非事务性组件手动处理事务的机制称为 Bring Your Own Transaction(BYOT),可用于手动创建具有任意长超时时间的事务。
DTC 以 `XACTUOW` 类型的 C 结构唯一标识每个事务,客户端可通过调用 `ITransaction::GetTransactionInfo` 获取该标识,COM+ 会将其重新解释为 GUID。
### 2.1 两阶段提交(Two-Phase Commit)
为确保事务的原子性,DTC 要求每个 RM 分两个阶段进行提交:准备阶段和提交阶段。
- **准备阶段**:RM 需确保提交阶段不会失败,处理可能出现的内部问题并返回相应错误。若一切正常,RM 保存状态信息并返回成功状态。
- **提交阶段**:RM 应用保存的状态信息,使更改永久生效,此阶段通常不应失败,除非遇到严重故障(如停电)。
两阶段提交的算法流程如下:
```mermaid
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;
A([客户端请求 DTC 提交事务]):::startend --> B( DTC 向每个已登记的 RM 发送准备请求):::process
B --> C{是否有 RM 返回失败状态?}:::decision
C -- 是 --> D( DTC 通知其余 RM 中止更改):::process
C -- 否 --> E( DTC 请求每个 RM 提交更改):::process
```
## 3. COM+ 自动事务支持
配置的组件通过事务属性表明参与事务的意愿。当此类组件的对象被激活时,COM+ 会设置对象的上下文以处理事务。COM+ 在以下两种情况下自动开始事务:
1. 非事务性客户端激活其组件的事务属性设置为 `TRANSACTION_REQUIRED` 或 `TRANSACTION_REQUIRES_NEW` 的对象。
2. 事务性客户端调用其组件的事务属性设置为 `TRANSACTION_REQUIRES_NEW` 的对象。
负责开始新事务的对象称为该事务的根对象,具有特殊作用。标记为 `TRANSACTION_REQUIRES_NEW` 的对象始终是根对象。根对象激活时,COM+ 会向 DTC 请求新事务,DTC 返回 `ITransaction` 指针,COM+ 将其存储在对象的上下文中。
在事务边界内激活且标记为 `TRANSACTION_REQUIRED` 或 `TRANSACTION_SUPPORTED` 的对象将共享该事务,一组共享事务的上下文称为事务流。为确保事务流中的上下文同时仅共享一个事务,COM+ 要求需要事务的组件也需要同步。
若对象参与事务,可从其上下文中获取事务 ID,示例代码如下:
```cpp
CComPtr<IObjectContextInfo> spInfo;
HRESULT hr = CoGetObjectContext(__uuidof(IObjectContextInfo),
(void**) &spInfo);
_ASSERT (SUCCEEDED(hr));
GUID tid;
hr = spInfo->GetTransactionId(&tid);
_ASSERT (SUCCEEDED(hr));
```
需要注意的是,COM+ 返回的事务 ID 是 GUID 而非 `XACTUOW` 结构。
事务性对象首次访问事务性资源时,数据访问层(如 ODBC 和 OLE DB)会自动访问上下文的事务,并将相应的 RM 登记到 DTC。每个参与事务的组件通过调用 `IContextState::SetMyTransactionVote` 进行投票。事务在根对象停用或超过超时阈值时完成,COM+ 会根据对象的投票结果决定是提交还是中止事务。
### 3.1 事务的生命周期
考虑以下 VBScript 代码:
```vbscript
set TradeMgr = CreateObject("TradeMgmt.TradeMgr")
TradeMgr.BuyStocks "Don", "INTC", 100
TradeMgr.BuyStocks "Chris", "MSFT", 1000
TradeMgr = NULL
```
由于 Chris 资金不足,第二次 `BuyStocks` 调用失败,导致第一次调用的更改也被回滚,因为两个调用属于同一事务。为解决此问题,可使用 COM+ 提供的即时激活(Just-In-Time,JIT)机制。
JIT 允许 COM+ 在基础客户端未释放对象时停用对象,并在客户端调用方法时透明地重新激活对象。COM+ 会为需要事务的组件自动启用 JIT 激活和同步。对象的上下文中有一个“完成”位(即返回时停用位),COM+ 在每次方法调用返回后检查该位,若该位为 `TRUE`,则停用对象。可通过 `IContextState::SetDeactivateOnReturn` 方法编程设置该位,示例代码如下:
```cpp
STDMETHODIMP CTradeMgr::BuyStocks(BSTR bsClient, BSTR bsSymbol,
long lShares)
{
CComPtr<IContextState> spState;
HRESULT hr = ::CoGetObjectContext(__uuidof(IContextState),
(void**) &spState);
if (FAILED(hr)) {
return hr;
}
hr = spState->SetDeactivateOnReturn(VARIANT_TRUE);
_ASSERT (SUCCEEDED(hr));
try {
//
// First operation - Obtain the stocks.
//
IStockMgrPtr spStockMgr(__uuidof(StockMgr));
long lAmount = spStockMgr->BuyStock(bsSymbol, lShares);
//
// Second operation - Debit the clien't account balance
//
IAccountMgrPtr spAccountMgr(__uuidof(AccountMgr));
spAccountMgr->Debit(bsClient, lAmount);
}catch(_com_error& e) {
spState->SetMyTransactionVote(TxAbort);
return Error(static_cast<LPCTSTR>(e.Description()),
GUID_NULL, e.Error());
}
spState->SetMyTransactionVote(TxCommit);
return S_OK;
}
```
通过这种方式,可确保每次 `BuyStocks` 调用都在独立的事务中执行。
## 4. 手动事务处理
虽然 COM+ 自动管理事务可简化组件开发,但有时基础客户端希望控制事务的结果。为此,COM+ 提供了 `TransactionContext` 类,通过 `TxCtx.TransactionObject` 的 PROGID 表示。
`TransactionContext` 对象支持 `ITransactionContext` 接口,其定义及方法说明如下:
```cpp
ITransactionContext : IDispatch
{
HRESULT CreateInstance([in] BSTR pszProgId,
[retval][out] VARIANT *pObject); // instantiate an object
HRESULT Commit(); // commit a transaction
HRESULT Abort(); // abort a transaction
};
```
基础客户端可通过调用 `ITransactionContext` 接口的方法开始事务、组合一个或多个 COM+ 组件的工作,并显式提交或中止事务,示例 VBScript 代码如下:
```vbscript
Dim txCtx
Set txCtx = CreateObject("TxCtx.TransactionContext")
Dim Accounts
set Accounts = txCtx.CreateInstance("AccountMgmt.AccountMgr")
Accounts.Debit "Don", 10
txCtx.Commit
msgbox "Done"
```
需要注意的是,通过 `ITransactionContext::CreateInstance` 激活的对象应属于 COM+ 配置的组件,每个激活的对象需使用上下文对象进行事务投票,基础客户端也可参与投票过程。同时,要区分对象上下文和事务上下文,对象上下文与单个对象相关,而事务上下文与整个事务相关。
综上所述,COM+ 在事务管理方面提供了丰富的功能和灵活的机制,无论是自动事务处理还是手动事务控制,都能满足不同场景的需求。通过合理运用这些机制,开发者可以构建出高效、可靠的分布式应用程序。
## 5. 补偿资源管理器(CRM)
资源管理器需要通过 ACID 测试,保证原子性、一致性、隔离性和持久性。由于不同 RM 有很多功能是共通的,COM+ 提供了一个框架来开发 RM,使用该框架开发的 RM 称为补偿资源管理器(Compensating Resource Manager,CRM)。
### 5.1 CRM 的组成
CRM 由两个协作的组件组成:CRM 工作器(CRM worker)和 CRM 补偿器(CRM compensator)。
- **CRM 工作器**:向客户端公开必要的 COM 对象,当客户端请求修改资源时,工作器使用 CRM 的服务记录更改。
- **CRM 补偿器**:读取 CRM 服务提供的记录更改,并决定是提交还是中止这些更改。
两者之间没有直接通信,唯一需要传递的数据是应用于资源的更改序列。为了便于存储更改序列,COM+ 提供了 CRM 书记员(CRM clerk)组件。CRM 工作器实例化 CRM 书记员并开始记录更改,事务结束时,COM+ 启动 CRM 补偿器并播放记录序列。
### 5.2 CRM 书记员接口
CRM 书记员支持 `ICrmLogControl` 接口,其原型
0
0
复制全文
相关推荐









