c# 使用 gRPC-Web 和 Blazor 构建高并发前端应用

来源:这里教程网 时间:2026-02-21 17:38:33 作者:

Blazor WebAssembly 能直接调用 gRPC-Web 吗?

能,但必须用

Grpc.Net.Client.Web
+
HttpClient
包装器,原生
GrpcChannel
不支持浏览器环境。Blazor Server 模式下反而不能用 gRPC-Web(它走 SignalR,gRPC-Web 是 HTTP/1.1 + base64 编码的二进制流),这点容易混淆。

关键点:

只在 Blazor WebAssembly 项目中启用 gRPC-Web,且服务端必须启用
MapGrpcWeb
客户端需注册
WebGrpcChannel
,不是
GrpcChannel.ForAddress
务必引用
Grpc.Net.Client.Web
2.49.0+(旧版对 WASM 的 streaming 支持不稳)

服务端如何正确暴露 gRPC-Web 接口?

ASP.NET Core 6+ 默认不启用 gRPC-Web,即使你加了

AddGrpc
也不行。必须显式配置中间件和终结点映射。

常见错误是只配了

app.MapGrpcService<myservice>()</myservice>
却没加
.EnableGrpcWeb()
,导致前端报
415 Unsupported Media Type
或空响应。

立即学习“前端免费学习笔记(深入)”;

服务端

Program.cs
关键片段:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddGrpc();
builder.Services.AddGrpcWeb(); // ← 必须加这一行
var app = builder.Build();
app.UseGrpcWeb(); // ← 必须加这一行,且要在 UseRouting() 之后、UseEndpoints() 之前
app.MapGrpcService<GreeterService>().EnableGrpcWeb(); // ← EnableGrpcWeb() 不能少
app.MapFallbackToFile("index.html");

注意:如果用了 CORS,

WithOrigins
必须包含 Blazor WASM 的实际部署地址(如
https://app.example.com
),不能写
"*"
—— gRPC-Web 的 preflight 请求会失败。

WASM 客户端调用时为什么 Streaming 总卡住或报错?

Blazor WebAssembly 的

HttpClient
底层基于 Fetch API,而 Fetch 对 server-sent events(SSE)和流式 gRPC-Web 响应支持有限。默认行为是等整个响应体收完才触发回调,导致
AsyncStreamReader
无法实时读取。

解决方法只有两个:

服务端改用
CallOptions
设置
Deadline
MaxReceiveMessageSize
,避免超长流阻塞
客户端必须用
WebChannelCredentials.None
(gRPC-Web 不支持 TLS 双向认证),且禁用压缩:
new GrpcChannelOptions { HttpHandler = new GrpcWebHandler(GrpcWebMode.GrpcWebText, new HttpClientHandler()) }
流式方法返回类型必须是
IAsyncEnumerable<t></t>
,不能用
Task<t></t>
混用

典型错误日志:

System.InvalidOperationException: The gRPC call was cancelled
—— 多半是流未及时消费或浏览器主动中断连接。

高并发下如何避免 Blazor WASM 端内存暴涨?

每个 gRPC-Web 调用都会创建独立的

GrpcChannel
实例(尤其在组件内 new 出来时),而 WASM 的 GC 不会立即回收大块二进制 buffer,连续发起 50+ 并发流请求极易触发 OOM。

实操建议:

全局单例复用
GrpcChannel
:在
Program.cs
中注册为
Singleton
,注入到组件中使用
流式调用务必手动
DisposeAsync()
或用
using await
,否则底层
HttpClient
连接不会释放
避免在
@onclick
中直接启动长生命周期流;改用状态标记 +
CancellationTokenSource
控制启停
服务端开启
KeepAlive
Http2KeepAlivePingInterval
)可减少连接重建开销,但 WASM 端无法控制该参数,只能靠服务端优化

真正棘手的是 WASM 的线程模型 —— 所有 gRPC 回调都在 UI 线程执行,大量并发响应会阻塞渲染。这不是 gRPC 本身的问题,而是 Blazor 渲染调度机制决定的。能做的只有节流、分页、降采样,别指望纯前端扛住上千并发流。

相关推荐