You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

缓存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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 20:57:50