Redis/NewLife.Redis/RedisStack.cs

119 lines
4.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using NewLife.Data;
namespace NewLife.Caching;
/// <summary>Redis栈右进右出</summary>
/// <typeparam name="T"></typeparam>
public class RedisStack<T> : RedisBase, IProducerConsumer<T>
{
#region
/// <summary>个数</summary>
public Int32 Count => Execute((r, k) => r.Execute<Int32>("LLEN", Key));
/// <summary>是否为空</summary>
public Boolean IsEmpty => Count == 0;
/// <summary>最小管道阈值达到该值时使用管道默认3</summary>
public Int32 MinPipeline { get; set; } = 3;
#endregion
#region
/// <summary>实例化队列</summary>
/// <param name="redis"></param>
/// <param name="key"></param>
public RedisStack(Redis redis, String key) : base(redis, key) { }
#endregion
/// <summary>批量生产添加</summary>
/// <param name="values"></param>
/// <returns></returns>
public Int32 Add(params T[] values)
{
var args = new List<Object> { Key };
foreach (var item in values)
{
args.Add(item);
}
return Execute((rc, k) => rc.Execute<Int32>("RPUSH", args.ToArray()), true);
}
/// <summary>批量消费获取</summary>
/// <param name="count"></param>
/// <returns></returns>
public IEnumerable<T> Take(Int32 count = 1)
{
if (count <= 0) yield break;
// 借助管道支持批量获取
if (count >= MinPipeline)
{
var rds = Redis;
rds.StartPipeline();
for (var i = 0; i < count; i++)
{
Execute((rc, k) => rc.Execute<T>("RPOP", Key), true);
}
var rs = rds.StopPipeline(true);
foreach (var item in rs)
{
if (item is null || Equals(item, default(T))) { break; }
yield return (T)item;
}
}
else
{
for (var i = 0; i < count; i++)
{
var value = Execute((rc, k) => rc.Execute<T>("RPOP", Key), true);
if (value is null || Equals(value, default(T))) break;
yield return value;
}
}
}
/// <summary>消费获取,支持阻塞</summary>
/// <param name="timeout">超时0秒永远阻塞负数表示直接返回不阻塞。</param>
/// <returns></returns>
public T? TakeOne(Int32 timeout = -1)
{
if (timeout < 0) return Execute((rc, k) => rc.Execute<T>("RPOP", Key), true);
var rs = Execute((rc, k) => rc.Execute<IPacket[]>("BRPOP", Key, timeout), true);
if (rs == null || rs.Length < 2) return default;
var msg = (T?)Redis.Encoder.Decode(rs[1], typeof(T));
if (typeof(T) != typeof(IPacket)) rs.TryDispose();
return msg;
}
/// <summary>异步消费获取</summary>
/// <param name="timeout">超时时间默认0秒永远阻塞负数表示直接返回不阻塞。</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns></returns>
public async Task<T?> TakeOneAsync(Int32 timeout = 0, CancellationToken cancellationToken = default)
{
if (timeout < 0) return await ExecuteAsync((rc, k) => rc.ExecuteAsync<T>("RPOP", Key), true).ConfigureAwait(false);
var rs = await ExecuteAsync((rc, k) => rc.ExecuteAsync<IPacket[]>("BRPOP", [Key, timeout], cancellationToken), true).ConfigureAwait(false);
if (rs == null || rs.Length < 2) return default;
var msg = (T?)Redis.Encoder.Decode(rs[1], typeof(T));
if (typeof(T) != typeof(IPacket)) rs.TryDispose();
return msg;
}
/// <summary>异步消费获取</summary>
/// <param name="timeout">超时时间默认0秒永远阻塞负数表示直接返回不阻塞。</param>
/// <returns></returns>
Task<T?> IProducerConsumer<T>.TakeOneAsync(Int32 timeout) => TakeOneAsync(timeout, default);
Int32 IProducerConsumer<T>.Acknowledge(params String[] keys) => throw new NotSupportedException();
}