c#实现redis的调用与基础类

网上对于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"
    }
  }
}
相关推荐
苦学编程的谢2 小时前
Redis_7_hash
数据库·redis·哈希算法
许愿OvO2 小时前
MySQL-索引
数据库·mysql
-指短琴长-2 小时前
MySQL快速入门——基本查询(上)
android·数据库·mysql
Yeats_Liao3 小时前
时序数据库系列(四):InfluxQL查询语言详解
数据库·后端·sql·时序数据库
白衣鸽子3 小时前
MySQL数据库的“隐形杀手”:深入理解文件结构与治理数据碎片
数据库·后端·mysql
IvanCodes3 小时前
openGauss安装部署详细教程
大数据·数据库·sql·opengauss
王道长服务器 | 亚马逊云3 小时前
AWS + 苹果CMS:影视站建站的高效组合方案
服务器·数据库·搜索引擎·设计模式·云计算·aws
java干货3 小时前
MySQL “灵异事件”:我 INSERT id=11,为什么被 UPDATE id=10 锁住了?
android·数据库·mysql
記億揺晃着的那天4 小时前
数据库中 ACID 四个核心特性
数据库·mysql·oracle·系统设计·acid