Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Às vezes, um padrão simples de mensagem/resposta não é suficiente, e o cliente precisa receber notificações assíncronas. Por exemplo, um usuário pode querer receber uma notificação quando um amigo publicar uma nova mensagem instantânea.
Os observadores de clientes são um mecanismo que permite a notificação assíncrona de clientes. As interfaces de observador devem herdar de IGrainObserver, e todos os métodos devem retornar void, Task, Task<TResult>, ValueTask, ou ValueTask<TResult>. Não recomendamos um tipo de retorno de void porque ele pode incentivar o uso de async void na implementação. Este é um padrão perigoso, pois pode causar falhas na aplicação se uma exceção for lançada pelo método. Em vez disso, para cenários de notificação em que se aplica o melhor esforço, considere aplicar o OneWayAttribute ao método de interface do observador. Isso faz com que o recetor não envie uma resposta para a invocação do método e faz com que o método retorne imediatamente no local da chamada, sem esperar por uma resposta do observador. Um grão chama um método em um observador invocando-o como qualquer método de interface de grão. O Orleans ambiente de execução garante a entrega de solicitações e respostas. Um caso de uso comum para observadores é inscrever um cliente para receber notificações quando ocorre um evento no Orleans aplicativo. Um módulo gerador dessas notificações deve fornecer uma API para adicionar ou remover observadores. Além disso, geralmente é conveniente expor um método que permita o cancelamento de uma assinatura existente.
Você pode usar uma classe de utilidade como ObserverManager<TObserver> para simplificar o desenvolvimento de tipos de grãos observados. Ao contrário dos grãos, que Orleans são reativados automaticamente conforme necessário após uma falha, os clientes não são tolerantes a falhas: um cliente que falha pode nunca se recuperar. Por esse motivo, o ObserverManager<T> utilitário remove assinaturas após uma duração configurada. Os clientes ativos devem renovar automaticamente após um intervalo para manter as suas assinaturas ativas.
Para assinar uma notificação, o cliente deve primeiro criar um objeto local implementando a interface do observador. Em seguida, CreateObjectReferencechama um método na fábrica do observador, ', para transformar o objeto em uma referência de grão. Em seguida, pode-se passar essa referência para o método de subscrição no grain de notificação.
Outros grãos também podem usar esse modelo para receber notificações assíncronas. Grãos podem implementar IGrainObserver interfaces. Em vez do caso de assinatura do cliente, o grão subscritor simplesmente implementa a interface do observador e passa uma referência para si mesmo (por exemplo, this.AsReference<IMyGrainObserverInterface>()). Não há necessidade de CreateObjectReference() porque os grãos já são endereçáveis.
Exemplo de código
Vamos supor que você tenha um grão que periodicamente envia mensagens aos clientes. Para simplificar, a mensagem em nosso exemplo é uma cadeia de caracteres. Primeiro, defina a interface no cliente que recebe a mensagem.
A interface tem esta aparência:
public interface IChat : IGrainObserver
{
Task ReceiveMessage(string message);
}
O único requisito especial é que a interface deve herdar do IGrainObserver.
Qualquer cliente que queira observar essas mensagens deve implementar uma classe que implemente IChat.
O caso mais simples é mais ou menos assim:
public class Chat : IChat
{
public Task ReceiveMessage(string message)
{
Console.WriteLine(message);
return Task.CompletedTask;
}
}
No servidor, você deve ter um componente que envia essas mensagens de bate-papo para os clientes. O módulo também deve fornecer um mecanismo para os clientes se inscreverem e cancelarem a subscrição de notificações. Para assinaturas, o grão pode usar uma instância da classe de utilitário ObserverManager<TObserver>.
Observação
ObserverManager<TObserver> faz parte do Orleans desde a versão 7.0. Para versões mais antigas, a seguinte implementação pode ser copiada.
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;
}
}
Para enviar uma mensagem aos clientes, use o método Notify da instância ObserverManager<IChat>. O método usa um Action<T> método ou expressão lambda (onde T é do tipo IChat aqui). Você pode chamar qualquer método na interface para enviá-lo aos clientes. No nosso caso, temos apenas um método, ReceiveMessagee o nosso código de envio no servidor tem o seguinte aspeto:
public Task SendUpdateMessage(string message)
{
_subsManager.Notify(s => s.ReceiveMessage(message));
return Task.CompletedTask;
}
Agora, o nosso servidor tem um método para enviar mensagens para clientes observadores e dois métodos para subscrever/cancelar a subscrição. O cliente implementou uma classe capaz de observar as mensagens do 'grain'. A etapa final é criar uma referência de observador no cliente usando nossa classe implementada Chat anteriormente e deixá-lo receber mensagens após a assinatura.
O código tem esta aparência:
//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);
Agora, sempre que o nosso grão no servidor chama o método SendUpdateMessage, todos os clientes registados recebem a mensagem. Em nosso código de cliente, a Chat instância na variável c recebe a mensagem e a envia para o console.
Importante
Os objetos passados para CreateObjectReference são mantidos através de um WeakReference<T> e, portanto, são recolhidos pelo processo de recolha de lixo se não existirem outras referências.
Você deve manter uma referência para cada observador que você não quer coletar.
Observação
Os observadores são inerentemente não confiáveis porque um cliente que hospeda um observador pode falhar, e os observadores criados após a recuperação têm identidades diferentes (aleatórias). ObserverManager<TObserver> depende da reinscrição periódica por parte dos observadores, como discutido acima, para que possa remover observadores inativos.
Modelo de execução
As implementações de IGrainObserver são registadas através de uma chamada para IGrainFactory.CreateObjectReference. Cada chamada para esse método cria uma nova referência apontando para essa implementação. Orleans executa solicitações enviadas a cada uma dessas referências, uma a uma, até a conclusão. Os observadores não são reentrantes; portanto, Orleans não intercala solicitações simultâneas a um observador. Se vários observadores receberem solicitações simultaneamente, essas solicitações poderão ser executadas em paralelo. Atributos como AlwaysInterleaveAttribute ou ReentrantAttribute não afetam a execução de métodos de observador, não é possível personalizar o modelo de execução.