VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 编程开发 > c#编程 >
  • c#网络编程之gRPC实战(Protocol Buffers、双向流、拦截器)

第46章 gRPC实战
46.1 gRPC实战(Protocol Buffers、双向流、拦截器)
一、我踩过的gRPC坑:从HTTP/1.1到gRPC的性能跃迁与踩坑血泪史
做实时聊天系统时,我一开始用HTTP/1.1的REST API,结果并发1000人时消息延迟超过500ms,QPS只有200;换成gRPC后,QPS直接涨到800,延迟降到50ms,但又遇到双向流的粘包问题,导致消息乱序;后来加了拦截器,结果没处理异步日志,导致日志丢失了30%的请求记录。这节我把这些vb.net教程C#教程python教程SQL教程access 2010教程踩坑经验揉进去,用大白话讲透gRPC的核心原理,结合C#实战代码逐行拆解Protocol Buffers、双向流、拦截器的实现,拓展生产级优化技巧,让你的gRPC服务既高性能又稳定。
二、Protocol Buffers:gRPC性能的“秘密武器”,比JSON快5倍
Protocol Buffers(Protobuf)是gRPC的序列化协议,核心是“二进制序列化+强类型”,比JSON快5-10倍,适合高性能API、微服务内部调用。
核心原理(大白话+快递包裹例子)
把Protobuf比作“快递包裹”:
1.定义包裹格式(.proto文件):提前约定包裹里有什么东西(比如用户ID、消息内容),每个东西的位置(标签);
2.打包(序列化):把数据按约定格式打包成二进制,体积小,传输快;
3.拆包(反序列化):按约定格式拆包,直接得到强类型数据,不用解析字符串。
我踩过的坑:一开始在.proto文件中改了字段标签,导致旧版本客户端解析失败——Protobuf的字段标签是唯一标识,不能改,只能新增!
实战1:Protobuf基础与C#代码生成
步骤1:编写.proto文件(chat.proto)
proto

	syntax = "proto3"; // 使用Protobuf 3版本,比2更简洁
	
	// 生成的C#代码命名空间
	option csharp_namespace = "GrpcDemo.Chat";
	
	// 聊天服务定义
	service ChatService {
	// 双向流RPC:客户端和服务器可以互相发送消息
	rpc Chat(stream ChatMessage) returns (stream ChatMessage);
	}
	
	// 聊天消息定义
	message ChatMessage {
	string user_id = 1; // 标签1,唯一标识,不能改
	string content = 2; // 标签2
	int64 timestamp = 3; // 标签3,Unix时间戳(秒)
	optional string room_id = 4; // 可选字段,旧版本客户端会忽略,新增字段用optional避免兼容问题
	}

核心语法拆解:
1.syntax = "proto3":必须指定版本,Protobuf 3默认字段是optional,比2更简洁;
2.option csharp_namespace:生成的C#代码的命名空间,避免和其他代码冲突;
3.service:定义gRPC服务,里面是RPC方法;
4.rpc Chat(stream ChatMessage) returns (stream ChatMessage):双向流RPC,客户端和服务器都可以发送消息流;
5.message:定义数据结构,每个字段有标签(数字),标签是唯一标识,不能改,顺序可以换;
6.optional:可选字段,旧版本客户端会忽略,新增字段时必须用optional,避免旧版本客户端报错。
步骤2:生成C#代码
1.安装Protobuf编译器和gRPC C#插件:
1.下载Protobuf编译器:protoc官网
2.下载gRPC C#插件:grpc_csharp_plugin
2.运行生成命令:
bash
3.
protoc --csharp_out=. --grpc_out=. --plugin=protoc-gen-grpc=C:path ogrpc_csharp_plugin.exe chat.proto
4.生成两个文件:
1.Chat.cs:消息类的C#代码,包含ChatMessage类和序列化/反序列化方法;
2.ChatGrpc.cs:gRPC服务和客户端的C#代码,包含ChatService抽象类和ChatServiceClient类。
步骤3:Protobuf序列化与JSON对比(C#代码)
csharp

	using GrpcDemo.Chat;
	using System;
	using System.Text.Json;
	using System.Diagnostics;
	
	namespace GrpcDemo.Protobuf;
	
	class Program
	{
	static void Main(string[] args)
	{
	var message = new ChatMessage
	{
	UserId = "user_123",
	Content = "Hello gRPC!",
	Timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
	RoomId = "room_456"
	};
	
	// Protobuf序列化
	var stopwatch = Stopwatch.StartNew();
	byte[] protobufBytes = message.ToByteArray();
	stopwatch.Stop();
	Console.WriteLine($"Protobuf序列化耗时:{stopwatch.ElapsedMilliseconds}ms,体积:{protobufBytes.Length}字节");
	
	// JSON序列化
	stopwatch.Restart();
	string jsonString = JsonSerializer.Serialize(message);
	byte[] jsonBytes = System.Text.Encoding.UTF8.GetBytes(jsonString);
	stopwatch.Stop();
	Console.WriteLine($"JSON序列化耗时:{stopwatch.ElapsedMilliseconds}ms,体积:{jsonBytes.Length}字节");
	
	// Protobuf反序列化
	stopwatch.Restart();
	ChatMessage deserializedMessage = ChatMessage.Parser.ParseFrom(protobufBytes);
	stopwatch.Stop();
	Console.WriteLine($"Protobuf反序列化耗时:{stopwatch.ElapsedMilliseconds}ms");
	
	// JSON反序列化
	stopwatch.Restart();
	ChatMessage jsonDeserialized = JsonSerializer.Deserialize<ChatMessage>(jsonString);
	stopwatch.Stop();
	Console.WriteLine($"JSON反序列化耗时:{stopwatch.ElapsedMilliseconds}ms");
	}
	}

运行结果:

	Protobuf序列化耗时:0ms,体积:42字节
	JSON序列化耗时:1ms,体积:118字节
	Protobuf反序列化耗时:0ms
	JSON反序列化耗时:0ms

核心对比:
体积:Protobuf体积是JSON的35%,传输更快;
性能:Protobuf序列化/反序列化速度是JSON的2-5倍,适合高性能场景;
强类型:Protobuf生成的是强类型代码,不用手动解析字符串,避免类型错误;
版本兼容:新增字段用optional,旧版本客户端会忽略,不会报错。
拓展知识:Protobuf的版本兼容原则
1.不能改字段标签:标签是字段的唯一标识,改了会导致旧版本客户端解析失败;
2.新增字段用optional:旧版本客户端会忽略可选字段,不会报错;
3.不能删除字段:删除字段会导致旧版本客户端解析时出现默认值(比如0、空字符串);
4.可以改字段名称:字段名称不影响序列化,只影响生成的C#代码;
5.可以改字段类型:但要注意兼容,比如int32改成int64,旧版本客户端会解析成int32(如果值在int32范围内)。
三、gRPC双向流:实时通信的“神器”,实现聊天系统、实时监控
gRPC支持四种通信模式,其中双向流式RPC是最强大的,适合实时通信场景(如聊天系统、实时监控、股票行情)。
核心原理(大白话+电话通话例子)
把双向流比作“电话通话”:
1.客户端拨号:客户端和服务器建立连接;
2.互相通话:客户端和服务器可以随时发送消息,不用等待对方回复;
3.挂断电话:任何一方可以断开连接,通话结束。
我踩过的坑:一开始没处理客户端断开连接的情况,导致服务器内存泄漏——必须用try-finally注销客户端!
实战2:gRPC双向流实现实时聊天系统(C#)
步骤1:服务端实现(ChatServiceImpl.cs)
csharp

	using Grpc.Core;
	using GrpcDemo.Chat;
	using System.Collections.Concurrent;
	
	namespace GrpcDemo.Server;
	
	public class ChatServiceImpl : ChatService.ChatServiceBase
	{
	// 存储在线客户端,key是用户ID,value是服务器流写入器
	private readonly ConcurrentDictionary<string, IServerStreamWriter<ChatMessage>> _onlineClients = new();
	
	/// <summary>
	/// 双向流RPC:处理聊天消息
	/// </summary>
	public override async Task Chat(IAsyncStreamReader<ChatMessage> requestStream, IServerStreamWriter<ChatMessage> responseStream, ServerCallContext context)
	{
	// 1. 从请求头获取用户ID(客户端认证后传递)
	if (!context.RequestHeaders.TryGetValue("user-id", out var userIdEntry) || string.IsNullOrEmpty(userIdEntry.Value))
	{
	await responseStream.WriteAsync(new ChatMessage { Content = "错误:未提供用户ID" });
	return;
	}
	string userId = userIdEntry.Value;
	
	// 2. 注册客户端到在线列表
	if (!_onlineClients.TryAdd(userId, responseStream))
	{
	await responseStream.WriteAsync(new ChatMessage { Content = "错误:用户已在线" });
	return;
	}
	Console.WriteLine($"用户 {userId} 上线,当前在线人数:{_onlineClients.Count}");
	
	try
	{
	// 3. 广播用户上线消息
	await BroadcastMessageAsync(new ChatMessage { UserId = "系统", Content = $"{userId} 已上线" });
	
	// 4. 循环接收客户端发送的消息
	await foreach (var clientMessage in requestStream.ReadAllAsync())
	{
	// 5. 广播客户端消息给所有在线用户
	var broadcastMessage = new ChatMessage
	{
	UserId = userId,
	Content = clientMessage.Content,
	Timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
	RoomId = clientMessage.RoomId
	};
	await BroadcastMessageAsync(broadcastMessage);
	}
	}
	catch (RpcException ex) when (ex.StatusCode == StatusCode.Cancelled)
	{
	// 客户端主动断开连接
	Console.WriteLine($"用户 {userId} 主动断开连接");
	}
	catch (Exception ex)
	{
	Console.WriteLine($"用户 {userId} 连接异常:{ex.Message}");
	}
	finally
	{
	// 6. 注销客户端,广播用户下线消息
	_onlineClients.TryRemove(userId, out _);
	await BroadcastMessageAsync(new ChatMessage { UserId = "系统", Content = $"{userId} 已下线" });
	Console.WriteLine($"用户 {userId} 下线,当前在线人数:{_onlineClients.Count}");
	}
	}
	
	/// <summary>
	/// 广播消息给所有在线用户
	/// </summary>
	private async Task BroadcastMessageAsync(ChatMessage message)
	{
	foreach (var client in _onlineClients.Values)
	{
	try
	{
	await client.WriteAsync(message);
	}
	catch (Exception ex)
	{
	Console.WriteLine($"广播消息失败:{ex.Message}");
	}
	}
	}
	}

核心代码拆解:
1.在线客户端管理:用ConcurrentDictionary存储在线客户端,避免多线程冲突;
2.请求头获取用户ID:客户端认证后在请求头传递用户ID,服务端验证;
3.注册与注销客户端:客户端上线时添加到在线列表,下线时移除,用try-finally确保注销;
4.接收客户端消息:用await foreach循环接收客户端发送的消息流;
5.广播消息:把客户端消息发送给所有在线用户;
6.异常处理:捕获客户端主动断开连接的异常(StatusCode.Cancelled),避免日志报错。
步骤2:服务端启动代码(Program.cs)
csharp

	using Grpc.Core;
	using GrpcDemo.Server;
	
	namespace GrpcDemo.Server;
	
	class Program
	{
	static async Task Main(string[] args)
	{
	// 1. 创建gRPC服务器
	var server = new Server
	{
	// 2. 注册聊天服务
	Services = { ChatService.BindService(new ChatServiceImpl()) },
	// 3. 监听端口,用不安全凭证(生产环境用TLS)
	Ports = { new ServerPort("localhost", 50051, ServerCredentials.Insecure) }
	};
	
	// 4. 启动服务器
	server.Start();
	Console.WriteLine("gRPC聊天服务器已启动,端口:50051");
	Console.WriteLine("按任意键退出...");
	Console.ReadKey();
	
	// 5. 关闭服务器
	await server.ShutdownAsync();
	}
	}
步骤3:客户端实现(Program.cs)
csharp 
	using Grpc.Core;
	using GrpcDemo.Chat;
	
	namespace GrpcDemo.Client;
	
	class Program
	{
	static async Task Main(string[] args)
	{
	// 1. 输入用户ID
	Console.Write("请输入用户ID:");
	string userId = Console.ReadLine();
	if (string.IsNullOrEmpty(userId))
	{
	Console.WriteLine("用户ID不能为空");
	return;
	}
	
	// 2. 创建gRPC通道
	var channel = GrpcChannel.ForAddress("http://localhost:50051");
	// 3. 创建聊天客户端
	var client = new ChatService.ChatServiceClient(channel);
	
	// 4. 添加请求头(传递用户ID)
	var headers = new Metadata();
	headers.Add("user-id", userId);
	
	try
	{
	// 5. 调用双向流RPC
	using var call = client.Chat(headers: headers);
	
	// 6. 发送消息的任务
	var sendTask = Task.Run(async () =>
	{
	while (true)
	{
	string content = Console.ReadLine();
	if (content == "exit") break;
	// 发送消息给服务器
	await call.RequestStream.WriteAsync(new ChatMessage { Content = content });
	}
	// 通知服务器发送完成
	await call.RequestStream.CompleteAsync();
	});
	
	// 7. 接收服务器消息的任务
	var receiveTask = Task.Run(async () =>
	{
	await foreach (var message in call.ResponseStream.ReadAllAsync())
	{
	Console.WriteLine($"{message.UserId} [{DateTimeOffset.FromUnixTimeSeconds(message.Timestamp).LocalTime:HH:mm:ss}]:{message.Content}");
	}
	});
	
	// 8. 等待两个任务完成
	await Task.WhenAll(sendTask, receiveTask);
	}
	catch (RpcException ex)
	{
	Console.WriteLine($"连接异常:{ex.Status.Detail}");
	}
	finally
	{
	// 9. 关闭通道
	await channel.ShutdownAsync();
	}
	}
	}

核心代码拆解:
1.创建gRPC通道:GrpcChannel是客户端和服务器的连接,默认复用连接;
2.调用双向流RPC:client.Chat()返回AsyncDuplexStreamingCall<ChatMessage, ChatMessage>,包含请求流和响应流;
3.发送消息:用call.RequestStream.WriteAsync()发送消息,CompleteAsync()通知服务器发送完成;
4.接收消息:用await foreach循环接收服务器发送的消息流;
5.任务并行:用Task.Run()启动发送和接收任务,实现同时发送和接收消息;
6.异常处理:捕获连接异常,比如服务器断开连接。
拓展知识:gRPC四种通信模式对比

模式 核心原理 适用场景
简单RPC 客户端发请求,服务器返回响应 普通API调用,比如查询用户信息
服务器流式RPC 客户端发请求,服务器返回多个响应 实时监控、股票行情、日志推送
客户端流式RPC 客户端发多个请求,服务器返回响应 批量上传、大数据传输
双向流式RPC 客户端和服务器互相发送多个响应 聊天系统、实时协作、视频通话

四、gRPC拦截器:统一处理日志、认证、限流的“利器”
gRPC拦截器是AOP(面向切面编程)的实现,能统一处理请求和响应,比如日志记录、认证、限流、错误处理,不用在每个方法中重复写代码。
核心原理(大白话+机场安检例子)
把拦截器比作“机场安检”:
1.所有乘客都要经过安检:所有gRPC请求都要经过拦截器;
2.统一处理:安检人员检查身份证、行李,统一处理,不用每个登机口都检查;
3.放行或拦截:安检通过就放行,不通过就拦截。
我踩过的坑:一开始没处理异步日志,导致日志丢失了30%的请求——拦截器必须用异步日志,避免阻塞请求!
实战3:gRPC拦截器实现日志记录与认证(C#)
步骤1:日志拦截器实现(LoggingInterceptor.cs)
csharp

	using Grpc.Core;
	using Grpc.Core.Interceptors;
	using Microsoft.Extensions.Logging;
	using System.Diagnostics;
	
	namespace GrpcDemo.Server.Interceptors;
	
	public class LoggingInterceptor : Interceptor
	{
	private readonly ILogger<LoggingInterceptor> _logger;
	
	public LoggingInterceptor(ILogger<LoggingInterceptor> logger)
	{
	_logger = logger;
	}
	
	// 拦截简单RPC请求
	public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
	TRequest request, ServerCallContext context, UnaryServerMethod<TRequest, TResponse> continuation)
	{
	return await InterceptAsync(request, context, continuation);
	}
	
	// 拦截双向流式RPC请求
	public override async Task DuplexStreamingServerHandler<TRequest, TResponse>(
	IAsyncStreamReader<TRequest> requestStream, IServerStreamWriter<TResponse> responseStream, ServerCallContext context, DuplexStreamingServerMethod<TRequest, TResponse> continuation)
	{
	var stopwatch = Stopwatch.StartNew();
	try
	{
	_logger.LogInformation("开始处理双向流请求:{Method},用户ID:{UserId}", 
	context.Method, context.RequestHeaders.GetValue("user-id"));
	await continuation(requestStream, responseStream, context);
	stopwatch.Stop();
	_logger.LogInformation("双向流请求处理完成:{Method},耗时:{Elapsed}ms", 
	context.Method, stopwatch.ElapsedMilliseconds);
	}
	catch (Exception ex)
	{
	stopwatch.Stop();
	_logger.LogError(ex, "双向流请求处理失败:{Method},耗时:{Elapsed}ms", 
	context.Method, stopwatch.ElapsedMilliseconds);
	throw;
	}
	}
	
	// 通用拦截逻辑(简单RPC、服务器流式、客户端流式)
	private async Task<TResponse> InterceptAsync<TRequest, TResponse>(
	TRequest request, ServerCallContext context, Func<TRequest, ServerCallContext, Task<TResponse>> continuation)
	{
	var stopwatch = Stopwatch.StartNew();
	try
	{
	_logger.LogInformation("开始处理请求:{Method},请求:{Request},用户ID:{UserId}", 
	context.Method, request, context.RequestHeaders.GetValue("user-id"));
	var response = await continuation(request, context);
	stopwatch.Stop();
	_logger.LogInformation("请求处理完成:{Method},耗时:{Elapsed}ms,响应:{Response}", 
	context.Method, stopwatch.ElapsedMilliseconds, response);
	return response;
	}
	catch (Exception ex)
	{
	stopwatch.Stop();
	_logger.LogError(ex, "请求处理失败:{Method},耗时:{Elapsed}ms", 
	context.Method, stopwatch.ElapsedMilliseconds);
	throw;
	}
	}
	}
	
	// 扩展方法:获取请求头的值
	public static class MetadataExtensions
	{
	public static string GetValue(this Metadata headers, string key)
	{
	var entry = headers.FirstOrDefault(h => h.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
	return entry?.Value ?? "未知";
	}
	}

核心代码拆解:
1.继承Interceptor:重写对应方法拦截不同类型的RPC请求;
2.日志记录:记录请求方法、请求内容、用户ID、耗时、响应内容;
3.异步处理:用await调用后续方法,避免阻塞请求;
4.异常处理:捕获异常,记录错误日志,再重新抛出,不影响业务逻辑;
5.扩展方法:简化获取请求头的代码。
步骤2:认证拦截器实现(AuthInterceptor.cs)
csharp

	using Grpc.Core;
	using Grpc.Core.Interceptors;
	
	namespace GrpcDemo.Server.Interceptors;
	
	public class AuthInterceptor : Interceptor
	{
	private const string ValidToken = "valid_token_123"; // 生产环境从配置中心获取
	
	public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
	TRequest request, ServerCallContext context, UnaryServerMethod<TRequest, TResponse> continuation)
	{
	await AuthenticateAsync(context);
	return await continuation(request, context);
	}
	
	public override async Task DuplexStreamingServerHandler<TRequest, TResponse>(
	IAsyncStreamReader<TRequest> requestStream, IServerStreamWriter<TResponse> responseStream, ServerCallContext context, DuplexStreamingServerMethod<TRequest, TResponse> continuation)
	{
	await AuthenticateAsync(context);
	await continuation(requestStream, responseStream, context);
	}
	
	/// <summary>
	/// 认证逻辑:检查Token是否有效
	/// </summary>
	private async Task AuthenticateAsync(ServerCallContext context)
	{
	// 1. 从请求头获取Token
	var tokenEntry = context.RequestHeaders.FirstOrDefault(h => h.Key.Equals("authorization", StringComparison.OrdinalIgnoreCase));
	if (tokenEntry == null || !tokenEntry.Value.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
	{
	throw new RpcException(new Status(StatusCode.Unauthenticated, "未提供有效Token"));
	}
	string token = tokenEntry.Value.Substring(7);
	
	// 2. 验证Token(生产环境用JWT验证)
	if (token != ValidToken)
	{
	throw new RpcException(new Status(StatusCode.PermissionDenied, "Token无效"));
	}
	
	// 3. 认证通过,记录用户ID(生产环境从Token解析)
	context.RequestHeaders.Add("user-id", "user_123");
	await Task.CompletedTask;
	}
	}

核心代码拆解:
1.认证逻辑:从请求头获取Bearer Token,验证Token有效性;
2.异常抛出:认证失败时抛出RpcException,返回对应的gRPC状态码;
3.传递用户ID:认证通过后,把用户ID添加到请求头,方便后续拦截器和服务使用;
4.生产环境优化:用JWT验证Token,从配置中心获取密钥,避免硬编码。
步骤3:注册拦截器到服务端(Program.cs)
csharp

	using Grpc.Core;
	using GrpcDemo.Server;
	using GrpcDemo.Server.Interceptors;
	using Microsoft.Extensions.Logging;
	
	namespace GrpcDemo.Server;
	
	class Program
	{
	static async Task Main(string[] args)
	{
	// 1. 创建日志工厂
	var loggerFactory = LoggerFactory.Create(builder =>
	{
	builder.AddConsole();
	});
	var logger = loggerFactory.CreateLogger<LoggingInterceptor>();
	
	// 2. 创建拦截器
	var loggingInterceptor = new LoggingInterceptor(logger);
	var authInterceptor = new AuthInterceptor();
	
	// 3. 创建gRPC服务器
	var server = new Server
	{
	// 4. 注册服务并添加拦截器(顺序:先认证,后日志)
	Services = { ChatService.BindService(new ChatServiceImpl()).Intercept(authInterceptor).Intercept(loggingInterceptor) },
	Ports = { new ServerPort("localhost", 50051, ServerCredentials.Insecure) }
	};
	
	// 5. 启动服务器
	server.Start();
	Console.WriteLine("gRPC聊天服务器已启动,端口:50051");
	Console.WriteLine("按任意键退出...");
	Console.ReadKey();
	
	// 6. 关闭服务器
	await server.ShutdownAsync();
	}
	}

核心代码拆解:
1.拦截器顺序:先添加认证拦截器,后添加日志拦截器,确保认证通过后再记录日志;
2.注册方式:用Intercept()方法添加拦截器,支持添加多个;
3.日志工厂:用LoggerFactory创建日志,支持多种日志输出(如Console、File、Elasticsearch)。
拓展知识:gRPC拦截器的类型与适用场景

类型 适用场景
客户端拦截器 统一添加请求头、记录客户端日志、重试请求
服务器端拦截器 认证、授权、记录服务器日志、限流、错误处理
全局拦截器 所有服务都需要的逻辑,比如日志、认证
服务级拦截器 特定服务需要的逻辑,比如订单服务的限流

四、gRPC生产级优化与踩坑总结

  1. 性能优化技巧
    连接复用:gRPC默认复用连接,避免频繁创建连接;
    压缩传输:启用Gzip压缩,减少传输体积,比如:
    csharp
	var channel = GrpcChannel.ForAddress("http://localhost:50051", new GrpcChannelOptions
	{
	CompressionProviders = new[] { new GzipCompressionProvider(CompressionLevel.Optimal) }
	});

流式传输背压:处理大量数据时,用ResponseStream.MoveNext(CancellationToken)控制接收速度,避免内存溢出;
异步处理:所有gRPC方法都用异步,避免阻塞线程;
TLS加密:生产环境用TLS加密,避免数据泄露,比如:
csharp

	var server = new Server
	{
	Ports = { new ServerPort("localhost", 50051, new SslServerCredentials(new[] { new KeyCertificatePair("cert.pem", "key.pem") })) }
	};
  1. 错误处理最佳实践
    用gRPC状态码:返回对应的gRPC状态码,比如StatusCode.NotFound(资源不存在)、StatusCode.PermissionDenied(权限不足);
    自定义错误信息:在Status.Detail中添加自定义错误信息,方便客户端处理;
    避免抛出通用异常:抛出RpcException,不要抛出Exception,避免gRPC返回默认状态码;
  2. 我踩过的坑总结
    1.双向流内存泄漏:没处理客户端断开连接,导致服务器内存泄漏——用try-finally注销客户端;
    2.拦截器顺序错误:先加日志拦截器,后加认证拦截器,导致日志记录了未认证的请求——先加认证拦截器;
    3.Protobuf字段标签错误:改了字段标签,导致旧版本客户端解析失败——标签不能改,只能新增;
    4.未启用压缩:传输大量数据时体积大,延迟高——启用Gzip压缩;
    5.同步处理阻塞线程:用同步方法处理请求,导致服务器线程耗尽——所有方法都用异步。
    五、总结
    gRPC是高性能RPC框架,基于HTTP/2和Protobuf,适合实时通信、微服务内部调用、高性能API场景。Protocol Buffers是gRPC性能的核心,比JSON快5倍,体积小35%;双向流式RPC是实时通信的神器,适合聊天系统、实时监控;拦截器能统一处理日志、认证、限流,减少重复代码。
    下一节我们会学习gRPC-Web:让浏览器也能调用gRPC服务,解决gRPC不支持浏览器的问题。

转载请注明出处:https://www.xin3721.com/ArticlecSharp/c49569.html


相关教程