Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Às vezes, um padrão de mensagem/resposta simples não é suficiente e o cliente precisa receber notificações assíncronas. Por exemplo, um usuário pode querer notificação quando um amigo publica uma nova mensagem instantânea.
Observadores de cliente são um mecanismo que permite a notificação assíncrona de clientes. As interfaces dos observadores 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 isso pode incentivar o uso de async void na implementação. Esse é um padrão perigoso, pois pode causar falhas no aplicativo se uma exceção for gerada do método. Em vez disso, para cenários de notificação de melhor esforço, considere aplicar OneWayAttribute ao método de interface do observador. Isso faz com que o receptor não envie uma resposta para a invocação do método e faz com que o método retorne imediatamente no site de chamada, sem aguardar uma resposta do observador. Uma granularidade chama um método em um observador, invocando-o como qualquer método de interface de granularidade. O Orleans runtime garante a entrega de solicitações e respostas. Um caso de uso comum para observadores é inscrever um cliente para receber notificações quando um evento ocorre no Orleans aplicativo. Uma entidade que publica essas notificações deve fornecer uma API para adicionar ou remover observadores. Além disso, geralmente é conveniente expor um método que permite o cancelamento de uma assinatura existente.
Você pode usar uma classe utilitária como ObserverManager<TObserver> para simplificar o desenvolvimento de tipos de grãos observados. Ao contrário dos grãos, que Orleans reativam automaticamente conforme necessário após a 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. Clientes ativos devem reassinar periodicamente para manter suas assinaturas ativas.
Para assinar uma notificação, o cliente deve primeiro criar um objeto local implementando a interface do observador. Em seguida, ele chama um método na fábrica de observadores, CreateObjectReference, para transformar o objeto em uma referência de grain. Em seguida, você pode passar essa referência para o método de assinatura no grain de notificação.
Outros grãos também podem usar esse modelo para receber notificações assíncronas. Os grãos podem implementar interfaces IGrainObserver. Ao contrário do caso de assinatura do cliente, o grão de assinatura 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 componente que envia mensagens periodicamente 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 de IGrainObserver.
Agora, qualquer cliente que queira observar essas mensagens deve implementar uma classe que implemente IChat.
O caso mais simples tem esta aparência:
public class Chat : IChat
{
public Task ReceiveMessage(string message)
{
Console.WriteLine(message);
return Task.CompletedTask;
}
}
No servidor, você deve ter em seguida um grão que envia essas mensagens de chat aos clientes. O módulo também deve fornecer um mecanismo para os clientes assinarem e cancelarem a assinatura de notificações. Para assinaturas, a granularidade pode usar uma instância da classe utilitária ObserverManager<TObserver>.
Observação
ObserverManager<TObserver> faz parte de Orleans desde a versão 7.0. Para versões mais antigas, a implementação a seguir 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 Notify método da ObserverManager<IChat> instância. O método usa um Action<T> método ou uma expressão lambda (onde T está o tipo IChat aqui). Você pode chamar qualquer método na interface para enviá-lo aos clientes. Em nosso caso, temos apenas um método ReceiveMessagee nosso código de envio no servidor tem esta aparência:
public Task SendUpdateMessage(string message)
{
_subsManager.Notify(s => s.ReceiveMessage(message));
return Task.CompletedTask;
}
Agora, nosso servidor tem um método para enviar mensagens para clientes observadores e dois métodos para assinatura/não assinatura. O cliente implementou uma classe capaz de observar as mensagens de granulação. A etapa final é criar uma referência de observador no cliente usando nossa classe implementada Chat anteriormente e deixá-la receber mensagens após a assinatura.
O código terá 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 nosso módulo do servidor chama o método SendUpdateMessage, todos os clientes assinantes recebem a mensagem. No código do cliente, a Chat instância na variável c recebe a mensagem e a envia para o console.
Importante
Objetos passados para CreateObjectReference são mantidos por meio de um WeakReference<T> e, portanto, são coletados como lixo se não houver outras referências.
Você deve manter uma referência para cada observador que não deseja coletar.
Observação
Observadores não são inerentemente confiáveis porque um cliente que hospeda um observador pode falhar e observadores criados após a recuperação têm identidades diferentes (aleatórias). ObserverManager<TObserver> depende da reinscrição periódica dos observadores, como discutido acima, para que possa remover observadores inativos.
Modelo de execução
As implementações de IGrainObserver são registradas por meio 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, para conclusão. 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; você não pode personalizar o modelo de execução.