网上对于redis的操作有很多文档,但大多太复杂,对于初识者来说有门槛。鉴于我碰到的这种情况,特地写一个redis与c#的基础控制类,仅供参考;
我的环境是.net9+webapi 本文件适用于.netcore的任何程序,包括控制台
安装
首先需要安装依赖包:stackexchange.redis
控制台执行:install package stackexchange.redis
安装完了如图:

演示示例
编写string类型的增加,查询,删除,以及覆盖的逻辑
cs
/// <summary>
/// 写入字符串到 Redis
/// </summary>
/// <param name="key">Redis 键名</param>
/// <param name="value">要存入的字符串值</param>
/// <param name="expireMinutes">过期时间(分钟),0 表示不过期</param>
[HttpPost("string/set")]
public async Task<IActionResult> SetString([FromQuery] string key, [FromQuery] string value, [FromQuery] int expireMinutes)
{
if (!await _redis.CheckRateLimitAsync($"rate:string:set:{userId}", 10, TimeSpan.FromMinutes(1)))
return BadRequest("访问过于频繁,请稍后再试");
TimeSpan? expiry = expireMinutes > 0 ? TimeSpan.FromMinutes(expireMinutes) : null;
bool success = await _redis.SetStringAsync(key, value, expiry);
return Ok(new { key, value, success });
}
/// <summary>
/// 获取 Redis 中的字符串
/// </summary>
/// <param name="key">Redis 键名</param>
[HttpGet("string/get")]
public async Task<IActionResult> GetString([FromQuery] string key)
{
var value = await _redis.GetStringRawAsync(key);
return Ok(new { key, value = value.ToString() });
}
/// <summary>
/// 删除 Redis 键
/// </summary>
/// <param name="key">Redis 键名</param>
[HttpDelete("string/delete")]
public async Task<IActionResult> DeleteKey([FromQuery] string key)
{
bool deleted = await _redis.DeleteKeyAsync(key);
return Ok(new { key, deleted });
}
赋值
启动服务;在api文档中调用:

请求之后,去数据库查看是否成功写入:

请求次数:限流使用,在调用CheckRateLimitAsync事件的时候设置了key为1,在达到指定测试会return报错提示,示例: 多次请求后数量累加:

查询
接口中查询看结果:

修改
如果需要更新值,设置一下When关键字,看文档可知:
always:默认值,只要有set事件就覆盖原值,也就达到了修改的目的
exists:只有值存在才修改
notexists:只有值不存在才添加
cs
namespace StackExchange.Redis
{
/// <summary>
/// Indicates when this operation should be performed (only some variations are legal in a given context).
/// </summary>
public enum When
{
/// <summary>
/// The operation should occur whether or not there is an existing value.
/// </summary>
Always,
/// <summary>
/// The operation should only occur when there is an existing value.
/// </summary>
Exists,
/// <summary>
/// The operation should only occur when there is not an existing value.
/// </summary>
NotExists,
}
}
具体设置代码:
cs
_db.StringSetAsync(key, value, expiry, When.Always);
目前设置的是always
请求覆盖测试:

数据库查看:

修改成功。同时Exists和NotExists方法效果可自行测试。
删除
api请求测试:

数据库查看:

到此为止已演示整体功能和设置。
除string类型还添加了其他更多类型不再演示,调用方式类似,我把文件上传了,可下载调试,免费的,如被设置收费就是csdn搞的鬼
https://download.csdn.net/download/qq_53217825/92261067
不想下在的可以往下翻看实现
代码查看
program代码:
cs
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddOpenApi();
// 注册 RedisService(构造函数读取 IConfiguration)
builder.Services.AddSingleton<RedisService>();
// 注册后台服务(如果你需要延迟队列 / 队列消费者)
builder.Services.AddHostedService<DelayQueueBackgroundService>();
builder.Services.AddHostedService<MessageQueueBackgroundService>();
builder.Services.AddLogging(config => config.AddConsole());
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.UseAuthorization();
app.MapControllers();
app.Run();
其中redis配置为:
cs
这个为核心注册单例
// 注册 RedisService(构造函数读取 IConfiguration)
builder.Services.AddSingleton<RedisService>();
这里为延迟队列,按需添加
// 注册后台服务(如果你需要延迟队列 / 队列消费者)
builder.Services.AddHostedService<DelayQueueBackgroundService>();
builder.Services.AddHostedService<MessageQueueBackgroundService>();
同时配置RedisService服务:
以下包括限流设置,string、hash、set、list、sortedSet 等常用设置
cs
using Microsoft.Extensions.Configuration;
using StackExchange.Redis;
using System.Text.Json;
using System.Security.Cryptography;
using System.Text;
public class RedisService : IDisposable
{
private readonly IConnectionMultiplexer _conn;
private readonly IDatabase _db;
private readonly IConfiguration _config;
// Lua 脚本:原子从 ZSET 中弹出最多 N 个 score <= now 的成员
// KEYS[1] = zsetKey, ARGV[1] = now(ms), ARGV[2] = batch
private const string PopDueLua = @"
local key = KEYS[1]
local maxScore = ARGV[1]
local count = tonumber(ARGV[2])
local items = redis.call('ZRANGEBYSCORE', key, '-inf', maxScore, 'LIMIT', 0, count)
if #items == 0 then
return {}
end
for i, v in ipairs(items) do
redis.call('ZREM', key, v)
end
return items
";
public RedisService(IConfiguration configuration)
{
_config = configuration;
var connStr = _config.GetValue<string>("Redis:ConnectionString") ?? "127.0.0.1:6379";
var opts = ConfigurationOptions.Parse(connStr);
opts.AbortOnConnectFail = false;
_conn = ConnectionMultiplexer.Connect(opts);
_db = _conn.GetDatabase(_config.GetValue<int>("Redis:Db")); // 默认 db = 0;如需其它 db,可调用 _conn.GetDatabase(index)
}
#region --- 设置redis ---
/// <summary>
/// 检查指定 key 是否超出限流次数
/// </summary>
/// <param name="key">限流标识 key(可根据用户、接口等自定义)</param>
/// <param name="limit">最大访问次数</param>
/// <param name="window">时间窗口</param>
/// <returns>true 表示未超限,false 表示超限</returns>
public async Task<bool> CheckRateLimitAsync(string key, int limit, TimeSpan window)
{
var count = await _db.StringIncrementAsync(key);
if (count == 1)
await _db.KeyExpireAsync(key, window);
return count <= limit;
}
#endregion
#region --- 基础 String / Object ---
/// <summary>
/// 写入普通字符串到 Redis
/// </summary>
/// <param name="key">Redis 键名</param>
/// <param name="value">要写入的字符串值</param>
/// <param name="expiry">可选过期时间,null 表示不过期</param>
/// <returns>返回 true 表示写入成功,false 表示失败</returns>
// 注意:此操作没有分布式锁或限流,直接写入 Redis
public Task<bool> SetStringAsync(string key, string value, TimeSpan? expiry = null)
=> _db.StringSetAsync(key, value, expiry);
/// <summary>
/// 写入普通字符串到 Redis
/// </summary>
/// <param name="key">Redis 键名</param>
/// <param name="value">要写入的字符串值</param>
/// <param name="expiry">可选过期时间,null 表示不过期</param>
/// <returns>返回 true 表示写入成功,false 表示失败</returns>
// 注意:此操作没有分布式锁或限流,直接写入 Redis
public Task<bool> SetStringNotExistsAsync(string key, string value, TimeSpan? expiry = null)
=> _db.StringSetAsync(key, value, expiry, When.NotExists);
/// <summary>
/// 从 Redis 获取字符串值(原始值)
/// </summary>
/// <param name="key">Redis 键名</param>
/// <returns>返回 RedisValue,如果键不存在返回 Null</returns>
// 此操作无需锁,直接读取
public Task<RedisValue> GetStringRawAsync(string key)
=> _db.StringGetAsync(key);
/// <summary>
/// 删除指定键
/// </summary>
/// <param name="key">Redis 键名</param>
/// <returns>返回 true 表示删除成功,false 表示键不存在</returns>
// 可与分布式锁配合使用,避免并发删除造成冲突
public Task<bool> DeleteKeyAsync(string key)
=> _db.KeyDeleteAsync(key);
/// <summary>
/// 写入对象到 Redis(序列化为 JSON 字符串)
/// </summary>
/// <typeparam name="T">对象类型</typeparam>
/// <param name="key">Redis 键名</param>
/// <param name="obj">要写入的对象</param>
/// <param name="expiry">可选过期时间</param>
/// <returns>返回 true 表示写入成功</returns>
// 无分布式锁/限流,需要并发安全请在调用前加锁
public Task<bool> SetObjectAsync<T>(string key, T obj, TimeSpan? expiry = null)
=> _db.StringSetAsync(key, JsonSerializer.Serialize(obj), expiry);
/// <summary>
/// 从 Redis 读取对象(JSON 反序列化为指定类型)
/// </summary>
/// <typeparam name="T">对象类型</typeparam>
/// <param name="key">Redis 键名</param>
/// <returns>返回对象,如果键不存在或值为空返回 default(T)</returns>
// 此操作无需锁
public async Task<T?> GetObjectAsync<T>(string key)
{
var v = await _db.StringGetAsync(key);
return v.IsNullOrEmpty ? default : JsonSerializer.Deserialize<T>(v!);
}
#endregion
#region --- Hash 操作 ---
/// <summary>
/// 设置哈希表字段,可一次性设置多个字段
/// </summary>
/// <param name="key">Redis 哈希表的键名</param>
/// <param name="fields">要设置的字段数组 HashEntry[]</param>
/// <returns>返回任务对象,完成后表示操作已执行</returns>
// 无分布式锁,建议在高并发场景下加锁或使用事务
public Task HashSetAsync(string key, HashEntry[] fields)
=> _db.HashSetAsync(key, fields);
/// <summary>
/// 获取哈希表所有字段及对应值
/// </summary>
/// <param name="key">Redis 哈希表的键名</param>
/// <returns>返回 HashEntry[] 数组,如果键不存在返回空数组</returns>
// 读取操作,无锁
public Task<HashEntry[]> HashGetAllAsync(string key)
=> _db.HashGetAllAsync(key);
/// <summary>
/// 获取哈希表指定字段的值
/// </summary>
/// <param name="key">Redis 哈希表的键名</param>
/// <param name="field">要获取的字段名</param>
/// <returns>返回字段值,如果字段不存在返回 Null</returns>
// 读取操作,无锁
public Task<RedisValue> HashGetAsync(string key, string field)
=> _db.HashGetAsync(key, field);
/// <summary>
/// 删除哈希表中指定字段
/// </summary>
/// <param name="key">Redis 哈希表的键名</param>
/// <param name="field">要删除的字段名</param>
/// <returns>返回 true 表示字段删除成功,false 表示字段不存在</returns>
// 删除操作,可配合分布式锁使用,保证并发安全
public Task<bool> HashDeleteAsync(string key, string field)
=> _db.HashDeleteAsync(key, field);
#endregion
#region --- Set 操作 ---
/// <summary>
/// 向 Set 添加成员
/// </summary>
/// <param name="key">Redis Set 的键名</param>
/// <param name="member">要添加的成员</param>
/// <returns>返回 true 表示添加成功,false 表示成员已存在</returns>
// 可用于实现分布式去重,无需额外锁
public Task<bool> SetAddAsync(string key, string member)
=> _db.SetAddAsync(key, member);
/// <summary>
/// 判断成员是否存在于 Set 中
/// </summary>
/// <param name="key">Redis Set 的键名</param>
/// <param name="member">要判断的成员</param>
/// <returns>返回 true 表示存在,false 表示不存在</returns>
// 读取操作,无锁
public Task<bool> SetContainsAsync(string key, string member)
=> _db.SetContainsAsync(key, member);
/// <summary>
/// 从 Set 中移除指定成员
/// </summary>
/// <param name="key">Redis Set 的键名</param>
/// <param name="member">要移除的成员</param>
/// <returns>返回 true 表示移除成功,false 表示成员不存在</returns>
// 移除操作,可配合分布式锁使用
public Task<bool> SetRemoveAsync(string key, string member)
=> _db.SetRemoveAsync(key, member);
#endregion
#region --- 普通消息队列(List) ---
/// <summary>
/// 推入队列(右侧入队)
/// </summary>
// 队列入队操作,可配合限流使用,确保高并发下不会刷爆队列
public Task<long> EnqueueAsync(string queueKey, string message)
=> _db.ListRightPushAsync(queueKey, message);
/// <summary>
/// 非阻塞取出(左侧出队),队列空时返回 Null
/// </summary>
// 出队操作,可配合分布式锁保证单消费者处理任务安全
public Task<RedisValue> DequeueAsync(string queueKey)
=> _db.ListLeftPopAsync(queueKey);
/// <summary>
/// 阻塞取出(BRPOP/BLPOP 风格)
/// </summary>
// 阻塞队列操作,可配合限流或分布式锁
public async Task<RedisValue?> BlockingDequeueAsync(string queueKey, int timeoutSeconds = 0)
{
var res = await _db.ExecuteAsync("BLPOP", queueKey, timeoutSeconds);
if (res.IsNull) return null;
var arr = (RedisResult[])res;
if (arr.Length >= 2)
{
return (RedisValue)arr[1];
}
return null;
}
#endregion
#region --- 延迟队列(SortedSet) ---
/// <summary>
/// 添加一个延迟任务
/// </summary>
// 延迟任务添加,推荐加分布式锁,避免重复入队
public Task<bool> AddDelayTaskAsync(string zsetKey, string value, TimeSpan delay)
{
double score = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() + delay.TotalMilliseconds;
return _db.SortedSetAddAsync(zsetKey, value, score);
}
/// <summary>
/// 原子弹出到期任务
/// </summary>
// 使用 Lua 脚本保证原子性,推荐配合分布式锁或限流
public async Task<RedisValue[]> PopDueDelayTasksAsync(string zsetKey, int batch = 10)
{
var nowMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString();
var result = (RedisResult[])await _db.ScriptEvaluateAsync(PopDueLua, new RedisKey[] { zsetKey }, new RedisValue[] { nowMs, batch });
return result.Select(r => (RedisValue)r).ToArray();
}
#endregion
#region --- 分布式锁 ---
/// <summary>
/// 尝试获取锁
/// </summary>
// 这是分布式锁核心方法
public Task<bool> AcquireLockAsync(string resourceKey, string token, TimeSpan expiry)
=> _db.LockTakeAsync(resourceKey, token, expiry);
/// <summary>
/// 释放锁(安全释放)
/// </summary>
// 分布式锁安全释放
public async Task<bool> ReleaseLockSafeAsync(string resourceKey, string token)
{
const string lua = @"
if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('del', KEYS[1])
else
return 0
end
";
var res = await _db.ScriptEvaluateAsync(lua, new RedisKey[] { resourceKey }, new RedisValue[] { token });
return (int)res == 1;
}
/// <summary>
/// 生成随机 token,可用于分布式锁
/// </summary>
public static string CreateLockToken() => Guid.NewGuid().ToString("N");
#endregion
#region --- 发布/订阅 ---
/// <summary>
/// 发布消息
/// </summary>
// 发布操作,可配合限流
public Task<long> PublishAsync(string channel, string message)
=> _conn.GetSubscriber().PublishAsync(channel, message);
/// <summary>
/// 订阅频道
/// </summary>
// 订阅操作无需锁,但处理回调时可能需要限流
public void Subscribe(string channel, Action<RedisChannel, RedisValue> handler)
=> _conn.GetSubscriber().Subscribe(channel, handler);
/// <summary>
/// 取消订阅
/// </summary>
// 取消订阅操作
public void Unsubscribe(string channel, Action<RedisChannel, RedisValue> handler)
=> _conn.GetSubscriber().Unsubscribe(channel, handler);
#endregion
#region --- 辅助方法 ---
/// <summary>生成任务唯一 id(如果需要把任务 body 做为 id 的话)</summary>
public static string Sha1Hex(string text)
{
using var sha = SHA1.Create();
var bytes = sha.ComputeHash(Encoding.UTF8.GetBytes(text));
return Convert.ToHexString(bytes);
}
#endregion
public void Dispose()
{
try { _conn?.Dispose(); } catch { }
}
}
在使用端:
添加了一个api调用控制器。包括全部的调用以及注释
cs
using Microsoft.AspNetCore.Mvc;
using StackExchange.Redis;
using System.Text.Json;
[ApiController]
[Route("api/redis")]
public class RedisController : ControllerBase
{
private readonly RedisService _redis;
public RedisController(RedisService redis)
{
_redis = redis;
}
int userId = 001;
#region --- String / Object 操作 ---
/// <summary>
/// 写入字符串到 Redis
/// </summary>
/// <param name="key">Redis 键名</param>
/// <param name="value">要存入的字符串值</param>
/// <param name="expireMinutes">过期时间(分钟),0 表示不过期</param>
[HttpPost("string/set")]
public async Task<IActionResult> SetString([FromQuery] string key, [FromQuery] string value, [FromQuery] int expireMinutes)
{
if (!await _redis.CheckRateLimitAsync($"rate:string:set:{userId}", 10, TimeSpan.FromMinutes(1)))
return BadRequest("访问过于频繁,请稍后再试");
TimeSpan? expiry = expireMinutes > 0 ? TimeSpan.FromMinutes(expireMinutes) : null;
bool success = await _redis.SetStringAsync(key, value, expiry);
return Ok(new { key, value, success });
}
/// <summary>
/// 获取 Redis 中的字符串
/// </summary>
/// <param name="key">Redis 键名</param>
[HttpGet("string/get")]
public async Task<IActionResult> GetString([FromQuery] string key)
{
var value = await _redis.GetStringRawAsync(key);
return Ok(new { key, value = value.ToString() });
}
/// <summary>
/// 删除 Redis 键
/// </summary>
/// <param name="key">Redis 键名</param>
[HttpDelete("string/delete")]
public async Task<IActionResult> DeleteKey([FromQuery] string key)
{
bool deleted = await _redis.DeleteKeyAsync(key);
return Ok(new { key, deleted });
}
/// <summary>
/// 写入对象到 Redis(JSON 序列化)
/// </summary>
/// <param name="key">Redis 键名</param>
/// <param name="obj">要存入的对象</param>
/// <param name="expireMinutes">过期时间(分钟),0 表示不过期</param>
[HttpPost("object/set")]
public async Task<IActionResult> SetObject([FromQuery] string key, [FromBody] object obj, [FromQuery] int expireMinutes)
{
TimeSpan? expiry = expireMinutes > 0 ? TimeSpan.FromMinutes(expireMinutes) : null;
bool success = await _redis.SetObjectAsync(key, obj, expiry);
return Ok(new { key, obj, success });
}
/// <summary>
/// 获取 Redis 中的对象(JSON 反序列化)
/// </summary>
/// <param name="key">Redis 键名</param>
[HttpGet("object/get")]
public async Task<IActionResult> GetObject([FromQuery] string key)
{
var obj = await _redis.GetObjectAsync<dynamic>(key);
return Ok(new { key, obj });
}
#endregion
#region --- Hash 操作 ---
/// <summary>
/// 设置哈希表字段
/// </summary>
/// <param name="key">Redis 哈希表键名</param>
/// <param name="fields">要设置的字段和值</param>
[HttpPost("hash/set")]
public async Task<IActionResult> HashSet([FromQuery] string key, [FromBody] HashEntry[] fields)
{
await _redis.HashSetAsync(key, fields);
return Ok(new { key, fields });
}
/// <summary>
/// 获取哈希表所有字段和值
/// </summary>
/// <param name="key">Redis 哈希表键名</param>
[HttpGet("hash/getall")]
public async Task<IActionResult> HashGetAll([FromQuery] string key)
{
var fields = await _redis.HashGetAllAsync(key);
return Ok(fields.Select(f => new { f.Name, f.Value }));
}
/// <summary>
/// 获取哈希表指定字段
/// </summary>
/// <param name="key">Redis 哈希表键名</param>
/// <param name="field">字段名</param>
[HttpGet("hash/get")]
public async Task<IActionResult> HashGet([FromQuery] string key, [FromQuery] string field)
{
var value = await _redis.HashGetAsync(key, field);
return Ok(new { key, field, value = value.ToString() });
}
/// <summary>
/// 删除哈希表字段
/// </summary>
/// <param name="key">Redis 哈希表键名</param>
/// <param name="field">字段名</param>
[HttpDelete("hash/delete")]
public async Task<IActionResult> HashDelete([FromQuery] string key, [FromQuery] string field)
{
bool deleted = await _redis.HashDeleteAsync(key, field);
return Ok(new { key, field, deleted });
}
#endregion
#region --- Set 操作 ---
/// <summary>
/// 向 Set 添加成员
/// </summary>
/// <param name="key">Redis Set 键名</param>
/// <param name="member">成员值</param>
[HttpPost("set/add")]
public async Task<IActionResult> SetAdd([FromQuery] string key, [FromQuery] string member)
{
bool added = await _redis.SetAddAsync(key, member);
return Ok(new { key, member, added });
}
/// <summary>
/// 判断成员是否存在于 Set
/// </summary>
/// <param name="key">Redis Set 键名</param>
/// <param name="member">成员值</param>
[HttpGet("set/contains")]
public async Task<IActionResult> SetContains([FromQuery] string key, [FromQuery] string member)
{
bool exists = await _redis.SetContainsAsync(key, member);
return Ok(new { key, member, exists });
}
/// <summary>
/// 从 Set 移除成员
/// </summary>
/// <param name="key">Redis Set 键名</param>
/// <param name="member">成员值</param>
[HttpDelete("set/remove")]
public async Task<IActionResult> SetRemove([FromQuery] string key, [FromQuery] string member)
{
bool removed = await _redis.SetRemoveAsync(key, member);
return Ok(new { key, member, removed });
}
#endregion
#region --- 队列操作(List) ---
/// <summary>
/// 入队(右入队)
/// </summary>
/// <param name="queueKey">队列键名</param>
/// <param name="message">要入队的消息</param>
[HttpPost("queue/enqueue")]
public async Task<IActionResult> QueueEnqueue([FromQuery] string queueKey, [FromQuery] string message)
{
long length = await _redis.EnqueueAsync(queueKey, message);
return Ok(new { queueKey, message, length });
}
/// <summary>
/// 出队(左出队,非阻塞)
/// </summary>
/// <param name="queueKey">队列键名</param>
[HttpGet("queue/dequeue")]
public async Task<IActionResult> QueueDequeue([FromQuery] string queueKey)
{
var message = await _redis.DequeueAsync(queueKey);
return Ok(new { queueKey, message = message.ToString() });
}
/// <summary>
/// 阻塞出队(可设置超时)
/// </summary>
/// <param name="queueKey">队列键名</param>
/// <param name="timeoutSeconds">超时时间,0 表示永久阻塞</param>
[HttpGet("queue/blockingdequeue")]
public async Task<IActionResult> QueueBlockingDequeue([FromQuery] string queueKey, [FromQuery] int timeoutSeconds)
{
var message = await _redis.BlockingDequeueAsync(queueKey, timeoutSeconds);
return Ok(new { queueKey, message = message?.ToString() });
}
#endregion
#region --- 延迟队列(SortedSet) ---
/// <summary>
/// 添加延迟任务
/// </summary>
/// <param name="zsetKey">SortedSet 键名</param>
/// <param name="payload">任务内容(JSON)</param>
/// <param name="delaySeconds">延迟时间(秒)</param>
[HttpPost("delay/add")]
public async Task<IActionResult> DelayAdd([FromQuery] string zsetKey, [FromBody] string payload, [FromQuery] int delaySeconds)
{
bool added = await _redis.AddDelayTaskAsync(zsetKey, payload, TimeSpan.FromSeconds(delaySeconds));
return Ok(new { zsetKey, payload, added });
}
/// <summary>
/// 弹出到期任务
/// </summary>
/// <param name="zsetKey">SortedSet 键名</param>
/// <param name="batch">最多弹出条数</param>
[HttpGet("delay/pop")]
public async Task<IActionResult> DelayPop([FromQuery] string zsetKey, [FromQuery] int batch)
{
var items = await _redis.PopDueDelayTasksAsync(zsetKey, batch);
return Ok(new { zsetKey, items });
}
#endregion
#region --- 分布式锁 ---
/// <summary>
/// 获取分布式锁
/// </summary>
/// <param name="key">锁键名</param>
/// <param name="expirySeconds">锁过期时间(秒)</param>
[HttpPost("lock/acquire")]
public async Task<IActionResult> LockAcquire([FromQuery] string key, [FromQuery] int expirySeconds)
{
string token = RedisService.CreateLockToken();
bool acquired = await _redis.AcquireLockAsync(key, token, TimeSpan.FromSeconds(expirySeconds));
return Ok(new { key, token, acquired });
}
/// <summary>
/// 释放分布式锁
/// </summary>
/// <param name="key">锁键名</param>
/// <param name="token">锁 token(获取锁时返回)</param>
[HttpPost("lock/release")]
public async Task<IActionResult> LockRelease([FromQuery] string key, [FromQuery] string token)
{
bool released = await _redis.ReleaseLockSafeAsync(key, token);
return Ok(new { key, token, released });
}
#endregion
#region --- 发布 / 订阅 ---
/// <summary>
/// 发布消息
/// </summary>
/// <param name="channel">频道名</param>
/// <param name="message">消息内容</param>
[HttpPost("pub/publish")]
public async Task<IActionResult> Publish([FromQuery] string channel, [FromQuery] string message)
{
long subscribers = await _redis.PublishAsync(channel, message);
return Ok(new { channel, message, subscribers });
}
/// <summary>
/// 订阅频道
/// </summary>
/// <param name="channel">频道名</param>
[HttpPost("pub/subscribe")]
public IActionResult Subscribe([FromQuery] string channel)
{
_redis.Subscribe(channel, (ch, msg) =>
{
Console.WriteLine($"Channel: {ch}, Message: {msg}");
});
return Ok(new { channel, subscribed = true });
}
/// <summary>
/// 取消订阅频道
/// </summary>
/// <param name="channel">频道名</param>
[HttpPost("pub/unsubscribe")]
public IActionResult Unsubscribe([FromQuery] string channel)
{
_redis.Unsubscribe(channel, (ch, msg) =>
{
Console.WriteLine($"Unsubscribed Channel: {ch}");
});
return Ok(new { channel, unsubscribed = true });
}
#endregion
}
appsetting配置端:
其中connectionString为连接地址;
Db为数据库,需特别注意,其他项可看情况添加
cs
{
"Redis": {
"ConnectionString": "127.0.0.1:6379",
"Db": 1,
"DelayQueueKey": "delayed_tasks",
"DeadLetterQueueKey": "dlq",
"RetryPrefix": "retry_count:",
"LuaPopBatchSize": 10,
"ConsumerPollIntervalMs": 500,
"MaxRetryTimes": 5
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning"
}
}
}