Setp 1: 基本環境: Link to heading
- Windows 11
- .Net Core 8.0 Web API 專案
- NuGet packages
- StackExchange.Redis
- StackExchange.Redis.Extensions.AspNetCore
- StackExchange.Redis.Extensions.Core
- StackExchange.Redis.Extensions.Newtonsoft
- Docker desktop 4.41.2
- Redis 7.4
p.s. Nuget 可能不用裝這麼多, uninstall 幾個也許還是可以 work 的,但我懶得試了XD
Step 2: 建立 Redis Cluster Link to heading
可參考我之前的筆記: 建立 Redis cluster on Docker (Windows)
這次的範例也會使用這份筆記內的 Redis Cluster 設定
Setp 3: 加入 Redis 的相關 config 到 appsetting.json 中 Link to heading
// some settings ...
"Redis": {
"Password": "mypassword",
"Endpoints": [
"localhost:7000",
"localhost:7001",
"localhost:7002",
"localhost:7003",
"localhost:7004",
"localhost:7005"
],
"ConnectTimeout": 5000,
"SyncTimeout": 5000,
"AbortOnConnectFail": false
},
// some settings ...
Password
設定為自己的 Redis Cluster 密碼Endpoints
為 cluster 的 6 個 nodes,依照原本的 redis conf 內的 ip 和 yaml 內每個節點的 port 設定
Setp 4: 建立 RedisSettings.cs Link to heading
在專案下建立 Configurations 資料夾,並在該資料夾下新增一個 RedisSettings.cs 來存放 Config 內容,column 就會依照 appsetting.json 內的設定
public class RedisSettings
{
public const string SectionName = "Redis";
public string Password { get; set; } = string.Empty;
public List<string> Endpoints { get; set; } = new();
public int ConnectTimeout { get; set; } = 5000;
public int SyncTimeout { get; set; } = 5000;
public bool AbortOnConnectFail { get; set; } = false;
}
Setp 5: 建立 ServiceCollectionExtensions.cs Link to heading
在專案下建立 Extensions 資料夾,並在該資料夾下建立 ServiceCollectionExtensions.cs,後續會在 Program.cs 來加入服務
當然也可以在 Program.cs 內直接做 AddSingleton,但用 Extension 可以增加一些異常判斷,然後看起來好像比較複雜比較厲害一點XD
public static IServiceCollection AddRedis(this IServiceCollection services, IConfiguration configuration)
{
// 使用 Configure<RedisSettings> 來綁定其他簡單的屬性
services.Configure<RedisSettings>(configuration.GetSection(RedisSettings.SectionName));
services.AddSingleton<IDatabase>(serviceProvider =>
{
// 從配置中獲取整個 RedisSettings 對象
var redisSettings = configuration.GetSection(RedisSettings.SectionName).Get<RedisSettings>();
List<string> actualEndpoints = redisSettings.Endpoints;
// 檢查是否有端點,如果沒有,則拋出異常
if (!actualEndpoints.Any())
{
throw new InvalidOperationException("No Redis endpoints found or could not be parsed from configuration.");
}
// 確保 password 即使為空也可用,如果設定為 null,StackExchange.Redis 可能會報錯
string password = redisSettings?.Password ?? string.Empty;
var configurationOptions = new ConfigurationOptions
{
Password = password,
ConnectTimeout = redisSettings?.ConnectTimeout ?? 5000,
SyncTimeout = redisSettings?.SyncTimeout ?? 5000,
AbortOnConnectFail = redisSettings?.AbortOnConnectFail ?? false,
Ssl = false, // 如果沒有使用 SSL/TLS
};
foreach (var endpoint in actualEndpoints)
{
configurationOptions.EndPoints.Add(endpoint);
}
// 執行連接
Log.Information("Connecting to Redis with endpoints: {Endpoints}", string.Join(", ", actualEndpoints));
Log.Information("Using Redis password: {PasswordPresent}", !string.IsNullOrEmpty(password) ? "Yes" : "No");
// 直接註冊 do0 對於後續使用比較方便
return ConnectionMultiplexer.Connect(configurationOptions).GetDatabase(0);
});
return services;
}
接著在 Program.cs 加入服務
builder.Services.AddRedis(builder.Configuration);
看起來如下
Step 6: 建立 IRedisService、RedisService Link to heading
在專案下建立 Services 資料夾,並在該資料夾下再建立一個 Interfaces 資料夾,接著在 Interfaces 資料夾內建立 IRedisService.cs
public interface IRedisService
{
Task<string?> GetAsync(string key);
Task<bool> SetAsync(string key, string value, TimeSpan? expiry = null);
Task<bool> DeleteAsync(string key);
}
回到上一層,在 Services 資料夾下建立 RedisService.cs,這邊就會建立針對 Redis 的操作,包含基本的 Get/Set/Delete,_logger 的部分若不需使用,也可以全部拿掉
public class RedisService : IRedisService
{
private readonly IDatabase _redisDb;
private readonly ILogger<RedisService> _logger;
public RedisService(IDatabase redisDb, ILogger<RedisService> logger)
{
_redisDb = redisDb;
_logger = logger;
}
public async Task<string?> GetAsync(string key)
{
try
{
var value = await _redisDb.StringGetAsync(key);
return value.HasValue ? value.ToString() : null;
}
catch (Exception ex)
{
_logger.LogError(ex, "Redis GET error for key '{Key}': {ErrorMessage}", key, ex.Message);
return null;
}
}
public async Task<bool> SetAsync(string key, string value, TimeSpan? expiry = null)
{
try
{
return await _redisDb.StringSetAsync(key, value, expiry);
}
catch (Exception ex)
{
_logger.LogError(ex, "Redis SET error for key '{Key}': {ErrorMessage}", key, ex.Message);
return false;
}
}
public async Task<bool> DeleteAsync(string key)
{
try
{
return await _redisDb.KeyDeleteAsync(key);
}
catch (Exception ex)
{
_logger.LogError(ex, "Redis DELETE error for key '{Key}': {ErrorMessage}", key, ex.Message);
return false;
}
}
}
接著再回到 Program.cs 將 RedisService 注入
builder.Services.AddScoped<IRedisService, RedisService>();
這步驟完成後,包含前面的 Extension,Program.cs 新增的部分會長這樣:
Step 7: 建立 RedisController.cs Link to heading
最後一步,在專案下的 Controller 資料夾建立 RedisController.cs,用來建立操作 Redis 的 API 服務
[ApiController]
[Route("api/[controller]")]
public class RedisController : ControllerBase
{
private readonly IRedisService _redisService;
public RedisController(IRedisService redisService)
{
_redisService = redisService;
}
[HttpGet("{key}")]
public async Task<ActionResult<string>> Get(string key)
{
var value = await _redisService.GetAsync(key);
if (value == null)
return NotFound();
return Ok(value);
}
[HttpPost("{key}")]
public async Task<ActionResult> Set(string key, string value)
{
var success = await _redisService.SetAsync(key, value);
if (!success)
return StatusCode(500, "Failed to set value");
return Ok();
}
[HttpPost("{key}/expire")]
public async Task<ActionResult> SetWithExpiry(string key, string Value, int ExpirySeconds)
{
var expiry = TimeSpan.FromSeconds(ExpirySeconds);
var success = await _redisService.SetAsync(key, Value, expiry);
if (!success)
return StatusCode(500, "Failed to set value");
return Ok();
}
[HttpDelete("{key}")]
public async Task<ActionResult> Delete(string key)
{
var success = await _redisService.DeleteAsync(key);
return Ok(new { Deleted = success });
}
}
最後的資料夾結構如下
RedisTest/
├── ...
├── Configurations/
│ └── RedisSettings.cs
├── Controllers/
│ └── RedisController.cs
├── Extensions/
│ └── ServiceCollectionExtensions.cs
├── Services/
│ ├── Interfaces/
│ │ └── IRedisService.cs
│ └── RedisService.cs
├── appsettings.json
└── Program.cs
Step 8: 執行測試 Link to heading
終於可以執行了,直接按 F5 來執行專案,可以看到 Swagger UI
(我不小心把 Test 寫成 Text 了… 請忽略這個不重要的小細節^^)
來操作 Post 看看,點開 Post 並點右上方的 Try it out,接著輸入要設定的 key, value,得到 Response Code 200,看來有執行成功
再來試試看 Get,輸入我們剛剛設定的 key 看看能不能拿到一樣的 value
結果也是成功的~完成!
程式碼範例 Link to heading
Github: Redis_Cluster_with_Dotnet
總結 Link to heading
網路上找到的資料並沒有設定地這麼複雜,比較多是只用一行的 Redis Config 和一行的AddSingleton 直接完成註冊,但這樣 appsetting 會比較不好看一點。
這次利用 AI 同步調整和測試,將 config 寫得整齊一點,並且透過 Extension 增加一些判定來讓整個架構更完整,所以說~善用 AI 工具真的很重要XD