缓存GRPC成功响应失败时加载缓存遇反序列化问题求助
解决GRPC响应缓存的可行方案
核心问题是HttpResponseMessage设计上不支持直接序列化(没有无参构造函数,且内部包含大量不可序列化的状态),所以不能直接缓存这个对象,得换思路,下面是几个实用方案:
方案一:缓存反序列化后的业务对象(推荐)
既然最终要的是GRPC返回的业务数据,直接缓存反序列化后的对象,完全绕开HttpResponseMessage的序列化问题,逻辑最简单,性能也更好。
示例代码(.NET环境):
private readonly IMemoryCache _cache; private readonly HttpClient _httpClient; // 泛型方法适配不同的GRPC响应对象 public async Task<T> GetGrpcResponseAsync<T>(string cacheKey, string grpcEndpoint) where T : class { // 先尝试读取缓存 if (_cache.TryGetValue(cacheKey, out T cachedData)) { return cachedData; } try { var httpResponse = await _httpClient.GetAsync(grpcEndpoint); httpResponse.EnsureSuccessStatusCode(); // 反序列化为业务对象 var responseData = await httpResponse.Content.ReadFromJsonAsync<T>(); // 缓存对象,设置合理过期时间 _cache.Set(cacheKey, responseData, TimeSpan.FromHours(1)); return responseData; } catch (HttpRequestException) { // 请求失败时二次尝试读取缓存(防止缓存刚好过期) if (_cache.TryGetValue(cacheKey, out T fallbackData)) { return fallbackData; } // 无缓存时再抛出异常,或者返回默认值根据业务调整 throw; } }
方案二:缓存响应的可序列化元数据+字节内容
如果必须保留HTTP响应的完整结构(比如状态码、响应头等),可以把HttpResponseMessage里可序列化的部分提取出来,封装成自定义类缓存,需要时再重建响应对象。
首先定义可序列化的缓存类:
[Serializable] public class CachedHttpResponse { public HttpStatusCode StatusCode { get; set; } public Dictionary<string, string> Headers { get; set; } public byte[] ContentBytes { get; set; } }
然后实现缓存逻辑:
public async Task<HttpResponseMessage> GetCachedGrpcResponseAsync(string cacheKey, string grpcEndpoint) { // 尝试从缓存读取重建响应 if (_cache.TryGetValue(cacheKey, out CachedHttpResponse cachedData)) { var response = new HttpResponseMessage(cachedData.StatusCode); // 还原响应头 foreach (var header in cachedData.Headers) { response.Headers.TryAddWithoutValidation(header.Key, header.Value); } // 还原响应内容 response.Content = new ByteArrayContent(cachedData.ContentBytes); return response; } try { var response = await _httpClient.GetAsync(grpcEndpoint); response.EnsureSuccessStatusCode(); // 提取可序列化数据 var cachedData = new CachedHttpResponse { StatusCode = response.StatusCode, Headers = response.Headers.ToDictionary(h => h.Key, h => string.Join(", ", h.Value)), ContentBytes = await response.Content.ReadAsByteArrayAsync() }; _cache.Set(cacheKey, cachedData, TimeSpan.FromHours(1)); return response; } catch (HttpRequestException) { // 请求失败时尝试读取缓存重建响应 if (_cache.TryGetValue(cacheKey, out CachedHttpResponse fallbackData)) { var fallbackResponse = new HttpResponseMessage(fallbackData.StatusCode); foreach (var header in fallbackData.Headers) { fallbackResponse.Headers.TryAddWithoutValidation(header.Key, header.Value); } fallbackResponse.Content = new ByteArrayContent(fallbackData.ContentBytes); return fallbackResponse; } throw; } }
方案三:自定义序列化转换器(不推荐)
可以通过自定义JSON转换器绕过HttpResponseMessage无参构造函数的限制,但因为HttpResponseMessage内部有很多未公开的状态,序列化后可能丢失信息,容易出现兼容性问题,仅作为特殊场景的备选。
示例自定义转换器(使用Newtonsoft.Json):
public class HttpResponseMessageConverter : JsonConverter<HttpResponseMessage> { public override HttpResponseMessage ReadJson(JsonReader reader, Type objectType, HttpResponseMessage existingValue, bool hasExistingValue, JsonSerializer serializer) { var jObj = JObject.Load(reader); var statusCode = jObj["StatusCode"].ToObject<HttpStatusCode>(); var headers = jObj["Headers"].ToDictionary(h => h.Key, h => h.Value.ToObject<string[]>()); var contentBytes = jObj["ContentBytes"].ToObject<byte[]>(); var response = new HttpResponseMessage(statusCode); foreach (var header in headers) { response.Headers.TryAddWithoutValidation(header.Key, header.Value); } response.Content = new ByteArrayContent(contentBytes); return response; } public override void WriteJson(JsonWriter writer, HttpResponseMessage value, JsonSerializer serializer) { var jObj = new JObject { ["StatusCode"] = value.StatusCode.ToString(), ["Headers"] = JObject.FromObject(value.Headers.ToDictionary(h => h.Key, h => h.Value)), ["ContentBytes"] = JToken.FromObject(value.Content.ReadAsByteArrayAsync().Result) }; jObj.WriteTo(writer); } }
使用方式:
// 序列化缓存 var response = await _httpClient.GetAsync(grpcEndpoint); var json = JsonConvert.SerializeObject(response, new HttpResponseMessageConverter()); _cache.Set(cacheKey, json); // 反序列化读取 var cachedJson = _cache.Get<string>(cacheKey); var cachedResponse = JsonConvert.DeserializeObject<HttpResponseMessage>(cachedJson, new HttpResponseMessageConverter());
内容的提问来源于stack exchange,提问作者sina heidari
相关产品推荐
相关产品推荐

