(七)RestAPI 毛子(Http 缓存/乐观锁/Polly/Rate limiting/异步大文件上传)



项目地址

  • 教程作者:
  • 教程地址:
  • 代码仓库地址:
  • 所用到的框架和插件:
dbt 
airflow

一、Http Cache

缓存的类型:
1. Client cache
2. Gateway cache(reverse poxy)
3. Proxy cache(CDN)

1.1 服务注册

  1. 注册cache服务
    在这里插入图片描述
  2. 注册中间件

在这里插入图片描述

1.2 Validation with ETag

  • 使用Rest api 返回ETag头部进行缓存
    在这里插入图片描述
    流程:
    1. 第一次请求,没有缓存,数据库查询后,返回并且添加ETag响应头
    2. 响应头存储在浏览器缓存,如果还是相同的请求或者没有更改,则返回304not modified,并从内存缓存里拿数据
    3. 如果更改或者没有缓存数据,则返回新的ETag

1. 添加ETagMiddleware中间件

  • 用于生成Etag头部和判断响应
namespace DevHabit.Api.Middleware;

//定义一个中间件,表示请求管道中的下一个中间件
public sealed class ETagMiddleware(RequestDelegate next)
{
   
    public async Task InvokeAsync(HttpContext context, InMemoryETagStore eTagStore)
    {
   
        //1.如果当前请求方法是 POST、PUT、PATCH 或 DELETE,就跳过 ETag 逻辑
        if (CanSkipETag(context))
        {
   
            await next(context);
            return;
        }
        //2.获取当前请求的 URI,用作标识资源的 key,稍后要用来生成和比对 ETag。
        string resourceUri = context.Request.Path.Value!;
        //3.从请求头中读取客户端带来的 If-None-Match ETag,用于判断资源是否修改过。去掉引号是为了统一格式。
        string? ifNoneMatch = context.Request.Headers.IfNoneMatch.FirstOrDefault()?.Replace("\"", "");
        
        //4.如果请求方法是 GET 或 HEAD,就从 ETag 存储中获取当前资源的 ETag
        Stream originalStream = context.Response.Body; //获取原始响应流
        using var memoryStream = new MemoryStream(); //创建一个内存流,用于缓存响应内容
        context.Response.Body = memoryStream; //将响应流写入内存流,以便后续读取响应内容

        //5.执行请求管道中的下一个中间件(或控制器),并把响应写入 memoryStream 中g
        await next(context); 

        //6. 如果响应状态码是 200 OK,并且响应内容类型是 JSON,就计算 ETag
        if (IsETaggableResponse(context))
        {
   
            memoryStream.Position = 0; //将内存流位置重置到开头
            byte[] responseBody = await GetResponseBody(memoryStream); //读取内存流中的响应内容
            string eTag = GenerateETag(responseBody); //计算 ETag

            eTagStore.SetETag(resourceUri, eTag); //将 ETag 存储到 ETag 存储中
            context.Response.Headers.ETag = $"\"{
     eTag}\""; //将 ETag 添加到响应头中
            context.Response.Body = originalStream; //将响应流恢复为原始响应流

            //9. 如果 ETag 存储中已经有当前资源的 ETag,并且和计算出来的 ETag 一致,就返回 304 Not Modified
            if (context.Request.Method == HttpMethods.Get && ifNoneMatch == eTag) 
            {
   
                context.Response.StatusCode = StatusCodes.Status304NotModified;
                context.Response.ContentLength = 0;
                return;
            }
        }
        //如果内容有更新,复制缓冲的响应内容到原始响应流中,让客户端收到响应。
        memoryStream.Position = 0;
        await memoryStream.CopyToAsync(originalStream);
    }

    //判断当前响应是否适合使用 ETag 进行缓存处理
    private static bool IsETaggableResponse(HttpContext context)
    {
   
        return context.Response.StatusCode == StatusCodes.Status200OK &&
            (context.Response.Headers.ContentType
                .FirstOrDefault()?
                .Contains("json", StringComparison.OrdinalIgnoreCase) ?? false);
    }
    //读取 MemoryStream 中的响应内容,并以 byte[] 的形式返回
    private static async Task<byte[]> GetResponseBody(MemoryStream memoryStream)
    {
   
        using var reader = new StreamReader(memoryStream, leaveOpen: true);
        memoryStream.Position = 0;

        string content = await reader.ReadToEndAsync();

        return Encoding.UTF8.GetBytes(content);
    }
    //根据响应内容生成 ETag 值
    private static string GenerateETag(byte[] content)
    {
   
        byte[] hash = SHA512.HashData(content);
        return Convert.ToBase64String(hash);
    }

    //判断当前请求方法是否可以跳过 ETag 逻辑
    private static bool CanSkipETag(HttpContext context)
    {
   
        return context.Request.Method == HttpMethods.Post ||
            context.Request.Method 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值