共用方式為


Reliable Services 通知

通知可讓用戶端追蹤對於他們感興趣物件所進行的變更。 兩種類型的物件支援通知:「可靠的狀態管理員」和「可靠的字典」

使用通知的常見原因如下:

  • 建置具體化檢視 (例如次要索引) 或複本狀態的彙總篩選檢視。 可靠的字典中所有索引鍵的排序索引便是其中一個例子。
  • 傳送監視資料,例如過去一小時內新增的使用者數目。

套用作業的過程中會引發通知。 在主要複本上,transaction.CommitAsync()this.StateManager.GetOrAddAsync() 中在仲裁通知之後會套用作業。 在次要複本上,複寫佇列資料處理時會套用作業。 因為如此,應該以最快速度處理通知,且同步事件不應包含任何耗費資源的作業。 否則,可能對交易處理時間和複本建立造成負面影響。

可靠的狀態管理員通知

可靠的狀態管理員提供下列事件的通知︰

  • 交易
    • 提交
  • 狀態管理員
    • 重建
    • 加入可靠的狀態
    • 移除可靠的狀態

可靠狀態管理員會追蹤目前傳遞的交易。 唯一會造成通知引發的交易狀態變更是認可交易。

可靠的狀態管理員會維護可靠狀態的集合,例如可靠的字典和可靠的佇列。 可靠的狀態管理員會在此集合變更時引發通知︰加入或移除可靠的狀態,或者重建整個集合時。 可靠的狀態管理員集合會在下列三種情況下重建:

  • 復原︰當複本啟動時,它會從磁碟復原為先前的狀態。 復原結束時,它會利用 NotifyStateManagerChangedEventArgs 引發事件,其中包含一組復原的可靠狀態。
  • 完整備份︰在複本可加入組態集之前,必須先建置它。 有時,這需要主要複本的可靠狀態管理員的狀態完整複本,以套用到閒置的次要複本。 次要複本上的可靠狀態管理員會使用 NotifyStateManagerChangedEventArgs 引發事件,其中包含一組它從主要複本取得的可靠狀態。
  • 還原︰在災害復原案例中,複本的狀態可經由 RestoreAsync從備份還原。 在這類案例中,主要複本上的可靠狀態管理員會使用 NotifyStateManagerChangedEventArgs 引發事件,其中包含一組它從備份還原來的可靠狀態。

若要註冊交易通知及/或狀態管理員通知,您必須向可靠的狀態管理員分別註冊 TransactionChangedStateManagerChanged 事件。 註冊這些事件處理常式的常見位置是您的具狀態服務的建構函式。 當您註冊建構函式時,您也不會錯過 IReliableStateManager存留期間的變更所造成的任何通知。

public MyService(StatefulServiceContext context)
    : base(MyService.EndpointName, context, CreateReliableStateManager(context))
{
    this.StateManager.TransactionChanged += this.OnTransactionChangedHandler;
    this.StateManager.StateManagerChanged += this.OnStateManagerChangedHandler;
}

TransactionChanged 事件處理常式會使用 NotifyTransactionChangedEventArgs ,來提供關於事件的詳細資料。 它包含指定變更類型的 Action 屬性 (例如, NotifyTransactionChangedAction.Commit)。 它也包含提供「指向變更之交易的參考」的交易屬性。

注意

現在,只有認可交易才會引發 TransactionChanged 事件。 此動作等於 NotifyTransactionChangedAction.Commit。 但是在未來,可能會有其他類型的交易狀態變更可以引發事件。 我們建議您檢查動作,並只在您預期的事件發生時處理事件。

以下是範例 TransactionChanged 事件處理常式。

private void OnTransactionChangedHandler(object sender, NotifyTransactionChangedEventArgs e)
{
    if (e.Action == NotifyTransactionChangedAction.Commit)
    {
        this.lastCommitLsn = e.Transaction.CommitSequenceNumber;
        this.lastTransactionId = e.Transaction.TransactionId;

        this.lastCommittedTransactionList.Add(e.Transaction.TransactionId);
    }
}

StateManagerChanged 事件處理常式使用 NotifyStateManagerChangedEventArgs 來提供事件的相關詳細資料。 NotifyStateManagerChangedEventArgs 有兩個子類別︰NotifyStateManagerRebuildEventArgsNotifyStateManagerSingleEntityChangedEventArgs。 您使用 NotifyStateManagerChangedEventArgs 中的 Action 屬性將 NotifyStateManagerChangedEventArgs 轉換為正確的子類別︰

  • NotifyStateManagerChangedAction.RebuildNotifyStateManagerRebuildEventArgs
  • NotifyStateManagerChangedAction.AddNotifyStateManagerChangedAction.RemoveNotifyStateManagerSingleEntityChangedEventArgs

以下是範例 StateManagerChanged 通知處理常式。

public void OnStateManagerChangedHandler(object sender, NotifyStateManagerChangedEventArgs e)
{
    if (e.Action == NotifyStateManagerChangedAction.Rebuild)
    {
        this.ProcessStateManagerRebuildNotification(e);

        return;
    }

    this.ProcessStateManagerSingleEntityNotification(e);
}

可靠的字典通知

可靠的字典提供下列事件的通知︰

  • 重建:當 ReliableDictionary 從復原或複製的本機狀態或備份復原至可讀取的中繼復原狀態時呼叫。 建立此復原狀態之後,將會套用交易記錄,再完成重建。 運用那些記錄,將提供清除、新增、更新和/或移除通知。
  • 清除︰在透過 ClearAsync 方法清除 ReliableDictionary 的狀態時呼叫。
  • 加入︰在已將項目加入至 ReliableDictionary時呼叫。
  • 更新︰在已更新 IReliableDictionary 中的項目時呼叫。
  • 移除︰在已刪除 IReliableDictionary 中的項目時呼叫。

若要註冊可靠的字典通知,使用者必須在 IReliableDictionary 上註冊事件處理常式 DictionaryChanged。 註冊這些事件處理常式的常見位置是在 ReliableStateManager.StateManagerChanged 加入通知中。 在將 IReliableDictionary 加入 IReliableStateManager 時註冊,可確保您不會錯過任何通知。

private void ProcessStateManagerSingleEntityNotification(NotifyStateManagerChangedEventArgs e)
{
    var operation = e as NotifyStateManagerSingleEntityChangedEventArgs;

    if (operation.Action == NotifyStateManagerChangedAction.Add)
    {
        if (operation.ReliableState is IReliableDictionary<TKey, TValue>)
        {
            var dictionary = (IReliableDictionary<TKey, TValue>)operation.ReliableState;
            dictionary.RebuildNotificationAsyncCallback = this.OnDictionaryRebuildNotificationHandlerAsync;
            dictionary.DictionaryChanged += this.OnDictionaryChangedHandler;
        }
    }
}

注意

ProcessStateManagerSingleEntityNotification 是前述 OnStateManagerChangedHandler 範例所呼叫的範例方法。

上述程式碼會設定 IReliableNotificationAsyncCallback 介面以及 DictionaryChanged。 由於 NotifyDictionaryRebuildEventArgs 包含需要以非同步方式列舉的 IAsyncEnumerable 介面,因此會透過 RebuildNotificationAsyncCallback 而不是 OnDictionaryChangedHandler 來引發重建通知。

public async Task OnDictionaryRebuildNotificationHandlerAsync(
    IReliableDictionary<TKey, TValue> origin,
    NotifyDictionaryRebuildEventArgs<TKey, TValue> rebuildNotification)
{
    this.secondaryIndex.Clear();

    var enumerator = e.State.GetAsyncEnumerator();
    while (await enumerator.MoveNextAsync(CancellationToken.None))
    {
        this.secondaryIndex.Add(enumerator.Current.Key, enumerator.Current.Value);
    }
}

注意

在上述程式碼中,於處理重建通知的過程中,會先清除所維護的彙總狀態。 因為正在利用新狀態重建可靠的集合,因此與先前的所有通知無關。

DictionaryChanged 事件處理常式會使用 NotifyDictionaryChangedEventArgs 來提供關於事件的詳細資料。 NotifyDictionaryChangedEventArgs 有五個子類別。 使用 NotifyDictionaryChangedEventArgs 中的 Action 屬性將 NotifyDictionaryChangedEventArgs 轉換為正確的子類別:

  • NotifyDictionaryChangedAction.RebuildNotifyDictionaryRebuildEventArgs
  • NotifyDictionaryChangedAction.ClearNotifyDictionaryClearEventArgs
  • NotifyDictionaryChangedAction.AddNotifyDictionaryItemAddedEventArgs
  • NotifyDictionaryChangedAction.UpdateNotifyDictionaryItemUpdatedEventArgs
  • NotifyDictionaryChangedAction.RemoveNotifyDictionaryItemRemovedEventArgs
public void OnDictionaryChangedHandler(object sender, NotifyDictionaryChangedEventArgs<TKey, TValue> e)
{
    switch (e.Action)
    {
        case NotifyDictionaryChangedAction.Clear:
            var clearEvent = e as NotifyDictionaryClearEventArgs<TKey, TValue>;
            this.ProcessClearNotification(clearEvent);
            return;

        case NotifyDictionaryChangedAction.Add:
            var addEvent = e as NotifyDictionaryItemAddedEventArgs<TKey, TValue>;
            this.ProcessAddNotification(addEvent);
            return;

        case NotifyDictionaryChangedAction.Update:
            var updateEvent = e as NotifyDictionaryItemUpdatedEventArgs<TKey, TValue>;
            this.ProcessUpdateNotification(updateEvent);
            return;

        case NotifyDictionaryChangedAction.Remove:
            var deleteEvent = e as NotifyDictionaryItemRemovedEventArgs<TKey, TValue>;
            this.ProcessRemoveNotification(deleteEvent);
            return;

        default:
            break;
    }
}

建議

  • 完成通知事件。
  • 執行任何耗費資源的作業 (例如 IO 作業) 做為同步事件的一部分。
  • Action 類型。 未來可能會加入新的 Action 類型。

以下是要牢記在心的一些事項:

  • 通知會在執行作業過程中引發。 例如,在還原作業的最後一步引發還原通知。 處理通知事件之前,不會繼續還原。
  • 因為通知是在套用作業過程中引發,因此,用戶端只會看見本機認可作業的通知。 而且因為作業只保證會在本機認可 (亦即記錄),所以它們不一定可在未來復原。
  • 在取消復原路徑上,會針對每個套用的作業引發單一通知。 這表示,如果交易 T1 包含 Create(X)、Delete(X)、Create(X),您將依序得到一個針對 X 建立的通知、一個針對刪除的通知,然後再收到一個建立的通知。
  • 如果是包含多個作業的交易,作業將依使用者在主要本上收到它們的順序套用。
  • 在處理錯誤過程中,次要複本上可能復原某些作業。 通知會針對這類復原作業加以引發,將複本狀態輪換回到可靠的時間點。 復原通知的一個重要差異,是使用重複索引鍵的事件會彙總在一起。 例如,如果復原上述的 T1,使用者將會看到 Delete(X) 的單一通知。

下一步

已知問題

  • 在特定情況下,某些交易通知可能會在重建期間遭到略過。 在此案例中,仍存在正確的值,而且仍可讀取或查看;僅缺少通知。 為了復原記憶體內部狀態,可靠的集合會使用定期壓縮到檢查點檔案的預先寫入記錄。 在還原期間,會先從觸發重建通知的檢查點檔案載入基底狀態。 然後會套用在記錄中儲存的交易,每個交易都會觸發自己的清除、新增、更新或移除通知。 此問題可能來自於在還原後,快速採用新檢查點的競爭條件。 如果檢查點在運用記錄之前完成,將會把記憶體內部狀態設定為新檢查點的狀態。 雖然這會產生正確的狀態,但表示尚未套用之記錄中的交易將不會傳送通知。