訓練
觀察者
在某些情況下,簡單的訊息/回應模式是不夠的,且用戶端需要接收非同步通知。 例如,當朋友發佈新的即時訊息時,使用者可能會想要收到通知。
用戶端觀察者是允許以非同步方式通知用戶端的機制。 觀察者介面必須繼承自 IGrainObserver,而且所有方法都必須傳回 void
、Task、Task<TResult>、ValueTask 或 ValueTask<TResult>。 不建議使用 void
傳回型別,因為如果方法擲回例外狀況,可能會造成應用程式損毀,因此建議在實作時使用 async void
。 相反地,對於盡力而為的通知情節,請考慮將 OneWayAttribute 套用至觀察者的介面方法。 這會導致接收者不會傳送方法叫用的回應,而且會導致方法在呼叫網站立即傳回,而不會等待觀察者的回應。 粒紋會透過叫用觀察者上的方法來呼叫方法,就像任何粒紋介面方法一樣。 Orleans 執行階段可確保要求和回應的傳遞。 觀察者常見的使用案例是登錄用戶端,以在 Orleans 應用程式中發生事件時接收通知。 發佈這類通知的粒紋應該提供 API 來新增或移除觀察者。 此外,將方法公開通常很方便,這類方法會允許將現有訂用帳戶取消。
粒紋開發人員可以使用公用程式類別,例如 ObserverManager<TObserver>,簡化觀察到的粒紋型別開發。 與在失敗後視需要自動重新啟動的粒紋不同,用戶端無法容錯:失敗的用戶端可能永遠不會復原。
基於這個理由,ObserverManager<T>
公用程式會在設定的持續時間之後移除訂用帳戶。 作用中的用戶端應該在計時器上重新訂閱,使其訂用帳戶保持作用中。
若要訂閱通知,用戶端必須先建立實作觀察者介面的本地物件。 然後其會在觀察者中心 (CreateObjectReference) 上呼叫方法,將物件轉換成粒紋參考,您可以將該粒紋參考傳遞至通知粒紋時所用的訂用帳戶方法。
其他粒紋也可以使用此模型來接收非同步通知。 粒紋也可以實作 IGrainObserver 介面。 與用戶端訂用帳戶案例不同,訂閱粒紋只會實作觀察者介面,並在參考中傳遞至本身 (例如 this.AsReference<IMyGrainObserverInterface>()
)。 因為粒紋已經可定址,所以不需要 CreateObjectReference()
。
假設我們有一個定期將訊息傳送給用戶端的粒紋。 為了簡單起見,範例中的訊息會是字串。 我們會先在接收訊息的用戶端上定義介面。
介面看起來像這樣
public interface IChat : IGrainObserver
{
Task ReceiveMessage(string message);
}
唯一的特殊事項是介面應繼承自 IGrainObserver
。
現在,任何想要觀察那些訊息的用戶端都應該實作實作 IChat
的類別。
最簡單的案例看起來類似如下:
public class Chat : IChat
{
public Task ReceiveMessage(string message)
{
Console.WriteLine(message);
return Task.CompletedTask;
}
}
在伺服器上,我們接下來應該有一個將這些聊天訊息傳送給用戶端的粒紋。 粒紋也應該有一個機制,可讓用戶端自行訂閱和取消訂閱通知。 對於訂用帳戶,粒紋可以使用公用程式類別 ObserverManager<TObserver> 的執行個體。
注意
ObserverManager<TObserver> 自 7.0 版起為 Orleans 的一部分。 對於較舊的版本,可以複製下列實作。
class HelloGrain : Grain, IHello
{
private readonly ObserverManager<IChat> _subsManager;
public HelloGrain(ILogger<HelloGrain> logger)
{
_subsManager =
new ObserverManager<IChat>(
TimeSpan.FromMinutes(5), logger);
}
// Clients call this to subscribe.
public Task Subscribe(IChat observer)
{
_subsManager.Subscribe(observer, observer);
return Task.CompletedTask;
}
//Clients use this to unsubscribe and no longer receive messages.
public Task UnSubscribe(IChat observer)
{
_subsManager.Unsubscribe(observer);
return Task.CompletedTask;
}
}
若要將訊息傳送給用戶端,可以使用 ObserverManager<IChat>
執行個體的 Notify
方法。 此方法會採用 Action<T>
方法或 Lambda 運算式 (其中 T
在此的型別為 IChat
)。 您可以在介面上呼叫任何方法,將其傳送給用戶端。 在案例中,我們只有一個方法 (ReceiveMessage
),而伺服器上的傳送程式碼看起來像這樣:
public Task SendUpdateMessage(string message)
{
_subsManager.Notify(s => s.ReceiveMessage(message));
return Task.CompletedTask;
}
現在,伺服器有方法可將訊息傳送給觀察者用戶端、訂閱/取消訂閱有兩種方法,而用戶端已實作能夠觀察粒紋訊息的類別。 最後一個步驟是使用先前實作的 Chat
類別,在用戶端上建立觀察者參考,並在訂閱之後接收訊息。
程式碼應該會看起來如下:
//First create the grain reference
var friend = _grainFactory.GetGrain<IHello>(0);
Chat c = new Chat();
//Create a reference for chat, usable for subscribing to the observable grain.
var obj = _grainFactory.CreateObjectReference<IChat>(c);
//Subscribe the instance to receive messages.
await friend.Subscribe(obj);
現在,每當伺服器上的粒紋呼叫 SendUpdateMessage
方法時,所有已訂閱的用戶端都會收到訊息。 在用戶端程式碼中,變數 c
中的 Chat
執行個體將會收到訊息,並將其輸出至主控台。
重要
傳遞至 CreateObjectReference
的物件會透過 WeakReference<T> 保留,因此如果沒有任何其他參考存在,則會進行記憶體回收。
使用者應該針對不想要收集的每個觀察者維護參考。
注意
觀察者本質上是不可靠的,因為裝載觀察者的用戶端可能會失敗,且復原後所建立的觀察者有不同的 (隨機化) 身分識別。 如上所述,ObserverManager<TObserver> 依賴觀察者的定期重新訂閱,以便可以移除非作用中的觀察者。
IGrainObserver 的實作是透過呼叫 IGrainFactory.CreateObjectReference 來註冊,而該方法的每個呼叫都會建立指向該實作的新參考。 Orleans 將會執行向每個參考傳送的要求,逐一傳送至完成。 觀察者是不可重入的,因此 Orleans 不會將對觀察者的並行要求交錯。 如果有多個觀察者同時接收要求,可以平行執行這些要求。 觀察者方法的執行不受 ReentrantAttribute 或 AlwaysInterleaveAttribute 之類的屬性影響:開發人員無法自訂執行模型。