如何高效存储WTelegramClient的Saved Session以优化多用户API应用?
基于WTelegramClient的高效会话存储方案(适配Azure Redis Cache)
首先明确:不要直接在Redis中存储WTelegram.Client实例——这类带网络连接和运行状态的.NET对象无法可靠完成序列化/反序列化,Redis适合存储轻量的会话数据而非实例本身。我们可以利用WTelegramClient自带的会话持久化能力,将会话数据存储到Redis,从而复用会话、大幅降低Client初始化的资源消耗。
核心思路
- 以用户手机号为唯一标识,在Redis中存储对应Telegram会话的序列化数据
- 创建Client时优先从Redis加载已有会话,跳过重复登录流程
- 会话更新后(如登录成功、token自动刷新),自动同步最新会话数据到Redis
- 本地保留短期Client实例缓存,平衡Redis访问开销与内存占用
修改后的实现代码
假设使用StackExchange.Redis操作Azure Redis Cache,先安装对应NuGet包:StackExchange.Redis
using StackExchange.Redis; using WTelegram; using System.Text.Json; using System.Collections.Concurrent; namespace TelegramMicroservice.Services.Telegram { public class WTelegramClientFactory : IWTelegramClientFactory { private const string ApiId = "你的API_ID"; private const string ApiHash = "你的API_HASH"; private readonly IDatabase _redisDb; private readonly ConcurrentDictionary<string, Client> _localClientCache = new(); // 本地Client实例缓存过期时间(可根据业务调整) private readonly TimeSpan _localCacheExpiry = TimeSpan.FromMinutes(30); public WTelegramClientFactory(IConnectionMultiplexer redisMultiplexer) { _redisDb = redisMultiplexer.GetDatabase(); // 后台定期清理本地过期的Client实例 _ = Task.Run(CleanupLocalCacheAsync); } public Client GetOrCreateClient(string phoneNumber) { // 先查本地缓存 if (_localClientCache.TryGetValue(phoneNumber, out var client)) return client; // 本地无缓存则从Redis加载会话 var sessionJson = _redisDb.StringGet(GetRedisSessionKey(phoneNumber)); Client newClient; if (!sessionJson.IsNullOrEmpty) { // 反序列化Redis中的会话数据 var savedSession = JsonSerializer.Deserialize<Client.Session>(sessionJson); newClient = new Client(ConfigCallback, savedSession); } else { // 无历史会话,创建新Client newClient = new Client(ConfigCallback); } // 监听会话变更事件,自动同步到Redis newClient.SessionChanged += async (_, args) => { var serializedSession = JsonSerializer.Serialize(args.Session); await _redisDb.StringSetAsync(GetRedisSessionKey(phoneNumber), serializedSession, TimeSpan.FromDays(7)); }; // 将新Client加入本地缓存 _localClientCache.TryAdd(phoneNumber, newClient); return newClient; } private string ConfigCallback(string configKey) { return configKey switch { "api_id" => ApiId, "api_hash" => ApiHash, "phone_number" => null, // 会话已包含登录信息,无需重复提供 _ => null, }; } private string GetRedisSessionKey(string phoneNumber) => $"telegram:session:{phoneNumber}"; private async Task CleanupLocalCacheAsync() { while (true) { await Task.Delay(TimeSpan.FromMinutes(10)); // 可扩展Client类添加LastUsedTime属性,判断实例是否闲置 // 示例逻辑:移除超过缓存过期时间的闲置实例 foreach (var phone in _localClientCache.Keys.ToList()) { // if (_localClientCache[phone].LastUsedTime < DateTime.UtcNow - _localCacheExpiry) // _localClientCache.TryRemove(phone, out _); } } } } }
关键优化点
- 自动会话同步:利用WTelegramClient的
SessionChanged事件,无需手动维护会话状态,更新自动同步到Redis - 分层缓存架构:本地短期缓存减少Redis访问频次,Redis存储长期会话数据,兼顾性能与分布式场景一致性
- 安全防护:建议对Redis中的会话数据进行加密(如AES),避免敏感登录信息泄露
- 资源回收机制:后台定期清理本地闲置Client实例,防止内存占用过高
内容的提问来源于stack exchange,提问作者Krystian
相关产品推荐
相关产品推荐

