GRpc异常处理Filter
全局错误处理服务端
微软已经实施了Interceptors,它们类似于Filter或Middlewares在ASP.NET MVC的核心或的WebAPI,它们可以用于全局异常处理,日志记录,验证等。
这是服务器端Interceptor自己的实现,Continuation是必须等待的Task,然后,如果引发了任何异常,则可以根据所获得的异常来控制RpcException和关联的StatusCode
using Grpc.Core;
using Grpc.Core.Interceptors;
using Microsoft.Extensions.Logging;
using System;
using System.Data.SqlClient;
using System.Threading.Tasks;
namespace DemoGrpc.Web.Logging
{
public class LoggerInterceptor : Interceptor
{
private readonly ILogger<LoggerInterceptor> _logger;
public LoggerInterceptor(ILogger<LoggerInterceptor> logger)
{
_logger = logger;
}
public async override Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
TRequest request,
ServerCallContext context,
UnaryServerMethod<TRequest, TResponse> continuation)
{
LogCall(context);
try
{
return await continuation(request, context);
}
catch (SqlException e)
{
_logger.LogError(e, $"An SQL error occured when calling {context.Method}");
Status status;
if (e.Number == -2)
{
status = new Status(StatusCode.DeadlineExceeded, "SQL timeout");
}
else
{
status = new Status(StatusCode.Internal, "SQL error");
}
throw new RpcException(status);
}
catch (Exception e)
{
_logger.LogError(e, $"An error occured when calling {context.Method}");
throw new RpcException(Status.DefaultCancelled, e.Message);
}
}
private void LogCall(ServerCallContext context)
{
var httpContext = context.GetHttpContext();
_logger.LogDebug($"Starting call. Request: {httpContext.Request.Path}");
}
}
}
注册方式如下
using DemoGrpc.Web.Logging;
using DemoGrpc.Web.Services;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace DemoGrpc.Web
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
//注册GRpc全局异常捕获
services.AddGrpc(options =>
{
options.Interceptors.Add<LoggerInterceptor>();
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGrpcService<CountryGrpcService>();
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
});
});
}
}
}
第二种方法也可以捕获到GRpc异常,但是写法比较粗糙。不推荐使用
using AutoMapper;
using DemoGrpc.Domain.Entities;
using DemoGrpc.Protobufs;
using DempGrpc.Services.Interfaces;
using Grpc.Core;
using System;
using System.Threading.Tasks;
public class CountryGrpcService : CountryService.CountryServiceBase
{
private readonly ICountryService _countryService;
private readonly IMapper _mapper;
public CountryGrpcService(ICountryService countryService, IMapper mapper)
{
_countryService = countryService;
_mapper = mapper;
}
public override async Task<CountriesReply> GetAll(EmptyRequest request, ServerCallContext context)
{
try
{
var countries = await _countryService.GetAsync();
return _mapper.Map<CountriesReply>(countries);
}
catch (Exception e)
{
throw new RpcException(Status.DefaultCancelled, e.Message);
}
}
}
Rpc异常信息介绍如下
一个普通标题 | 一个普通标题 |
---|---|
Aborted | 操作被中止,通常是由于并发性问题,如顺序器检查失败、事务中止等。 |
AlreadyExists | 试图创建的一些实体(例如,文件或目录)已经存在。 |
Cancelled | 该操作被取消(通常由调用者取消)。 |
DataLoss | 不可恢复的数据丢失或损坏。 |
DeadlineExceeded | 操作完成前截止日期已过期。对于改变系统状态的操作,即使操作已经成功完成,也会返回此错误。例如,来自服务器的成功响应可能会延迟到截止日期过期。 |
FailedPrecondition | 操作被拒绝,因为系统没有处于执行操作所需的状态。例如,要删除的目录可能是非空的,一个rmdir操作被应用到一个非目录,等等。 |
Internal | 内部错误。表示底层系统期望的某些不变量被打破。 |
InvalidArgument | 客户端指定了无效的参数。注意,这与FAILED_PRECONDITION不同。INVALID_ARGUMENT表示与系统状态无关的参数(例如格式不正确的文件名)。 |
NotFound | 一些被请求的实体(例如,文件或目录)没有找到。 |
OK | 成功返回 |
OutOfRange | 操作尝试超过有效范围。例如,查找或读取文件的前端。 |
PermissionDenied | 调用者没有权限执行指定的操作。PERMISSION_DENIED不能用于由于耗尽某些资源而导致的拒绝(对于那些错误,应该使用RESOURCE_EXHAUSTED)。如果无法识别调用者,则不能使用PERMISSION_DENIED(对于那些错误,则使用UNAUTHENTICATED)。 |
ResourceExhausted | 某些资源已经耗尽,可能是每个用户的配额,或者可能是整个文件系统空间不足。 |
Unauthenticated | 未认证/授权 |
Unavailable | 该服务目前不可用。这很可能是一种暂时的情况,可以通过后退重新尝试来纠正。注意,重试非幂等操作并不总是安全的。 |
Unimplemented | 此服务中未实现或不支持/启用操作。 |
Unknown | 未知的错误。可能返回此错误的一个示例是,如果从另一个地址空间接收到的状态值属于此地址空间中未知的错误空间。如果api没有返回足够的错误信息,则可能会将其转换为此错误。 |
具体地址:https://grpc.github.io/grpc/csharp/api/Grpc.Core.StatusCode.html
RpcException有相对应的重载:具体如下,可以自定义异常返回的信息
全局错误处理客户端
客户端也可以通过拦截器处理错误(实现客户端事件,如AsyncUnaryCall):
using Grpc.Core;
using Grpc.Core.Interceptors;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;
namespace ConsoleAppGRPC.Logging
{
public class LoggerInterceptor : Interceptor
{
private readonly ILogger<LoggerInterceptor> _logger;
public LoggerInterceptor(ILogger<LoggerInterceptor> logger)
{
_logger = logger;
}
public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
TRequest request,
ClientInterceptorContext<TRequest, TResponse> context,
AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
{
LogCall(context.Method);
var call = continuation(request, context);
return new AsyncUnaryCall<TResponse>(HandleResponse(call.ResponseAsync), call.ResponseHeadersAsync, call.GetStatus, call.GetTrailers, call.Dispose);
}
private async Task<TResponse> HandleResponse<TResponse>(Task<TResponse> t)
{
try
{
var response = await t;
_logger.LogDebug($"Response received: {response}");
return response;
}
catch (RpcException ex)
{
_logger.LogError($"Call error: {ex.Message}");
return default;
}
}
private void LogCall<TRequest, TResponse>(Method<TRequest, TResponse> method) where TRequest : class where TResponse : class
{
_logger.LogDebug($"Starting call. Type: {method.Type}. Request: {typeof(TRequest)}. Response: {typeof(TResponse)}");
}
}
}
使用方法:
using DemoGrpc.Domain.Entities;
using DemoGrpc.Protobufs;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
using System.Threading.Tasks;
using static DemoGrpc.Protobufs.CountryService;
using Microsoft.Extensions.Logging;
using ConsoleAppGRPC.Logging;
namespace ConsoleAppGRPC
{
class Program
{
static async Task Main(string[] args)
{
var services = new ServiceCollection();
services.AddScoped<LoggerInterceptor>();
services.AddLogging(logging =>
{
logging.AddConsole();
logging.SetMinimumLevel(LogLevel.Debug);
});
services.AddGrpcClient<CountryServiceClient>(o =>
{
o.Address = new Uri("https://localhost:5001");
}).AddInterceptor<LoggerInterceptor>();
var provider = services.BuildServiceProvider();
var client = provider.GetRequiredService<CountryServiceClient>();
var logger = provider.GetRequiredService<ILogger<Program>>();
var countries = (await client.GetAllAsync(new EmptyRequest()))?.Countries.Select(x => new Country
{
CountryId = x.Id,
Description = x.Description,
CountryName = x.Name
}).ToList();
if (countries != null)
{
logger.LogInformation("Found countries");
countries.ForEach(x => Console.WriteLine($"Found country {x.CountryName} ({x.CountryId}) {x.Description}"));
}
else
{
logger.LogDebug("No countries found");
}
}
}
}
得到的结果信息
结论
我们在本文中看到了如何全局处理错误。拦截器、RpcException、状态代码和返回信息的使用为我们提供了一定的灵活性,比如定制错误和向客户端发送相关错误的可能性。
GRpc异常处理Filter的更多相关文章
- 重新整理 .net core 实践篇—————grpc[三十三]
前言 简单整理一下grpc. 正文 什么是grpc? 一个远程过程调用框架,可以像类一样调用远程方法. 这种模式一般来说就是代理模式,然后都是框架自我生成的. 由google 公司发起并开源,故而前面 ...
- static,你还敢用吗?
我用火狐的HttpRequester测试开发组里一个同学发布的Web API接口,遇到了一个奇怪的问题. 我测试边界情况时,第一次调用响应的结果是正常的,但当再次及以后的请求时,却返回了异常“Syst ...
- 在MVC中处理异常的总结
无论是桌面程序还是web程序,异常处理都是必须的. 一般的处理方式是, 捕获异常,然后记录异常的详细信息到文本文件或者数据库中.在Asp.net MVC中可以使用内建的filter——HandleEr ...
- Spring+Maven+Dubbo+MyBatis+Linner+Handlebars—Web开发环境搭建
本文主要分三部分,分别是:后台核心业务逻辑.桥梁辅助控制和前台显示页面. 本Web开发环境综合了多种工具,包括Maven包管理与编译工具.Dubbo分布式服务框架.MyBatis数据持久化工具.Lin ...
- 【ASP.NET Core】处理异常(下篇)
上一篇中,老周给大伙伴们扯了有关 ASP.NET Core 中异常处理的简单方法.按照老周的优良作风,我们应该顺着这个思路继续挖掘. 本文老周就不自量力地介绍一下如何使用 MVC Filter 来处理 ...
- 【ASP.NET Core】处理异常--转
老周写的[ASP.NET Core]处理异常非常的通俗易懂,拿来记录下. 转自老周:http://www.cnblogs.com/tcjiaan/p/8461408.html 今天咱们聊聊有关异常处理 ...
- 扩展 IHttpModule
上篇提到请求进入到System.Web后,创建完HttpApplication对象后会执行一堆的管道事件,然后可以通过HttpModule来对其进行扩展,那么这篇文章就来介绍下如何定义我们自己的mod ...
- WebApi简介
简单创建.NET Core WebApi:https://www.cnblogs.com/yanbigfeg/p/9197375.html 登陆验证四种方式:https://www.cnblogs.c ...
- Spring Cloud 系列之 Netflix Zuul 服务网关
什么是 Zuul Zuul 是从设备和网站到应用程序后端的所有请求的前门.作为边缘服务应用程序,Zuul 旨在实现动态路由,监视,弹性和安全性.Zuul 包含了对请求的路由和过滤两个最主要的功能. Z ...
随机推荐
- Spring Boot笔记(二) springboot 集成 SMTP 发送邮件
个人博客网:https://wushaopei.github.io/ (你想要这里多有) 笔记:乘着项目迭代的间隙,把一些可复用的功能从项目中抽取出来,这是其中之一, 一.添加SMTP 及 MA ...
- Java实现 LeetCode 794 有效的井字游戏 (暴力分析)
794. 有效的井字游戏 用字符串数组作为井字游戏的游戏板 board.当且仅当在井字游戏过程中,玩家有可能将字符放置成游戏板所显示的状态时,才返回 true. 该游戏板是一个 3 x 3 数组,由字 ...
- (Java实现) 车厢重组
[问题描述] 在一个旧式的火车站旁边有一座桥,其桥面可以绕河中心的桥墩水平旋转.一个车站的职工发现桥的长度最多能容纳两节车厢,如果将桥旋转180度,则可以把相邻两节车厢的位置交换,用这种方法可以重新排 ...
- (Java实现) 洛谷 P1115 最大子段和
题目描述 给出一段序列,选出其中连续且非空的一段使得这段和最大. 输入输出格式 输入格式: 第一行是一个正整数NN,表示了序列的长度. 第二行包含NN个绝对值不大于1000010000的整数A_iA ...
- Java实现算法提高十进制数转八进制数
算法提高 十进制数转八进制数 时间限制:1.0s 内存限制:512.0MB 编写函数,其功能为把一个十进制数转换为其对应的八进制数.程序读入一个十进制数,调用该函数实现数制转换后,输出对应的八进制数. ...
- Java实现 LeetCode 136 只出现一次的数字
136. 只出现一次的数字 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次.找出那个只出现了一次的元素. 说明: 你的算法应该具有线性时间复杂度. 你可以不使用额外空间来实现 ...
- 阿里云高级技术专家空见: CDN的数据化之路
想要实现优质高速的互联网视频服务,一定离不开高质量的内容分发网络服务,就是我们常说的CDN,在10月13日云栖大会视频多媒体分论坛上,阿里云高级技术专家空见为大家讲解了CDN服务过程中,数据处理.安全 ...
- 滴滴HBase大版本滚动升级之旅
桔妹导读:滴滴HBase团队日前完成了0.98版本 -> 1.4.8版本滚动升级,用户无感知.新版本为我们带来了丰富的新特性,在性能.稳定性与易用性方便也均有很大提升.我们将整个升级过程中面临的 ...
- C#9.0 终于来了,您还学的动吗? 带上VS一起解读吧!(应该是全网第一篇)
一:背景 1. 讲故事 好消息,.NET 5.0 终于在2020年6月10日发布了第五个预览版,眼尖的同学一定看到了在这个版本中终于支持了 C# 9.0,此处有掌声,太好了!!! .Net5官方链接 ...
- Verifying dml pool data
数据出错 ,硬件变动 解决方案: 1.移除外接设备 2.bios还原