如何在 gRPC 上实现服务器“回调”

时间:2021-03-03 09:49:29

标签: .net grpc protobuf-net

我在 gRPC 上制作了一个 POC,看看它可以做什么并在 .Net 5 上迁移 WCF 应用程序

POC 是一个简单的聊天,发送新消息,所有客户端都收到消息

共享代码

public interface IChatService
{
    IAsyncEnumerable<MessageResponse> Subscribe();
    ValueTask PostMessage(MessageRequest msg);
}

[ProtoContract]
public class MessageRequest
{
    public MessageRequest(string user, string msg)
    {
        User = user;
        Msg = msg;
    }

    [ProtoMember(1)]
    public string Msg { get; }

    [ProtoMember(2)]
    public string User { get; }
}

[ProtoContract]
public class MessageResponse
{
    public MessageResponse(string user, string msg)
    {
        User = user;
        Msg = msg;
        Date = DateTime.Now;
    }

    [ProtoMember(1)]
    public string Msg { get; }

    [ProtoMember(2)]
    public string User { get; }

    [ProtoMember(2)]
    public DateTime Date { get; }
}

客户端代码:

        using var http = GrpcChannel.ForAddress("http://localhost:10042");

        var chatService = http.CreateGrpcService<IChatService>();

        await foreach (var msg in chatService.Subscribe())
        {
            Console.WriteLine($"{msg.Date} : {msg.User} - {msg.Msg}");
        }

        await chatService.PostMessage(new MessageRequest("User1", "MyMessage"));

服务端代码:

public class ChatService : IChatService
{
    private static readonly List<MessageResponse> OldMessages = new List<MessageResponse>();

    public async IAsyncEnumerable<MessageResponse> Subscribe()
    {
        foreach (var msg in OldMessages)
        {
            await Task.Delay(10);
            yield return msg;
        }
    }

    public async ValueTask PostMessage(MessageRequest msg)
    {
        await Task.Delay(10);
        OldMessages.Add(new MessageResponse(msg.User, msg.Msg));
    }
}

在这个想法中,当有新消息发布时,订阅方法发送到客户端 和 PostMessage 方法在聊天中添加消息(很明显)

但我不明白如何在服务器端代码中将消息“添加”到 Subscribe 的 IAsyncEnumerable 返回? 使用 proto,它会是这样的:

service ChatService {
  rpc Subscribe (request) returns (stream response);
}

感谢您的帮助!

1 个答案:

答案 0 :(得分:0)

目前,您正在对列表使用简单的 foreach 循环。如果您 .Add 到此列表,foreach 将中断,并且不会返回更多数据。您本质上需要的是一个线程安全的启用异步队列机制,它是设计用于生产者/消费者场景的。幸运的是,Channel<T> 提供了这样的机制。但是,您仍然需要将数据推送到所有订阅者实例中,这可能是更大的挑战 - 通常情况下您不想通知调用 .Add

相关问题