十、Redis 的性能增强

缓存是提高应用性能的常用技术。 通常,我们会在内容分发网络(CDN)、HTTP 缓存和数据库缓存中遇到缓存。 缓存通过最小化访问底层较慢的数据存储层的需求来提高数据检索性能。 在本章中我们将学习的缓存技术是内存缓存和分布式缓存。

本章将涵盖以下主题:

  • ASP 中的内存缓存.NET Core
  • 分布式缓存
  • 设置和运行 Redis
  • 在 ASP 中实现 Redis。 核心网 5

技术要求

以下是你完成本章所需要的东西:

下面是该存储库的最终代码:https://github.com/PacktPublishing/ASP.NET-Core-and-Vue.js/tree/master/Chapter10的链接。

ASP 的内存缓存 NET Core

使用内存缓存允许开发人员将数据存储在服务器的资源中,特别是内存中。 因此,它通过删除对外部数据源的不必要的 HTTP 请求来帮助开发人员提高性能。

在 ASP 中实现内存缓存.NET Core 非常简单。 但是,我们不会在我们的应用中应用它。 我们将选择一种可扩展的缓存方式,即分布式缓存。 我们将只看如何实现内存缓存的部分,以便您有一个想法。

在 ASP 中启用内存缓存 NET Core

重复一遍,我们不会将本节中的代码应用到我们的应用中。 无论如何,你可以在Startup.csConfigureServices中启用内存缓存:

public void ConfigureServices(IServiceCollection services)
{
…
    services.AddMemoryCache();
}

方法在。net 中添加了一个非分布式内存实现。 您可以开始使用内存缓存,而不安装任何 NuGet 包。 然后将IMemoryCache注入到需要缓存的控制器中:

[Route("api/[controller]")]
[ApiController]
public class CacheController : ControllerBase
{
    private readonly IMemoryCache _memoryCache;
    public CacheController(IMemoryCache memoryCache)
    {
        _memoryCache = memoryCache;
    }
}

在从Microsoft.Extensions.Caching.Memory命名空间注入IMemoryCache之后,您就可以开始使用它了。 下面的代码块只检查缓存中是否存在蛋糕列表,如果是true则返回。 否则,它将使用服务并存储结果:

        [HttpGet("{cakeName}")]
        public async Task<List<string>> Get(string 
          cakeName)
        {
            var cacheKey = cakeName.ToLower();
            if (!_memoryCache.TryGetValue(cacheKey, out 
              List<string> cakeList))
            {
                cakeList = await Service.GetCakeList(
                  cakeName);
                var cacheExpirationOptions =
                    new MemoryCacheEntryOptions
                    {
                        AbsoluteExpiration = 
                          DateTime.Now.AddHours(6),
                        Priority = CacheItemPriority.
                          Normal,
                        SlidingExpiration = 
                          TimeSpan.FromMinutes(5)
                    };
                _memoryCache.Set(cacheKey, cakeList, 
                   cacheExpirationOptions);
            }
            return cakeList;
        }

您还会注意到在代码块中缓存中存在过期。 AbsoluteExpiration表示一个确定的到期日期,而SlidingExpiration用于监视缓存的非活动状态,或者仅仅是将它最后一次使用的时间放置。

虽然内存缓存消耗了它的服务器的资源,但内存缓存比分布式缓存快,因为它是物理上连接到服务器上的,但对于大型和多 web 服务器并不理想。

这里有一条建议。 在运行一个解决方案的多个实例时,不建议使用内存缓存,因为数据将不一致。 在多台服务器上工作时,有一种更好的缓存方法,这将在下一节中讨论。

分布式缓存

分布式缓存或全局缓存是具有专用网络的单实例或一组缓存服务器。 当应用到达分布式缓存时,如果与应用请求相关的缓存数据不存在,请求将重定向到数据库来查询数据。 否则,分布式缓存将只响应应用所需的数据。

这是两个服务器共享同一个分布式缓存实例的图:

Figure 10.1 – Distributed caching

图 10.1 -分布式缓存

上图显示了来自两个服务器的请求在决定是否从数据库中查询之前首先访问 Redis 缓存。

如果您的服务中的一个崩溃会发生什么? 实际上什么都没有,因为每个人都将查询分布式缓存。 因为缓存是分布式的,它会维护数据的一致性。 我们可以把所有的信息和所有头疼的事情都转移到分布式缓存中,大部分时候是 Redis。 分布式缓存比内存中慢,但更准确。

需要分布式缓存的原因之一是为了获得更高的精度。 例如,如果服务器崩溃,它不会把它的数据带到坟墓里。 这种方式更有弹性。

另一个原因是你可以独立地扩展分布式缓存或 Redis 缓存。 你可以独立地扩展 Redis 实例,同时保持你的 web 服务正常运行,而不使用他们的资源缓存。

设置和运行 Redis

Redis 官方支持 Linux 和 macOS,但不支持 Windows,因为工程师编写的 Redis 使用 BSD Unix。 Windows 端口是由一些被称为Microsoft Open Tech 小组的志愿者开发人员编写的。

让我们在 Windows、macOS 和 Ubuntu 上安装 Redis。 以下步骤取决于您的操作系统。

Windows 用户

  1. Go to https://github.com/microsoftarchive/redis/releases/tag/win-3.0.504 to download the installer of Redis for Windows:

    Figure 10.2 – Redis MSI installer and ZIP file

    图 10.2——Redis MSI 安装程序和 ZIP 文件

  2. Download and extract the Redis ZIP. Double-click the redis-server file. Allow the permission dialog box that will pop up by accepting Yes. The Redis instance will automatically start.

    检查安装是否完成,在终端中执行如下命令:

    redis-cli ping

    redis-cli是 Redis 功能的 CLI。 您应该看到来自终端的pong响应。

下面是安装 Redis 的另一种方法,使用msi文件从下载链接。

下载并安装msi文件只需点击它。 允许通过接受Yes将弹出的权限对话框。 Redis 实例将自动启动。

检查安装是否完成,在终端中执行如下命令:

redis-cli ping

redis-cli是 Redis 功能的 CLI。 您应该看到来自终端的pong响应:

Figure 10.3 – Extracted files and the running Redis instance on the Windows terminal

图 10.3 -解压后的文件和 Windows 终端上运行的 Redis 实例

这里是从 Windows 的 ZIP 文件和 CMD 中提取的文件,其中显示了点击redis-server文件后的 Redis 图像。

如果你正在考虑使用 Chocolatey 包管理器来安装 Redis,那么在写这篇文章的时候,这个 URL 是坏的。 我收到一个错误说404 没有找到

就是这样。 Redis 现在已经安装在 Windows 10 系统上。

用于 macOS 用户

你可以快速安装 Redis 在 Mac 上使用brew:

  1. 首先,通过运行以下命令更新brew:
  2. 接下来,我们通过运行以下命令安装 Redis:

    brew install redis

  3. 然后,让我们运行命令启动已安装的 Redis:

    brew services start redis

  4. Now run the following command to check whether Redis is running and reachable:

    redis-cli ping

    redis-cli是 Redis 功能的 CLI。 您应该看到来自 Terminal 的pong响应。

    注意:

    使用brew的 Redis 安装工作在 macOS 大苏尔,这是自最初的 macOS 以来最大的变化。

就是这样。 Redis 现在已经安装在 macOS 上了。

适用于 Linux 或 Ubuntu 用户

在 Linux 下安装 Redis 很简单:

  1. 让我们首先通过运行以下命令来更新我们的资源:
  2. 然后执行以下命令安装 Redis。

    sudo apt install redis-server

  3. 现在执行以下命令检查 Redis 是否运行且可达:

    redis-cli ping

redis-cli是 Redis 功能的 CLI。 您应该看到来自 Terminal 的pong响应。 就是这样。 Redis 现在已经安装在你的 Linux 机器上了。

所以,这就是在 Windows、macOS 和 Linux 机器上安装 Redis 服务器。 现在让我们在 ASP 中使用 Redis.NET Core 5。

在 ASP 中实现 Redis.NET Core

所以,让我们使用我们刚刚安装在机器上的 Redis 通过将其与我们现有的 ASP 集成.NET Core 5 解决方案。 以下是步骤:

  1. Go to the Travel.Application project and install these NuGet packages. The following NuGet package is a distributed cache implementation of the Microsoft.Extensions.Caching.StackExchangeRedis namespace using Redis:

    Microsoft.Extensions.Caching.StackExchangeRedis

    下面的 NuGet 包帮助我们检索appsettings.json中的配置:

    Microsoft.Extensions.Configuration

    下面的 NuGet 包是。net 的 JSON 框架:

    Newtonsoft.Json

  2. Next, we update the DependencyInjection.cs file of the Travel.Application project with the following code:

    ``` namespace Travel.Application { public static class DependencyInjection { public static IServiceCollection AddApplication(this IServiceCollection services, IConfiguration config) { services.AddAutoMapper(Assembly.GetExecutingAssembly()); services.AddValidatorsFromAssembly(Assembly. GetExecutingAssembly()); services.AddMediatR(Assembly.GetExecutingAssembly()); services.AddStackExchangeRedisCache(options => { options.Configuration = config.GetConnectionString("RedisConnection");

                var assemblyName = Assembly.
                  GetExecutingAssembly().GetName();
                options.InstanceName = assemblyName.
                  Name;
            });
           
            return services;
        }
    }
    

    } ```

    前面的Travel.Application的依赖注入实现现在需要一个IConfiguration参数。 我们将 Redis 分布式缓存服务添加到依赖注入容器中。 连接字符串的名称是RedisConnection,我们将在下一步中对其进行设置。

  3. 接下来是转到Travel.WebApi项目并使用以下代码更新appsettings.json:

    { "AuthSettings": { "Secret": "ReplaceThsWithYour0wnSecretKeyAnd StoreItInAzureKeyVault!" }, "ConnectionStrings": { "DefaultConnection": "Data Source= TravelTourDatabase.sqlite3", "RedisConnection": "localhost:6379" }, "Logging": { … }, "MailSettings": { … }, "AllowedHosts": "*" }

  4. We are adding connection strings for Redis and SQLite3 in this code. Consequently, we are also going to update DependencyInjection.cs of Travel.Data. So, let's update that with the following code:

    namespace Travel.Data { public static class DependencyInjection { public static IServiceCollection AddInfrastructureData(this IServiceCollection services, IConfiguration config) { services.AddDbContext<ApplicationDbContext>(options => options .UseSqlite(config.GetConnectionString("DefaultConnecti on"))); … } } }

    Travel.Data的依赖注入文件现在在appsettings.json中定义了DefaultConnection配置。

  5. Another thing to do here is to update the Startup.cs file of Travel.WebApi. Go to that file and update it with the following code:

    public void ConfigureServices(IServiceCollection services) { services.AddApplication(Configuration); … services.AddHttpContextAccessor(); services.AddControllers(); … }

    我们现在在AddApplication扩展方法中传递IConfiguration Configuration。 这样,Travel.Application可以访问appsettings.json中的RedisConnection

  6. Now let's use Redis to cache the response of the localhost:5001/api/v1.0/TourLists endpoint to its consumers sending a GET request. To do this, we will update the handler of api/v1.0/TourLists for the GET request, which is GetToursQuery.

    GetToursQuery可在Travel.Application/TourLists/Queries/GetTours/GetTours/GetToursQuery.cs中找到。 用以下代码更新GetToursQuery.cs:

    … using Microsoft.Extensions.Caching.Distributed; using Newtonsoft.Json; … namespace Travel.Application.TourLists.Queries.GetTours { public class GetToursQuery : IRequest<ToursVm> { } public class GetToursQueryHandler : IRequestHandler<GetToursQuery, ToursVm> { private readonly IApplicationDbContext _context; private readonly IMapper _mapper; private readonly IDistributedCache _distributedCache; public GetToursQueryHandler( IApplicationDbContext context, IMapper mapper, IDistributedCache distributedCache) { _context = context; _mapper = mapper; _distributedCache = distributedCache; } public async Task<ToursVm> Handle( GetToursQuery request, CancellationToken cancellationToken) { … } } }

    我们将Microsoft.Extensions.Caching.Distributed命名空间中的IDistributedCache注入GetToursQueryHandler的构造函数中。 我们将在Handle方法的逻辑中使用distributedCache,出于可读性的考虑,我将其截断。

    下面的代码是Handle方法更新后的业务逻辑:

    public async Task<ToursVm> Handle(GetToursQuery request, CancellationToken cancellationToken) { const string cacheKey = "GetTours"; ToursVm tourLists; string serializedTourList; var redisTourLists = await _distributedCache.GetAsync(cacheKey, cancellationToken); if (redisTourLists == null) { tourLists = new ToursVm { Lists = await _context.TourLists .ProjectTo<TourListDto>(_mapper. ConfigurationProvider) .OrderBy(t => t.City). ToListAsync( cancellationToken) }; serializedTourList = JsonConvert.SerializeObject(tourLists); redisTourLists = Encoding.UTF8.GetBytes(serializedTourList); var options = new DistributedCacheEntryOptions() .SetAbsoluteExpiration(DateTime.Now.AddMinutes(5)) .SetSlidingExpiration(TimeSpan.FromMinutes(1)); await _distributedCache.SetAsync( cacheKey,redisTourLists, options, cancellationToken); return tourLists; } serializedTourList = Encoding.UTF8.GetString( redisTourLists); tourLists = JsonConvert .DeserializeObject<ToursVm>(serializedTourList); return tourLists; }

    前面的代码块是GetToursQuery处理程序的更新逻辑。 我们有"GetTours"作为cacheKey,我们将使用它从缓存中检索数据并从缓存中保存数据。 cacheKey将用于搜索特定缓存时的查找。

    我们还通过_distributedCache.GetAsync检查是否存在现有缓存。 如果没有数据,则序列化tourLists对象并将其保存在缓存_distributedCache.SetAsync中,然后返回tourLists。 我们缓存的数据在 Redis,但我们把过期。 SetAbsoluteExpiration设置绝对过期时间,而SetSlidingExpiration设置条目可以不活动多长时间。

    如果有数据,则返回一个反序列化的tourLists

    现在,在我们继续 Vue.js 下一章,第 11 章,Vue.js 基本面 Todo 应用【显示】,Startup.cs让我们清理文件,因为它开始变得混乱。

    我们要做的是将 Swagger 配置移动到它的目录和文件中,然后安排所有服务并删除所有不必要的using语句。

  7. So, go to Travel.WebApi and create a folder named Extensions in the root directory of the project. Create two C# files named AppExtension.cs and Services.Extensions.cs. We are moving the Swagger code from Startup.cs to these two files like so:

    // AppExtension.cs

    … namespace Travel.WebApi.Extensions { public static class AppExtensions { public static void UseSwaggerExtension(this IApplicationBuilder app, IApiVersionDescriptionProvider provider) { app.UseSwagger(); app.UseSwaggerUI(c => { ... }); } } }

    这里,我们将两个中间件从Configure方法,即app.UserSwagger()app.UseSwaggerUI()迁移到AppExtension.cs文件中。

    // ServicesExtensions.cs

    … namespace Travel.WebApi.Extensions { public static class ServicesExtensions { public static void AddApiVersioningExtension( this IServiceCollection services) { services.AddApiVersioning(config => { ... }); } public static void AddVersionedApiExplorerExtension(this IServiceCollection services) { services.AddVersionedApiExplorer(options => { ... }); } public static void AddSwaggerGenExtension(this IServiceCollection services) { services.AddSwaggerGen(c => { ... }); } } }

    在这里,我们将ConfigureServices方法中的services.AddApiVersion()services.AddVersionedApiExplorer()services.AddSwaggerGen()服务迁移到ServicesExtensions.cs

  8. After moving the code to the Extensions directory, let's refactor Startup.cs by calling the extension methods that we created like so:

    public void ConfigureServices(IServiceCollection services) { services.AddApplication(Configuration); services.AddInfrastructureData(Configuration); services.AddInfrastructureShared(Configuration); services.AddInfrastructureIdentity(Configuration); services.AddHttpContextAccessor(); services.AddControllers(); services.AddApiVersioningExtension(); services.AddVersionedApiExplorerExtension(); services.AddSwaggerGenExtension(); services.AddTransient<IConfigureOptions<SwaggerGenOpti ons>, ConfigureSwaggerOptions>(); }

    现在,让我们看看应用的中间件:

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApiVersionDescriptionProvider provider) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseSwaggerExtension(provider); } app.UseHttpsRedirection(); app.UseRouting(); app.UseMiddleware<JwtMiddleware>(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }

    上述代码为Startup.cs中间件的重构块。 中间件现在比以前更干净了。

    同样,删除您将在Startup.cs文件中找到的未使用的using语句。

    让我们运行应用,看看 Redis 是否正常工作:

  9. Send a GET request to /api/v1.0/TourLists using Postman. Don't forget to include your JWT. The following screenshot shows the response time of the first request to the ASP.NET Core 5 application, which is more than 2 seconds:

    Figure 10.4 – API response without Redis cache

    图 10.4 -没有 Redis 缓存的 API 响应

  10. Let's send the same request to the same API to see whether the response time will be shorter:

    Figure 10.5 – API response with Redis cache

    图 10.5 - Redis 缓存的 API 响应

    前面的屏幕截图显示了第二个GET请求的较短的响应时间,33 毫秒,这是由于在对相同 API 的第一个GET请求期间存储了缓存。

  11. 要在中查看缓存,你可以使用 Redis 管理器工具。 这是一个免费的复述,马钎子工具,你可以下载并安装,https://www.electronjs.org/apps/anotherredisdesktopmanager,【T7 和付费版本发现 https://rdm.dev/】【显示】。 RDM 是为 Windows 和 macOS 用户提供的付费应用,但不适用于 Linux 用户。

  12. After running the Redis manager tool, send a new request to the /api/v1.0/TourLists API and check your Redis manager tool.

    让我们检查一下 Windows 10 v20H2、macOS Pro Big Sur 和 Ubuntu v20.10 Groovy Gorilla 中的缓存。 这些操作系统是撰写本书时的最新版本。

    下面的截图显示AnotherRedisDeskTopManager在 Windows 上运行:

Figure 10.6 – Another Redis DeskTop Manager on Windows

图 10.6 - Windows 上的另一个 Redis 桌面管理器

下面的截图显示了在 macOS 上运行的AnotherRedisDeskTopManager:

Figure 10.7 – Another Redis DeskTop Manager on macOS

图 10.7 -另一个在 macOS 上的 Redis 桌面管理器

下面的截图显示了在 Ubuntu 上运行的 Redis GUI:

Figure 10.8 – Redis GUI on Ubuntu

图 10.8 - Ubuntu 上的 Redis GUI

如果你不喜欢任何仪表盘或 GUI 的 Redis,你也可以使用一个 CLI 命令来调试或监控每一个命令处理你的 Redis 服务器。 运行命令开启监控:

redis-cli monitor

下面的屏幕截图显示了在运行redis-cli monitor后,您的请求在命令行中的样子:

Figure 10.9 – redis-cli monitor

图 10.9 - redis-cli 监视器

代码更新

接下来,我们更新应用中的一些代码,并更改命名约定,这对于前端准备来说非常简单。

以下是包含需要更新的项目、目录和文件的路径。 所以,去这一章的 GitHub repo,写下你的文件中缺少的东西,或者你可以从 GitHub 复制代码并粘贴到你的代码中。

同样,这些是命名约定、新属性和类中的简单更改。

Travel.Domain/Entities/TourList.cs:

public TourList()
 {
   TourPackages = new List<TourPackage>();
 }
public IList<TourPackage> TourPackages { get; set; }

前面的代码是更新TourList类中的Tours

Travel.Domain/Entities/User.cs:

public string Email { get; set; }

前面的代码正在更新User类中的Username

Travel.Application/Dtos/Tour/TourPackageDto.cs:

…
        public string WhatToExpect { get; set; }
        public float Price { get; set; }
        public string MapLocation { get; set; }
        public void Mapping(Profile profile)
        {
            profile.CreateMap<TourPackage, 
              TourPackageDto>()
              .ForMember(tpDto =>
                tpDto.Currency, opt =>
                opt.MapFrom(tp =>
                  (int)tp.Currency));
        }

前面的代码正在更新TourPackageDtoc 类。

Travel.Application/Dtos/Tour/TourListDto.cs:

public TourListDto()
{
   TourPackages = new List<TourPackageDto>();
}
public IList<TourPackageDto> TourPackages { get; set; }
public string Country { get; set; }

前面的代码正在更新TourListDto类。

Travel.Application/Dtos/User/AuthenticateRequest.cs:

public string Email { get; set; }

前面的代码正在更新AuthenticateRequest类中的Username

Travel.Application/Dtos/User/AuthenticateResponse.cs:

public string Email { get; set; }
…
Email = user.Email;

前面的代码正在AuthenticateResponse中更新Username

Travel.Application/TourLists/Commands/CreateTourList/CreateTourListCommand.cs:

var entity = new TourList { City = request.City, Country = 
  request.Country, About = request.About };

前面的代码是在CreateTourListCommand中添加properties

Travel.Application/TourLists/Commands/UpdateTourList/UpdateTourListCommand.cs:

entity.Country = request.Country;
entity.About = request.About;

前面的代码是在UpdateTourListCommand中添加属性。

TourPackages目录中创建一个新的文件夹,并将其命名为Queries。 在查询中,创建两个新的 c#文件,并将其命名为GetTourPackagesQuery.csGetTourPackagesValidator.cs.

Travel/Application/TourPackages/Queries/GetTourPackagesQueryValidator.cs:

using System.Collections.Generic;
 // for brevity, please see the code in the Github
using Travel.Application.Dtos.Tour;
namespace Travel.Application.TourPackages.Queries
{
    public class GetTourPackagesQuery : IRequest
      <List<TourPackageDto>>
    {
        public int ListId { get; set; }
    }
    public class GetTourPackagesQueryHandler : 
      IRequestHandler<GetTourPackagesQuery, List<
         TourPackageDto>>
    {
        private readonly IApplicationDbContext _context;
        private readonly IMapper _mapper;
        public GetTourPackagesQueryHandler(
          IApplicationDbContext context, IMapper mapper)
        {
            _context = context;
            _mapper = mapper;
        }
        public Task<List<TourPackageDto>> 
          Handle(GetTourPackagesQuery request, 
            CancellationToken cancellationToken)
        {
            var tourPackages = _context.TourPackages
                .Where(tp => tp.ListId == request.ListId)
                .OrderBy(tp => tp.Name)
                .ProjectTo<TourPackageDto>(_mapper.
                   ConfigurationProvider)
                .ToListAsync(cancellationToken);
            return tourPackages;
        }
    }
}

在前面的代码中没有 ing new。 新文件简单地添加了一个获得旅行团的查询。

Travel.Application/TourPackages/Queries/GetTourPackagesQueryValidator.cs:

using FluentValidation;
namespace Travel.Application.TourPackages.Queries
{
    public class GetTourPackagesQueryValidator : 
       AbstractValidator<GetTourPackagesQuery>
    {
        public GetTourPackagesQueryValidator()
        {
            RuleFor(x => x.ListId)
                .NotNull()
                .NotEmpty().WithMessage("ListId is 
                  required.");
        }
    }
}

前面的代码行在查询旅行包之前添加了一个新的验证器。

Travel.Identity/Services/UserService.cs:

Email = "yoursuperhero@gmail.com",
var user = _users.SingleOrDefault(u => u.Email == 
  model.Email && 

u.Password == model.Password);

Subject = new ClaimsIdentity(new[] { new Claim("sub", 
  user.Id.ToString()), new Claim("email", user.Email) }),

前面的代码正在更新UserService类。

Travel.Identity/Helpers/JwtMiddleware.cs:

var userId = int.Parse(jwtToken.Claims.First(c => c.Type == 
   "sub").Value);

前面的代码是更新JwtMiddleware类的AttachUserToContext方法。

Travel.WebApi/Controllers/v1/TourPackagesController.cs:

[HttpGet]
public async Task<ActionResult<List<TourPackageDto>>> 
  GetTourPackages([FromQuery] GetTourPackagesQuery query)
{
  return await Mediator.Send(query);
}

上述代码是TourPackagesController的一个新的Action方法。

现在,在您的存储库中更新了 code 之后,是时候挑战自己了。

运动/练习时间:

为了加强您在这里的学习,并在继续到前端部分之前,我希望您创建一个 ASP.NET Core 5 应用。 应用应该使用你在这本书中学到的所有东西,比如干净的架构、CQRS、API 版本控制、OpenAPI 和分布式缓存,无需认证或使用认证,或者使用像 Auth0 这样的身份作为服务来节省你的时间。 我现在能想到的一个应用是电子游戏的在线商店。 实体可以是DeveloperGameGameReviewsGenrePublisher等。 这个练习很简单,你可以在一周内完成。 我知道你能做到。 好运!

好的,让我们总结一下你在这一章所学到的东西。

总结

你终于读完了这一章,你学到了很多东西。 您已经了解到内存缓存比分布式缓存更快,因为它更靠近服务器。 但是,它不适用于同一服务器的多个实例。

您已经了解了分布式缓存解决了多个实例中的内存缓存问题,因为它为所有服务器实例提供了缓存数据的单一真实来源。

你已经学习了如何安装和运行 Redis 在 pc, macOS 和 Linux 机器,以及如何整合 Redis 到一个 ASP.NET Core Web API 来提高应用的性能,给最终用户带来更好的用户体验。

在下一章中,您将使用 Vue.js 3 构建您的第一个单页面应用。