We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cache.BeginCacheLockAsync 没有工作
为了将accesstoken的当前所剩过期时间能返回给调用方,重新封装了下AccessTokenContainer如下: `public class AccessTokenContainerExt: AccessTokenContainer{
public static async Task GetAccessTokenInfo(string appId, bool getNewToken = false) { await ValidateAppRegistered(appId).ConfigureAwait(false);
var accessTokenBag = await TryGetItemAsync(appId).ConfigureAwait(false); using (await Cache.BeginCacheLockAsync(LockResourceName, appId).ConfigureAwait(false))//同步锁 { if (getNewToken || accessTokenBag.AccessTokenExpireTime <= SystemTime.Now) { //已过期,重新获取 var accessTokenResult = await CommonApi.GetTokenAsync(accessTokenBag.AppId, accessTokenBag.AppSecret).ConfigureAwait(false); accessTokenBag.AccessTokenResult = accessTokenResult; accessTokenBag.AccessTokenExpireTime = ApiUtility.GetExpireTime(accessTokenBag.AccessTokenResult.expires_in); await UpdateAsync(accessTokenBag, null).ConfigureAwait(false);//更新到缓存 } } return new AccessTokenInfo { AppId = accessTokenBag.AppId, AccessToken = accessTokenBag.AccessTokenResult.access_token, ExpireTime = accessTokenBag.AccessTokenExpireTime, ExpireIn = (int)(accessTokenBag.AccessTokenExpireTime - DateTimeOffset.Now).TotalSeconds };
} }`
验证分布式锁时, 发现多个实例都进入了竞争代码Cache.BeginCacheLockAsync 没有起作用,并查看了Redis库,也确实没有锁的数据写入, 并且验证过其他Redis功能都是正常的, 生成的accesstoken也都能正常写入Redis,目前就分布式锁有问题。
加了断点调试发现lockSuccessful 为false的时候也进到竞争逻辑块里了,等同于没有起到锁的作用, 本来以为只有lockSuccessful是true才会进来,是不是版本不对造成的?
初始代码如下: ` private void UseSenparcWexin(IApplicationBuilder app) { var senparcSetting = app.ApplicationServices.GetRequiredService<IOptions>().Value; var senparcWeixinSetting = app.ApplicationServices.GetRequiredService<IOptions>().Value;
// 启动 CO2NET 全局注册,必须! // 关于 UseSenparcGlobal() 的更多用法见 CO2NET Demo:https://github.com/Senparc/Senparc.CO2NET/blob/master/Sample/Senparc.CO2NET.Sample.netcore3/Startup.cs var registerService = app.UseSenparcGlobal(_environment, senparcSetting, globalRegister => { //当同一个分布式缓存同时服务于多个网站(应用程序池)时,可以使用命名空间将其隔离(非必须) globalRegister.ChangeDefaultCacheNamespace("DefaultCO2NETCache"); #region 配置和使用 Redis -- DPBMARK Redis //Register.SetConfigurationOption(senparcSetting.Cache_Redis_Configuration); ////以下会立即将全局缓存设置为 Redis //Register.UseKeyValueRedisNow(); //键值对缓存策略(推荐) #endregion #region 注册 StackExchange.Redis /* 如果需要使用 StackExchange.Redis,则可以使用 Senparc.CO2NET.Cache.Redis 库 * 注意:这一步注册和上述 CsRedis 库两选一即可,本 Sample 需要同时演示两个库,因此才都进行注册 */ Senparc.CO2NET.Cache.Redis.Register.SetConfigurationOption(senparcSetting.Cache_Redis_Configuration); Senparc.CO2NET.Cache.Redis.Register.UseKeyValueRedisNow();//键值对缓存策略(推荐) #endregion }, true); //使用 Senparc.Weixin SDK registerService.UseSenparcWeixin(senparcWeixinSetting, (weixinRegister, weixinSetting) => { //weixinRegister.UseSenparcWeixinCacheCsRedis(); weixinRegister.UseSenparcWeixinCacheRedis(); weixinSetting.Items .Where(i => i.Key != "Default") .ToArray() .ForEach(item => { if (!string.IsNullOrWhiteSpace(item.Value.WeixinAppId)) { weixinRegister.RegisterMpAccount(item.Value, item.Key); } else if (!string.IsNullOrWhiteSpace(item.Value.WeixinCorpId)) { weixinRegister.RegisterWorkAccount(item.Value, item.Key); } }); });
}`
The text was updated successfully, but these errors were encountered:
我尝试用了个老的版本 2024.10.8(这个版本使用CSRedis使,不需要依赖其他库), 这个时候lockSuccessful是true的时候才会进入了,但是问题是,一旦进入后,redis中的锁就被释放了, 因为默认的ttl我调试看了下是2ms, 所以就导致一开始发现的问题,两个实例同时运行时,会同时获得同步锁,造成并发冲突了。
Sorry, something went wrong.
切换回Senparc.Weixin.All 2024.8.11,并使用CsRedis时: 通过调试看到默认的调用ttl最小是0.2秒, 那么如果在0.2秒外有并发操作,那么就锁不住了。如果调的地方设置retrydelay 为1秒时,retryTime默认是20,则redis 的stringSet ttl是20秒,则可以按预期的工作。也意味着锁最多锁20秒。所以Cache.BeginCacheLockAsync的默认retryTime是不是应该设大点? 另外TryGetItemAsync为什么在锁之外调用,而不是在锁之内调用?这样是不是多个并发如果获取到的AccessTokenExpireTime都是过期的,就都会去刷新token了?然后如果由于分布式io,cpu运行时的时序不确定性导致先刷新的token后更新到redis,则会使得在后续的2小时内的token都无效了。
@IvanZheng 这个问题需要从从同步锁的原理上面来理解,同步锁(尤其支持分布式),通常来讲一定要考虑超时情况,因为只有占有锁的进程才能解锁,所以如果这个进程一直不结束或者因为某种原因闪退了,这个锁可能永远无法解开,导致最后系统崩溃,所以这里的锁会有一个过期时间的设置,你如果是做测试的话,可以把锁的时间设置长一点(包含次数和每次重试间隔时间,可以近似理解为两个数的乘积是最大允许的等待时间),一旦超过等待时间,锁就会自动释放,其他请求就可以进来(当然也可以消灭这些请求,一般不会这么做)。
No branches or pull requests
问题描述
Cache.BeginCacheLockAsync 没有工作
重现问题步骤(如果可以)
为了将accesstoken的当前所剩过期时间能返回给调用方,重新封装了下AccessTokenContainer如下:
`public class AccessTokenContainerExt: AccessTokenContainer{
public static async Task GetAccessTokenInfo(string appId, bool getNewToken = false)
{
await ValidateAppRegistered(appId).ConfigureAwait(false);
}
}`
验证分布式锁时, 发现多个实例都进入了竞争代码Cache.BeginCacheLockAsync 没有起作用,并查看了Redis库,也确实没有锁的数据写入, 并且验证过其他Redis功能都是正常的, 生成的accesstoken也都能正常写入Redis,目前就分布式锁有问题。
加了断点调试发现lockSuccessful 为false的时候也进到竞争逻辑块里了,等同于没有起到锁的作用, 本来以为只有lockSuccessful是true才会进来,是不是版本不对造成的?
初始代码如下:
` private void UseSenparcWexin(IApplicationBuilder app)
{
var senparcSetting = app.ApplicationServices.GetRequiredService<IOptions>().Value;
var senparcWeixinSetting = app.ApplicationServices.GetRequiredService<IOptions>().Value;
}`
发现问题的模块
模块对应的 .net 版本
开发环境
缓存环境
系统环境
The text was updated successfully, but these errors were encountered: