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