Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Redis

การใช้ Azure Redis Cache กับ .NET 6 Web API:คำแนะนำทีละขั้นตอน

บทความนี้จะกล่าวถึงพื้นฐาน Azure Redis Cache และการใช้งานโดยใช้ .NET Core Web API

วาระการประชุม

  • บทนำ
  • แคชคืออะไร
  • ประเภทของแคช
  • เรดดิสแคช
  • การตั้งค่าแคช Azure Redis
  • การใช้งานทีละขั้นตอน

ข้อกำหนดเบื้องต้น

  • วิชวลสตูดิโอ 2022
  • บัญชี Azure
  • .NET Core 6

บทนำ

การแคชเป็นที่นิยมอย่างมากในปัจจุบันในอุตสาหกรรมซอฟต์แวร์ เนื่องจากจะช่วยปรับปรุงประสิทธิภาพและความสามารถในการปรับขนาดของแอปพลิเคชัน เราใช้เว็บแอปพลิเคชันมากมาย เช่น Gmail และ Facebook และดูว่าแอปพลิเคชันเหล่านี้ตอบสนองได้ดีเพียงใด และเราได้รับประสบการณ์การใช้งานที่ยอดเยี่ยม มีผู้ใช้อินเทอร์เน็ตจำนวนมาก และหากแอปพลิเคชันมีปริมาณการใช้งานและความต้องการเครือข่ายจำนวนมาก เราจำเป็นต้องดูแลหลายสิ่งหลายอย่างที่ช่วยเราปรับปรุงประสิทธิภาพและการตอบสนองของแอปพลิเคชัน ด้วยเหตุนี้ จึงมีวิธีแก้ปัญหาการแคช และนั่นเป็นเหตุผลว่าทำไมการแคชจึงเข้ามาอยู่ในภาพ

แคชคืออะไร

แคชคือที่เก็บข้อมูลหน่วยความจำที่ใช้ในการจัดเก็บข้อมูลที่เข้าถึงบ่อยลงในที่จัดเก็บข้อมูลชั่วคราว ซึ่งจะช่วยปรับปรุงประสิทธิภาพได้อย่างมาก และหลีกเลี่ยงการโดนฐานข้อมูลโดยไม่จำเป็นและจัดเก็บข้อมูลที่ใช้บ่อยไว้ในแคช

การใช้ Azure Redis Cache กับ .NET 6 Web API:คำแนะนำทีละขั้นตอน

การใช้ Azure Redis Cache กับ .NET 6 Web API:คำแนะนำทีละขั้นตอน

ดังที่คุณเห็นในภาพด้านบน มีสองสถานการณ์ สถานการณ์หนึ่งไม่ได้ใช้แคช และอีกสถานการณ์หนึ่งใช้แคช ดังนั้นเมื่อเราไม่ใช้แคช ในกรณีนี้ สมมติว่าผู้ใช้ต้องการข้อมูล จากนั้นพวกเขาจะเข้าถึงฐานข้อมูลแต่ละครั้ง และจะเพิ่มความซับซ้อนของเวลาและลดประสิทธิภาพในกรณีที่มีข้อมูลคงที่บางอย่างที่ผู้ใช้ต้องการและเป็นข้อมูลเดียวกันสำหรับผู้ใช้ทุกคน เมื่อเราไม่ใช้แคช แต่ละแคชจะเข้าสู่ฐานข้อมูลที่ไม่จำเป็นเพื่อดึงข้อมูล อีกด้านหนึ่ง คุณจะเห็นว่าเราใช้แคช และในกรณีนั้น หากมีข้อมูลคงที่และเหมือนกันสำหรับผู้ใช้ทุกคน จะมีเพียงผู้ใช้รายแรกเท่านั้นที่จะเข้าถึงฐานข้อมูลและดึงข้อมูลและจัดเก็บไว้ในหน่วยความจำแคช จากนั้นผู้ใช้อีกสองคนก็ใช้สิ่งนั้นจากแคชโดยไม่ต้องเข้าถึงฐานข้อมูลเพื่อดึงข้อมูลโดยไม่จำเป็น

ประเภทของแคช

โดยพื้นฐานแล้ว การแคชที่ .NET Core รองรับมีอยู่สองประเภท

  1. การแคชในหน่วยความจำ
  2. แคชแบบกระจาย

เมื่อเราใช้ In-Memory Cache ข้อมูลจะถูกจัดเก็บไว้ในหน่วยความจำของแอปพลิเคชันเซิร์ฟเวอร์ เมื่อใดก็ตามที่เราต้องการ เราจะดึงข้อมูลจากสิ่งนั้นและนำไปใช้ทุกที่ที่เราต้องการ และใน Distributed Caching มีกลไกของบุคคลที่สามมากมาย เช่น Redis และอื่นๆ อีกมากมาย แต่ในส่วนนี้ เราจะมาดูรายละเอียดเกี่ยวกับ Redis Cache และวิธีการทำงานใน .NET Core

แคชแบบกระจาย

การใช้ Azure Redis Cache กับ .NET 6 Web API:คำแนะนำทีละขั้นตอน

  • โดยพื้นฐานแล้ว ข้อมูลแคชแบบกระจายจะถูกจัดเก็บและแบ่งปันระหว่างเซิร์ฟเวอร์หลายเครื่อง
  • นอกจากนี้ ยังง่ายต่อการปรับปรุงความสามารถในการปรับขนาดและประสิทธิภาพของแอปพลิเคชันหลังจากจัดการโหลดระหว่างเซิร์ฟเวอร์หลายเครื่องเมื่อเราใช้แอปพลิเคชันที่มีผู้เช่าหลายราย
  • สมมติว่าในอนาคต หากเซิร์ฟเวอร์ตัวใดตัวหนึ่งขัดข้องและรีสตาร์ท แอปพลิเคชันจะไม่ได้รับผลกระทบใดๆ เนื่องจากเซิร์ฟเวอร์หลายตัวเป็นไปตามความต้องการของเราหากเราต้องการ

Redis เป็นแคชที่ได้รับความนิยมมากที่สุดซึ่งหลายบริษัทใช้ในปัจจุบันเพื่อปรับปรุงประสิทธิภาพและความสามารถในการปรับขนาดของแอปพลิเคชัน ดังนั้น เราจะมาหารือเกี่ยวกับ Redis และการใช้งานของมันทีละรายการ

เรดดิสแคช

  • Redis คือที่จัดเก็บโครงสร้างข้อมูลในหน่วยความจำแบบโอเพนซอร์ส (BSD ที่ได้รับอนุญาต) ซึ่งใช้เป็นฐานข้อมูล
  • โดยพื้นฐานแล้ว มันถูกใช้เพื่อจัดเก็บข้อมูลที่ใช้บ่อยและข้อมูลคงที่บางส่วนภายในแคช และใช้และสงวนไว้ตามความต้องการของผู้ใช้
  • มีโครงสร้างข้อมูลมากมายใน Redis ที่เราสามารถใช้ได้ เช่น รายการ ตั้งค่า การแฮช สตรีม และอื่นๆ อีกมากมายเพื่อจัดเก็บข้อมูล

การตั้งค่าแคช Azure Redis

ขั้นตอนที่ 1

เข้าสู่ระบบพอร์ทัล Azure

ขั้นตอนที่ 2

ค้นหา Azure Cache สำหรับ Redis ในตลาดกลางแล้วเปิด

การใช้ Azure Redis Cache กับ .NET 6 Web API:คำแนะนำทีละขั้นตอน

ขั้นตอนที่ 3

คลิกที่สร้างและระบุข้อมูลอื่นๆ

การใช้ Azure Redis Cache กับ .NET 6 Web API:คำแนะนำทีละขั้นตอน

การใช้ Azure Redis Cache กับ .NET 6 Web API:คำแนะนำทีละขั้นตอน

การใช้ Azure Redis Cache กับ .NET 6 Web API:คำแนะนำทีละขั้นตอน

การใช้ Azure Redis Cache กับ .NET 6 Web API:คำแนะนำทีละขั้นตอน

ขั้นตอนที่ 4

หลังจากนั้นไปที่ส่วนคีย์การเข้าถึงภายในแคชที่เราสร้างไว้ก่อนหน้านี้ และคัดลอกสตริงการเชื่อมต่อหลักที่เราต้องการภายใน .NET Core Web API

การใช้ Azure Redis Cache กับ .NET 6 Web API:คำแนะนำทีละขั้นตอน

การใช้งานทีละขั้นตอน

ขั้นตอนที่ 1

เปิด Visual Studio และสร้างโปรเจ็กต์ .NET Core Web API ใหม่

การใช้ Azure Redis Cache กับ .NET 6 Web API:คำแนะนำทีละขั้นตอน

ขั้นตอนที่ 2

กำหนดค่าโครงการใหม่

การใช้ Azure Redis Cache กับ .NET 6 Web API:คำแนะนำทีละขั้นตอน

ขั้นตอนที่ 3

ระบุรายละเอียดเพิ่มเติม

การใช้ Azure Redis Cache กับ .NET 6 Web API:คำแนะนำทีละขั้นตอน

ขั้นตอนที่ 4

โครงสร้างโครงการ

การใช้ Azure Redis Cache กับ .NET 6 Web API:คำแนะนำทีละขั้นตอน

ขั้นตอนที่ 5

สร้างคลาสรายละเอียดผลิตภัณฑ์

namespace AzureRedisCacheDemo.Models {
 public class ProductDetails {
 public int Id {
 get;
 set;
 }
 public string ProductName {
 get;
 set;
 }
 public string ProductDescription {
 get;
 set;
 }
 public int ProductPrice {
 get;
 set;
 }
 public int ProductStock {
 get;
 set;
 }
 }
}

ขั้นตอนที่ 6

ถัดไป เพิ่ม Db Context Class ภายในโฟลเดอร์ Data

using AzureRedisCacheDemo.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Hosting;
namespace AzureRedisCacheDemo.Data {
 public class DbContextClass: DbContext {
 public DbContextClass(DbContextOptions < DbContextClass > options): base(options) {}
 public DbSet < ProductDetails > Products {
 get;
 set;
 }
 }
}

ขั้นตอนที่ 7

หลังจากนั้นให้เพิ่มคลาส Seed Data ซึ่งเราใช้ในการแทรกข้อมูลบางส่วนในตอนแรก

using AzureRedisCacheDemo.Models;
using Microsoft.EntityFrameworkCore;
namespace AzureRedisCacheDemo.Data
{
 public class SeedData
 {
 public static void Initialize(IServiceProvider serviceProvider)
 {
 using (var context = new DbContextClass(
 serviceProvider.GetRequiredService<DbContextOptions<DbContextClass>>()))
 {
 if (context.Products.Any())
 {
 return;
 }
 context.Products.AddRange(
 new ProductDetails
 {
 Id = 1,
 ProductName = "IPhone",
 ProductDescription = "IPhone 14",
 ProductPrice = 120000,
 ProductStock = 100
 },
 new ProductDetails
 {
 Id = 2,
 ProductName = "Samsung TV",
 ProductDescription = "Smart TV",
 ProductPrice = 400000,
 ProductStock = 120
 });
 context.SaveChanges();
 }
 }
 }
}

ขั้นตอนที่ 8

กำหนดค่าสตริงการเชื่อมต่อ Azure Redis Cache ภายในไฟล์ appsettings.json

{
 "Logging": {
 "LogLevel": {
 "Default": "Information",
 "Microsoft.AspNetCore": "Warning"
 }
 },
 "AllowedHosts": "*",
 "RedisURL": "<valuefromportal>"
}

ขั้นตอนที่ 9

สร้างคลาส Configuration Manager และ Connection Helper ภายในโฟลเดอร์ Helper ที่เราใช้เพื่อวัตถุประสงค์ในการเชื่อมต่อ

เครื่องมือจัดการการกำหนดค่า

namespace AzureRedisCacheDemo.Helper {
 static class ConfigurationManager {
 public static IConfiguration AppSetting {
 get;
 }
 static ConfigurationManager() {
 AppSetting = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json").Build();
 }
 }
}

ตัวช่วยการเชื่อมต่อ

using StackExchange.Redis;
namespace AzureRedisCacheDemo.Helper {
 public class ConnectionHelper {
 static ConnectionHelper() {
 ConnectionHelper.lazyConnection = new Lazy < ConnectionMultiplexer > (() => {
 return ConnectionMultiplexer.Connect(ConfigurationManager.AppSetting["RedisURL"]);
 });
 }
 private static Lazy < ConnectionMultiplexer > lazyConnection;
 public static ConnectionMultiplexer Connection {
 get {
 return lazyConnection.Value;
 }
 }
 }
}

ขั้นตอนที่ 10

ถัดไป เพิ่ม IProductService ภายในที่เก็บ

using AzureRedisCacheDemo.Models;
namespace AzureRedisCacheDemo.Repositories {
 public interface IProductService {
 public Task < List < ProductDetails >> ProductListAsync();
 public Task < ProductDetails > GetProductDetailByIdAsync(int productId);
 public Task < bool > AddProductAsync(ProductDetails productDetails);
 public Task < bool > UpdateProductAsync(ProductDetails productDetails);
 public Task < bool > DeleteProductAsync(int productId);
 }
}

ขั้นตอนที่ 11

จากนั้น สร้างคลาส ProductService และใช้อินเทอร์เฟซ IProductService ภายในนั้น

using AzureRedisCacheDemo.Data;
using AzureRedisCacheDemo.Models;
using Microsoft.EntityFrameworkCore;
using System.Linq;
namespace AzureRedisCacheDemo.Repositories {
 public class ProductService: IProductService {
 private readonly DbContextClass dbContextClass;
 public ProductService(DbContextClass dbContextClass) {
 this.dbContextClass = dbContextClass;
 }
 public async Task < List < ProductDetails >> ProductListAsync() {
 return await dbContextClass.Products.ToListAsync();
 }
 public async Task < ProductDetails > GetProductDetailByIdAsync(int productId) {
 return await dbContextClass.Products.Where(ele => ele.Id == productId).FirstOrDefaultAsync();
 }
 public async Task < bool > AddProductAsync(ProductDetails productDetails) {
 await dbContextClass.Products.AddAsync(productDetails);
 var result = await dbContextClass.SaveChangesAsync();
 if (result > 0) {
 return true;
 } else {
 return false;
 }
 }
 public async Task < bool > UpdateProductAsync(ProductDetails productDetails) {
 var isProduct = ProductDetailsExists(productDetails.Id);
 if (isProduct) {
 dbContextClass.Products.Update(productDetails);
 var result = await dbContextClass.SaveChangesAsync();
 if (result > 0) {
 return true;
 } else {
 return false;
 }
 }
 return false;
 }
 public async Task < bool > DeleteProductAsync(int productId) {
 var findProductData = dbContextClass.Products.Where(_ => _.Id == productId).FirstOrDefault();
 if (findProductData != null) {
 dbContextClass.Products.Remove(findProductData);
 var result = await dbContextClass.SaveChangesAsync();
 if (result > 0) {
 return true;
 } else {
 return false;
 }
 }
 return false;
 }
 private bool ProductDetailsExists(int productId) {
 return dbContextClass.Products.Any(e => e.Id == productId);
 }
 }
}

ขั้นตอนที่ 12

สร้างอินเทอร์เฟซ IRedisCache

namespace AzureRedisCacheDemo.Repositories.AzureRedisCache {
 public interface IRedisCache {
 T GetCacheData < T > (string key);
 bool SetCacheData < T > (string key, T value, DateTimeOffset expirationTime);
 object RemoveData(string key);
 }
}

ขั้นตอนที่ 13

หลังจากนั้น ให้สร้างคลาส RedisCache และใช้วิธีการอินเทอร์เฟซภายในที่เราสร้างไว้ก่อนหน้านี้

using AzureRedisCacheDemo.Helper;
using Newtonsoft.Json;
using StackExchange.Redis;
namespace AzureRedisCacheDemo.Repositories.AzureRedisCache
{
 public class RedisCache : IRedisCache
 {
 private IDatabase _db;
 public RedisCache()
 {
 ConfigureRedis();
 }
 private void ConfigureRedis()
 {
 _db = ConnectionHelper.Connection.GetDatabase();
 }
 public T GetCacheData<T>(string key)
 {
 var value = _db.StringGet(key);
 if (!string.IsNullOrEmpty(value))
 {
 return JsonConvert.DeserializeObject<T>(value);
 }
 return default;
 }
 public object RemoveData(string key)
 {
 bool _isKeyExist = _db.KeyExists(key);
 if (_isKeyExist == true)
 {
 return _db.KeyDelete(key);
 }
 return false;
 }
 public bool SetCacheData<T>(string key, T value, DateTimeOffset expirationTime)
 {
 TimeSpan expiryTime = expirationTime.DateTime.Subtract(DateTime.Now);
 var isSet = _db.StringSet(key, JsonConvert.SerializeObject(value), expiryTime);
 return isSet;
 }
 }
}

ขั้นตอนที่ 14

สร้างตัวควบคุมผลิตภัณฑ์ใหม่

using AzureRedisCacheDemo.Models;
using AzureRedisCacheDemo.Repositories;
using AzureRedisCacheDemo.Repositories.AzureRedisCache;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
namespace AzureRedisCacheDemo.Controllers
{
 [Route("api/[controller]")]
 [ApiController]
 public class ProductsController : ControllerBase
 {
 private readonly IProductService _productService;
 private readonly IRedisCache _redisCache;
 public ProductsController(IProductService productService, IRedisCache redisCache)
 {
 _productService = productService;
 _redisCache = redisCache;
 }
 /// <summary>
 /// Product List
 /// </summary>
 /// <returns></returns>
 [HttpGet]
 public async Task<ActionResult<List<ProductDetails>>> ProductListAsync()
 {
 var cacheData = _redisCache.GetCacheData<List<ProductDetails>>("product");
 if (cacheData != null)
 {
 return new List<ProductDetails>(cacheData);
 }
 var productList = await _productService.ProductListAsync();
 if(productList != null)
 {
 var expirationTime = DateTimeOffset.Now.AddMinutes(5.0);
 _redisCache.SetCacheData<List<ProductDetails>>("product", productList, expirationTime);
 return Ok(productList);
 }
 else
 {
 return NoContent();
 }
 }
 /// <summary>
 /// Get Product By Id
 /// </summary>
 /// <param name="productId"></param>
 /// <returns></returns>
 [HttpGet("{productId}")]
 public async Task<ActionResult<ProductDetails>> GetProductDetailsByIdAsync(int productId)
 {
 var cacheData = _redisCache.GetCacheData<List<ProductDetails>>("product");
 if (cacheData != null)
 {
 ProductDetails filteredData = cacheData.Where(x => x.Id == productId).FirstOrDefault();
 return new ActionResult<ProductDetails>(filteredData);
 }
 var productDetails = await _productService.GetProductDetailByIdAsync(productId);
 if(productDetails != null)
 {
 return Ok(productDetails);
 }
 else
 {
 return NotFound();
 }
 }
 /// <summary>
 /// Add a new product
 /// </summary>
 /// <param name="productDetails"></param>
 /// <returns></returns>
 [HttpPost]
 public async Task<IActionResult> AddProductAsync(ProductDetails productDetails)
 {
 var isProductInserted = await _productService.AddProductAsync(productDetails);
 _redisCache.RemoveData("product");
 if (isProductInserted)
 {
 return Ok(isProductInserted);
 }
 else
 {
 return BadRequest();
 }
 }
 /// <summary>
 /// Update product details
 /// </summary>
 /// <param name="productDetails"></param>
 /// <returns></returns>
 [HttpPut]
 public async Task<IActionResult> UpdateProductAsync(ProductDetails productDetails)
 {
 var isProductUpdated = await _productService.UpdateProductAsync(productDetails);
 _redisCache.RemoveData("product");
 if (isProductUpdated)
 {
 return Ok(isProductUpdated);
 }
 else
 {
 return BadRequest();
 }
 }
 /// <summary>
 /// Delete product by id
 /// </summary>
 /// <param name="productId"></param>
 /// <returns></returns>
 [HttpDelete]
 public async Task<IActionResult> DeleteProductAsync(int productId)
 {
 var isProductDeleted = await _productService.DeleteProductAsync(productId);
 _redisCache.RemoveData("product");
 if (isProductDeleted)
 {
 return Ok(isProductDeleted);
 }
 else
 {
 return BadRequest();
 }
 }
 }
}

ขั้นตอนที่ 15

ลงทะเบียนบริการบางอย่างภายในคลาสโปรแกรม

using AzureRedisCacheDemo.Data;
using AzureRedisCacheDemo.Models;
using AzureRedisCacheDemo.Repositories;
using AzureRedisCacheDemo.Repositories.AzureRedisCache;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Hosting;
using System;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddScoped < IProductService, ProductService > ();
builder.Services.AddDbContext < DbContextClass > (o => o.UseInMemoryDatabase("RedisCacheDemo"));
builder.Services.AddScoped < IRedisCache, RedisCache > ();
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
using(var scope = app.Services.CreateScope()) {
 var services = scope.ServiceProvider;
 var context = services.GetRequiredService < DbContextClass > ();
 SeedData.Initialize(services);
}
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment()) {
 app.UseSwagger();
 app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

ขั้นตอนที่ 16

สุดท้าย ให้เรียกใช้แอปพลิเคชัน แล้วเราจะเห็น UI ที่ผยองพร้อมกับจุดสิ้นสุดของ API ของเรา

การใช้ Azure Redis Cache กับ .NET 6 Web API:คำแนะนำทีละขั้นตอน

ขั้นตอนที่ 17

กดปุ่มรับตำแหน่งข้อมูลผลิตภัณฑ์แล้วเปิด Redis CLI ภายใน Azure Portal จากนั้นคุณจะเห็นรายการผลิตภัณฑ์จะถูกจัดเก็บเมื่อเราไปถึงตำแหน่งข้อมูลในครั้งแรก

การใช้ Azure Redis Cache กับ .NET 6 Web API:คำแนะนำทีละขั้นตอน

ในกรณีนี้ อันดับแรก เราจะตรวจสอบว่าข้อมูลมีอยู่ในแคชหรือไม่ ถ้าไม่เช่นนั้นเราจะรับข้อมูลจากฐานข้อมูลและบันทึกลงในแคชด้วย เราได้เขียนโค้ดที่เกี่ยวข้องกับสิ่งนั้นภายในคอนโทรลเลอร์แล้ว ดังนั้นครั้งต่อไปที่เรารับข้อมูลจากแคช หากคุณใส่ดีบักเกอร์ไว้ในคอนโทรลเลอร์ คุณจะเข้าใจได้ง่ายว่าสิ่งต่างๆ จะทำงานอย่างไร

URL ของ GitHub

https://github.com/Jaydeep-007/AzureRedisCacheDemo/tree/master/AzureRedisCacheDemo

บทสรุป

ที่นี่เรามาดูการแนะนำแคชและการกำหนดค่าบน Azure นอกจากนี้ การใช้งานทีละขั้นตอนโดยใช้ .NET Core Web API