使用客户端缓存提高API性能

摘要

在现代应用程序中,性能始终是一个关键的考虑因素。无论是提高响应速度,降低延迟,还是减轻服务器负载,开发者都在寻找各种方法来优化他们的API。在Web开发中,利用客户端缓存是一种有效的方法,可以显著提高API的性能。本文将结合ReplicantDelta,深入探讨如何在.NET中使用客户端缓存,巧妙地提升API的响应速度。

问题概述

尽管数据库已经过优化,但在实际应用中,我们仍然会遇到一些性能瓶颈。例如,当数据库中有数百万条记录时,即使添加了索引,某些查询(如对用户名的部分匹配搜索)仍可能导致响应延迟。在API集成中,每次请求都需要花费较长的时间,这不仅影响用户体验,还会增加服务器的负载。

解决方案概述

为了应对这些性能挑战,我们可以采用以下策略:

  1. 利用客户端缓存:通过在客户端缓存API响应,减少重复请求,提高响应速度。
  2. 使用HTTP缓存头:利用HTTP的缓存控制头信息(如ETagCache-Control)来管理缓存,实现高效的数据更新检测。
  3. 引入辅助库:使用诸如ReplicantDelta这样的库,简化缓存的实现方式,降低开发成本。

接下来,我们将详细探讨如何在.NET中实现上述策略,并提供具体的代码示例。

客户端缓存概述

什么是HTTP缓存头?

HTTP提供了一系列头信息,用于管理缓存行为。这些头信息包括:

  • ETag:实体标签,用于标识资源的版本。当资源发生变化时,ETag也会随之更新。
  • Cache-Control:指定缓存策略,如no-cachemax-age等。
  • Last-Modified:指示资源的最后修改时间。

通过合理地设置这些头信息,客户端和服务器可以协同工作,实现高效的缓存机制。

浏览器中的缓存机制

当浏览器发送请求时,如果服务器返回了ETagLast-Modified等头信息,浏览器会缓存响应。当再次请求相同资源时,浏览器会携带If-None-MatchIf-Modified-Since等条件请求头,询问服务器资源是否更新。如果资源未更新,服务器返回304 Not Modified,浏览器直接使用本地缓存的数据。

在HTTP客户端中实现缓存

在非浏览器环境(如使用HttpClient进行API调用)中,实现类似的缓存机制需要额外的工作。幸运的是,Replicant库提供了便利的解决方案。

使用Delta优化服务器端缓存

什么是Delta?

Delta是一个开源的.NET库,通过在数据库中添加一个版本列(如RowVersion)或数据库自带的追踪数据,结合浏览器或客户端的缓存机制,实现高效的数据更新检测。它可以自动处理ETag的生成和验证,简化服务器端的开发工作。

如何使用Delta?

  1. 安装NuGet包

    Install-Package Delta
  2. 配置中间件

    Startup.csProgram.cs中,添加Delta的中间件:

     app.UseDelta<BloggingContext>(); //Delta内置了对EFCore的支持,只要是基于ADO.NET的ORM都可以使用Delta
  3. 修改数据库

  • SQL Server

    在相关的数据库表中,添加一个时间戳或版本列,用于跟踪数据的变化。例如,使用RowVersion列:

    [Timestamp]
    public byte[] RowVersion { get; set; }
  • PostgreSQL

    启用track_commit_timestamp功能

Delta的工作原理

graph LR
请求
计算ETag[根据时间戳<br/>从 WebAssembly 和 SQL<br/>计算当前 ETag]
是否有IfNoneMatch{是否有<br/>If-None-Match<br/>Header?}
Etag匹配{当前<br/>ETag与<br/>If-None-Match匹配?}
添加ETag[将当前 ETag<br/>添加到响应头]
304[响应<br/>304 未修改]
请求 --> 计算ETag
计算ETag --> 是否有IfNoneMatch
是否有IfNoneMatch -->|是| Etag匹配
是否有IfNoneMatch -->|否| 添加ETag
Etag匹配 -->|否| 添加ETag
Etag匹配 -->|是| 304

  • ETag生成:Delta会根据数据库中的RowVersion或时间戳列,自动生成ETag
  • 条件请求:当客户端发送请求时,携带If-None-Match头信息,Delta会根据ETag判断数据是否发生变化。
  • 响应优化:如果数据未变化,服务器直接返回304 Not Modified,客户端可以使用缓存的数据。

使用Delta的优势

  • 非侵入式:无需对现有的API进行大量修改,只需简单配置即可。
  • 性能提升:在数据未变化的情况下,避免了不必要的数据库查询和数据传输。
  • 适用性强:适用于各种类型的API,包括RESTful API、GraphQL等。

使用Replicant实现HTTP客户端缓存

什么是Replicant?

Replicant是由Simon Cropp开发的一个开源.NET库,用于实现HTTP客户端的缓存机制。它利用了与浏览器相同的HTTP缓存头(如ETagCache-Control),可以缓存HTTP响应,避免重复请求。

使用Replicant的步骤

  1. 安装NuGet包

    在项目中安装Replicant包:

    Install-Package Replicant
  2. 替换HttpClient

    Replicant提供了一个包装的HttpClient,在创建客户端时使用HttpCache

    HttpCache cachedClient = HttpCache.Default;
  3. 发送请求

    使用HttpCache发送请求,与普通的HttpClient用法相同:

    var response = await httpClient.ResponseAsync("https://api.example.com/products");
    var content = await response.Content.ReadAsStringAsync();

Replicant的工作原理

  • 首次请求:当第一次请求某个资源时,Replicant会将响应的内容和相关的缓存头信息(如ETag)存储在本地缓存中。
  • 后续请求:再次请求相同资源时,Replicant会检查本地缓存的有效性。如果缓存有效,且资源未更新,Replicant会直接返回缓存的内容,避免实际的HTTP请求。

综合示例:提升API集成请求性能

下面,我们将结合Replicant和Delta,提供一个完整的示例,展示如何利用客户端和服务器端缓存,提高API的性能。

后端代码示例

以下是使用Delta库的后端代码示例。该示例中,我们构建了一个简单的博客应用,包含了博客(Blog)和帖子(Post)两个实体。

1. 配置Program.cs

using Delta;
using Microsoft.EntityFrameworkCore;
using System.Text.Json;
using System.Text.Json.Serialization;
using TutorialClientCache.Components; var builder = WebApplication.CreateBuilder(args);
builder.AddNpgsqlDbContext<BloggingContext>("mydb"); // 添加服务到容器
builder.Services.AddRazorComponents()
.AddInteractiveWebAssemblyComponents(); builder.Services.AddBootstrapBlazor(); var app = builder.Build(); // 使用Delta中间件
app.UseDelta<BloggingContext>(); // 定义API端点
app.MapGet("/posts", async (string? title, BloggingContext db) =>
{
var query = db.Posts
.Where(p => title == null || p.Title.Contains(title))
.OrderByDescending(p => p.Title)
.ThenBy(p => p.Content)
.Take(10); return await query.ToListAsync();
}); // 配置HTTP请求管道
if (app.Environment.IsDevelopment())
{
app.UseWebAssemblyDebugging();
}
else
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
} app.UseAntiforgery(); app.MapStaticAssets();
app.MapRazorComponents<App>()
.AddInteractiveWebAssemblyRenderMode()
.AddAdditionalAssemblies(typeof(TutorialClientCache.Client._Imports).Assembly); app.Run();

2. 定义数据上下文和实体

数据上下文 BloggingContext

public class BloggingContext : DbContext
{
public BloggingContext(DbContextOptions<BloggingContext> options)
: base(options)
{
} public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
}

实体类 BlogPost

public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; } public List<Post> Posts { get; } = new List<Post>();
} public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; } public int BlogId { get; set; }
public Blog Blog { get; set; }
}

客户端代码示例

由于浏览器HttpClient已经内置客户端缓存逻辑,这里提供一个可以在其他Runtime环境内使用缓存的例子:

// See https://aka.ms/new-console-template for more information
using Replicant;
using System.Diagnostics; Console.WriteLine("Hello, World!"); string url = "http://localhost:5225/posts?title=CSS"; //warm up
await MeasureHttpClientPerformance(url);
await MeasureHttpCachePerformance(url); for (int i = 0; i < 5; i++)
{
var httpClientTime = await MeasureHttpClientPerformance(url);
Console.WriteLine($"HttpClient Time: {httpClientTime} ms");
}
for (int i = 0; i < 5; i++)
{
var httpCacheTime = await MeasureHttpCachePerformance(url);
Console.WriteLine($"HttpCache Time: {httpCacheTime} ms");
} static async Task<long> MeasureHttpClientPerformance(string url)
{
using var httpClient = new HttpClient();
var stopwatch = Stopwatch.StartNew(); var response = await httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync(); stopwatch.Stop();
return stopwatch.ElapsedMilliseconds;
} static async Task<long> MeasureHttpCachePerformance(string url)
{
HttpCache cachedClient = HttpCache.Default;
var stopwatch = Stopwatch.StartNew(); var response = await cachedClient.ResponseAsync(url);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync(); stopwatch.Stop();
return stopwatch.ElapsedMilliseconds;
}

效果





结论

通过结合使用Delta库和Blazor WebAssembly,我们可以在.NET应用程序中有效地利用客户端缓存机制,显著提升API的性能。利用ETag304 Not Modified等HTTP特性,我们能够减少不必要的网络请求和数据传输,提高应用的响应速度和用户体验。

参考链接

[.NET] 使用客户端缓存提高API性能的更多相关文章

  1. http缓存提高性能

    秋招也算是正式结束了,现在整理一下笔记,当作巩固一下知识,也希望这个对大家有帮助 http 缓存 和 cdn 缓存可以说是面试必问的问题,竟然是必问的问题,那就总结全面一点- http缓存机制 缓存分 ...

  2. 使用 libevent 和 libev 提高网络应用性能

    使用 libevent 和 libev 提高网络应用性能 Martin C. Brown, 作家, Freelance 简介: 构建现代的服务器应用程序需要以某种方法同时接收数百.数千甚至数万个事件, ...

  3. 在Winform开发框架中下拉列表绑定字典以及使用缓存提高界面显示速度

    在我们开发Winform界面的时候,往往需要绑定数据字典操作,也就是绑定一些下拉列表或者一些列表显示等,以便我们方便选择数据操作,常见的字典绑定操作就是对下拉列表的处理,本篇随笔是基于DevExpre ...

  4. 8 种提升 ASP.NET Web API 性能的方法

    ASP.NET Web API 是非常棒的技术.编写 Web API 十分容易,以致于很多开发者没有在应用程序结构设计上花时间来获得很好的执行性能. 在本文中,我将介绍8项提高 ASP.NET Web ...

  5. [.net 面向对象程序设计进阶] (15) 缓存(Cache)(二) 利用缓存提升程序性能

    [.net 面向对象程序设计进阶] (15) 缓存(Cache)(二) 利用缓存提升程序性能 本节导读: 上节说了缓存是以空间来换取时间的技术,介绍了客户端缓存和两种常用服务器缓布,本节主要介绍一种. ...

  6. 关于大小型项目如何最大限度提高WebAPi性能

    前言 WebAPi作为接口请求的一种服务,当我们请求该服务时我们目标是需要快速获取该服务的数据响应,这种情况在大型项目中尤为常见,此时迫切需要提高WebAPi的响应机制,当然也少不了前端需要作出的努力 ...

  7. 转载--提高C++性能的编程技术

    读书笔记:提高C++性能的编程技术   第1章 跟踪范例 1.1 关注点 本章引入的实际问题为:定义一个简单的Trace类,将当前函数名输出到日志文件中.Trace对象会带来一定的开销,因此在默认情况 ...

  8. paip.cache 缓存架构以及性能提升总结

    paip.cache 缓存架构以及性能提升总结 1         缓存架构以及性能(贯穿读出式(LookThrough) 旁路读出式(LookAside) 写穿式(WriteThrough) 回写式 ...

  9. 【转】提高PHP性能的53个技巧

    PHP技巧汇总:提高PHP性能的53个技巧用单引号代替双引号来包含字符串,这样做会更快一些.因为PHP会在双引号包围的字符串中搜寻变量,单引号则不会,注意:只有echo能这么做,它是一种可以把多个字符 ...

  10. 六种简单方法提升ASP.NET Web API性能

    ASP.NET Web API 是非常棒的技术.编写 Web API 十分容易,以致于很多开发者没有在应用程序结构设计上花时间来获得很好的执行性能. 在本文中,我将介绍8项提高 ASP.NET Web ...

随机推荐

  1. house of stom

    完成事项 house of stom学习 未完成事项 wmctf的blineless没打通 如何解决未完成事项 下周待做事项 house of orange house of lore 本周学习的知识 ...

  2. 一文彻底熟练掌握并使用Java的NIO操作

    一.基本概念 Java NIO 是 Java 1.4 引入的,用于处理高速.高并发的 I/O 操作.与传统的阻塞 I/O 不同,NIO 支持非阻塞 I/O 和选择器,可以更高效地管理多个通道. 二.核 ...

  3. 矩阵怪 - 2024全新矩阵产品,一键分发抖音,快手,视频号,B站,小红书!

    本方案面向谁,解决了什么问题 本方案主要面向C端客户,特别是那些在各大短视频平台(如小红书.抖音.视频号.快手.B站等)上进行内容创作和分发的个人用户.自由职业者.小型团队或企业.这些用户通常面临着在 ...

  4. Nuxt.js 应用中的 nitro:build:public-assets 事件钩子详解

    title: Nuxt.js 应用中的 nitro:build:public-assets 事件钩子详解 date: 2024/11/5 updated: 2024/11/5 author: cmdr ...

  5. 在美国和以色列的技术支持下BP机可以爆炸,那么苹果手机是否也可以被远程引爆

    要知道,这一切在技术上都是可以实现的. 由此可见,带电池的产品,最为稳妥的办法就是购买在中国组装的产品,否则其安全性是无法保证的.有人可能会说美国政府不会单独的通过这种方法去定向的杀害某个中国普通人, ...

  6. html代码新手教学

    HTML 是超文本标记语言(HyperText Markup Language)的缩写,是用来描述网页结构的标记语言.在这篇教学中,我们将介绍一些 HTML 基础知识,帮助新手快速学习并掌握如何编写简 ...

  7. WebLogic T3反序列化漏洞

    目录 前言 T3协议概述 T3反序列漏洞分析 漏洞复现 修复措施 前言 WebLogic的反序列化漏洞是一个经典的漏洞系列,原因就在于WebLogic在通信过程中使用T3协议传输数据,涉及到了序列化和 ...

  8. 我和JVM初次约会

    前言:该篇是参考结合<高手深度解析:JVM是什么>,自己剔除添加了一些内容整理的而来,这里感谢<高手深度解析:JVM是什么>的文章的指点讲解. JVM的生命周期 "J ...

  9. 话说ReferenceQueue

    也是几年前写的,在内部邮件列表里发过,在这里保存一下. 看到了这篇帖子: <WeakHashMap的神话>http://www.javaeye.com/topic/587995 因为Jav ...

  10. 使用 LLVM 框架创建一个工作编译器,第 1 部分

    使用 LLVM 及其中间表示构建一个自定义编译器 LLVM 编译器基础架构提供了一种强大的方法来优化您使用任何编程语言编写的应用程序.了解本系列文章(由两部分组成)第一部分中有关 LLVM 的基础知识 ...