//------------------------------------------------------------------------------ // 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 // 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 // CSDN博客:https://blog.csdn.net/qq_40374647 // 哔哩哔哩视频:https://space.bilibili.com/94253567 // Gitee源代码仓库:https://gitee.com/RRQM_Home // Github源代码仓库:https://github.com/RRQM // API首页:https://touchsocket.net/ // 交流QQ群:234762506 // 感谢您的下载和使用 //------------------------------------------------------------------------------ using System; using System.Threading.Tasks; using TouchSocket.Core; using TouchSocket.Resources; namespace TouchSocket.Dmtp.Redis; /// /// DmtpRedisActor 类,实现了 IDmtpRedisActor 接口。 /// 该类通过 Redis 操作,为分布式消息传输协议(Dmtp)提供演员(Actor)模型的实现。 /// internal sealed class DmtpRedisActor :DisposableObject, IDmtpRedisActor { /// /// 初始化DmtpRedisActor类的新实例。 /// /// 一个IDmtpActor接口的实现,用于处理actor的具体逻辑。 public DmtpRedisActor(IDmtpActor dmtpActor) { this.DmtpActor = dmtpActor; } /// public BytesSerializerConverter Converter { get; set; } /// public IDmtpActor DmtpActor { get; } /// public ICache ICache { get; set; } /// public int Timeout { get; set; } = 30 * 1000; /// public async Task AddAsync(string key, TValue value, int duration = 60000) { var cache = new CacheEntry(key) { Duration = TimeSpan.FromSeconds(duration) }; if (!(value is byte[])) { cache.Value = this.Converter.Serialize(null, value); } return await this.AddCacheAsync(cache).ConfigureAwait(EasyTask.ContinueOnCapturedContext); } /// public async Task AddCacheAsync(ICacheEntry entity) { return !await this.ContainsCacheAsync(entity.Key).ConfigureAwait(EasyTask.ContinueOnCapturedContext) && await this.SetCacheAsync(entity).ConfigureAwait(EasyTask.ContinueOnCapturedContext); } /// public async Task ClearCacheAsync() { var package = new RedisRequestWaitPackage { packageType = RedisPackageType.Clear }; var waitData = this.DmtpActor.WaitHandlePool.GetWaitDataAsync(package); try { using (var byteBlock = new ByteBlock(1024*64)) { var block = byteBlock; package.Package(ref block); await this.DmtpActor.SendAsync(this.m_redis_Request, byteBlock.Memory).ConfigureAwait(EasyTask.ContinueOnCapturedContext); } switch (await waitData.WaitAsync(this.Timeout).ConfigureAwait(EasyTask.ContinueOnCapturedContext)) { case WaitDataStatus.SetRunning: { if (waitData.WaitResult.Status == 1) { return; } else { throw new Exception(waitData.WaitResult.Message); } } case WaitDataStatus.Overtime: throw new TimeoutException(TouchSocketDmtpStatus.Overtime.GetDescription()); case WaitDataStatus.Canceled: case WaitDataStatus.Default: case WaitDataStatus.Disposed: default: throw new Exception(TouchSocketDmtpStatus.UnknownError.GetDescription()); } } finally { this.DmtpActor.WaitHandlePool.Destroy(waitData); } } /// public async Task ContainsCacheAsync(string key) { if (string.IsNullOrEmpty(key)) { throw new ArgumentException($"“{nameof(key)}”不能为 null 或空。", nameof(key)); } var package = new RedisRequestWaitPackage { key = key, packageType = RedisPackageType.Contains }; var waitData = this.DmtpActor.WaitHandlePool.GetWaitDataAsync(package); try { using (var byteBlock = new ByteBlock(1024*64)) { var block = byteBlock; package.Package(ref block); await this.DmtpActor.SendAsync(this.m_redis_Request, byteBlock.Memory).ConfigureAwait(EasyTask.ContinueOnCapturedContext); } switch (await waitData.WaitAsync(this.Timeout).ConfigureAwait(EasyTask.ContinueOnCapturedContext)) { case WaitDataStatus.SetRunning: { return waitData.WaitResult.Status == 1 ? true : waitData.WaitResult.Status == byte.MaxValue ? false : throw new Exception(waitData.WaitResult.Message); } case WaitDataStatus.Overtime: throw new TimeoutException(TouchSocketDmtpStatus.Overtime.GetDescription()); case WaitDataStatus.Canceled: case WaitDataStatus.Default: case WaitDataStatus.Disposed: default: throw new Exception(TouchSocketDmtpStatus.UnknownError.GetDescription()); } } finally { this.DmtpActor.WaitHandlePool.Destroy(waitData); } } /// public async Task GetAsync(string key) { var cache = await this.GetCacheAsync(key).ConfigureAwait(EasyTask.ContinueOnCapturedContext); if (cache != null) { if (cache.Value is null) { return default; } if (cache.Value is TValue value1) { return value1; } var value = (TValue)this.Converter.Deserialize(null, cache.Value, typeof(TValue)); return value; } return default; } /// public async Task> GetCacheAsync(string key) { if (string.IsNullOrEmpty(key)) { throw new ArgumentException($"“{nameof(key)}”不能为 null 或空。", nameof(key)); } var package = new RedisRequestWaitPackage() { key = key, packageType = RedisPackageType.Get }; var waitData = this.DmtpActor.WaitHandlePool.GetWaitDataAsync(package); try { using (var byteBlock = new ByteBlock((package.value == null ? 0 : package.value.Length) + 1024)) { var block = byteBlock; package.Package(ref block); await this.DmtpActor.SendAsync(this.m_redis_Request, byteBlock.Memory).ConfigureAwait(EasyTask.ContinueOnCapturedContext); } switch (await waitData.WaitAsync(this.Timeout).ConfigureAwait(EasyTask.ContinueOnCapturedContext)) { case WaitDataStatus.SetRunning: { var responsePackage = (RedisResponseWaitPackage)waitData.WaitResult; return responsePackage.Status == 1 ? new CacheEntry(key) { Value = responsePackage.value } : responsePackage.Status == byte.MaxValue ? new CacheEntry(key) : (ICacheEntry)default; } case WaitDataStatus.Overtime: throw new TimeoutException(TouchSocketDmtpStatus.Overtime.GetDescription()); case WaitDataStatus.Canceled: case WaitDataStatus.Default: case WaitDataStatus.Disposed: default: throw new TimeoutException(TouchSocketDmtpStatus.UnknownError.GetDescription()); } } finally { this.DmtpActor.WaitHandlePool.Destroy(waitData); } } /// /// 处理收到的消息 /// /// 接收到的消息对象 /// 返回一个异步任务,指示处理是否成功 public async Task InputReceivedData(DmtpMessage message) { if (message.ProtocolFlags == this.m_redis_Request) { var waitResult = new RedisResponseWaitPackage(); try { var package = new RedisRequestWaitPackage(); var block = message.BodyByteBlock; package.Unpackage(ref block); waitResult.Sign = package.Sign; switch (package.packageType) { case RedisPackageType.Set: { var success = this.ICache.SetCache(new CacheEntry(package.key) { Duration = package.timeSpan.Value, Value = package.value }); waitResult.Status = success ? (byte)1 : byte.MaxValue; break; } case RedisPackageType.Get: { var cache = this.ICache.GetCache(package.key); if (cache != null) { waitResult.Status = 1; waitResult.value = cache.Value; } else { waitResult.Status = byte.MaxValue; } } break; case RedisPackageType.Contains: { waitResult.Status = this.ICache.ContainsCache(package.key) ? (byte)1 : byte.MaxValue; } break; case RedisPackageType.Remove: { waitResult.Status = this.ICache.RemoveCache(package.key) ? (byte)1 : byte.MaxValue; } break; case RedisPackageType.Clear: { this.ICache.ClearCache(); waitResult.Status = 1; } break; default: return true; } } catch (Exception ex) { waitResult.Status = 2; waitResult.Message = ex.Message; } using (var byteBlock = new ByteBlock(1024*64)) { var block = byteBlock; waitResult.Package(ref block); await this.DmtpActor.SendAsync(this.m_redis_Response, byteBlock.Memory).ConfigureAwait(EasyTask.ContinueOnCapturedContext); } return true; } else if (message.ProtocolFlags == this.m_redis_Response) { var waitResult = new RedisResponseWaitPackage(); var block = message.BodyByteBlock; waitResult.Unpackage(ref block); this.DmtpActor.WaitHandlePool.SetRun(waitResult); return true; } return false; } /// public async Task RemoveCacheAsync(string key) { if (string.IsNullOrEmpty(key)) { throw new ArgumentException($"“{nameof(key)}”不能为 null 或空。", nameof(key)); } var package = new RedisRequestWaitPackage { key = key, packageType = RedisPackageType.Remove }; var waitData = this.DmtpActor.WaitHandlePool.GetWaitDataAsync(package); try { using (var byteBlock = new ByteBlock((package.value == null ? 0 : package.value.Length) + 1024)) { var block = byteBlock; package.Package(ref block); await this.DmtpActor.SendAsync(this.m_redis_Request, byteBlock.Memory).ConfigureAwait(EasyTask.ContinueOnCapturedContext); } switch (await waitData.WaitAsync(this.Timeout).ConfigureAwait(EasyTask.ContinueOnCapturedContext)) { case WaitDataStatus.SetRunning: { return waitData.WaitResult.Status == 1 ? true : waitData.WaitResult.Status == byte.MaxValue ? false : throw new Exception(waitData.WaitResult.Message); } case WaitDataStatus.Overtime: throw new TimeoutException(Resources.TouchSocketDmtpStatus.Overtime.GetDescription()); case WaitDataStatus.Canceled: return false; case WaitDataStatus.Default: case WaitDataStatus.Disposed: default: throw new TimeoutException(Resources.TouchSocketDmtpStatus.UnknownError.GetDescription()); } } finally { this.DmtpActor.WaitHandlePool.Destroy(waitData); } } /// public async Task SetAsync(string key, TValue value, int duration = 60000) { var cache = new CacheEntry(key) { Duration = TimeSpan.FromSeconds(duration), Value = value is byte[] bytes ? bytes : this.Converter.Serialize(null, value) }; return await this.SetCacheAsync(cache).ConfigureAwait(EasyTask.ContinueOnCapturedContext); } /// public async Task SetCacheAsync(ICacheEntry cache) { if (string.IsNullOrEmpty(cache.Key)) { throw new ArgumentException($"“{nameof(cache.Key)}”不能为 null 或空。", nameof(cache.Key)); } if (cache is null) { throw new ArgumentNullException(nameof(cache)); } var package = new RedisRequestWaitPackage { key = cache.Key, timeSpan = cache.Duration, value = cache.Value, packageType = RedisPackageType.Set }; var waitData = this.DmtpActor.WaitHandlePool.GetWaitDataAsync(package); try { using (var byteBlock = new ByteBlock((package.value == null ? 0 : package.value.Length) + 1024)) { var block = byteBlock; package.Package(ref block); await this.DmtpActor.SendAsync(this.m_redis_Request, byteBlock.Memory).ConfigureAwait(EasyTask.ContinueOnCapturedContext); } switch (await waitData.WaitAsync(this.Timeout).ConfigureAwait(EasyTask.ContinueOnCapturedContext)) { case WaitDataStatus.SetRunning: { return waitData.WaitResult.Status == 1 || (waitData.WaitResult.Status == byte.MaxValue ? false : throw new Exception(waitData.WaitResult.Message)); } case WaitDataStatus.Overtime: throw new TimeoutException(Resources.TouchSocketDmtpStatus.Overtime.GetDescription()); case WaitDataStatus.Canceled: return false; case WaitDataStatus.Default: case WaitDataStatus.Disposed: default: throw new TimeoutException(TouchSocketDmtpStatus.UnknownError.GetDescription()); } } finally { this.DmtpActor.WaitHandlePool.Destroy(waitData); } } /// /// 设置处理协议标识的起始标识。 /// /// public void SetProtocolFlags(ushort start) { this.m_redis_Request = start++; this.m_redis_Response = start++; } #region 字段 private ushort m_redis_Request; private ushort m_redis_Response; #endregion 字段 }