解决 .NET Core 中 GetHostAddressesAsync 引起的 EnyimMemcached 死锁问题
在我们将站点从ASP.NET+Windows迁移至ASP.NETCore+Linux的过程中,目前遇到的最大障碍就是——没有可用的支持.NETCore的memcached客户端。
我们一直用的是EnyimMemcached,在没有其它选择的情况下,我们自己尝试着将EnyimMemcached迁移至.NETCore。。。基于.NETCore修改好了代码,在开发环境下测试通过,在Linux服务器上自己访问很正常(没有并发访问量),但是只要接入一定的访问量就会发生死锁(deadlock),浏览器请求卡死。
这个问题困扰了我们很长时间,昨天才定位到是发生在将memcached服务器名称解析为IP地址的时候。
varaddresses=System.Net.Dns.GetHostAddressesAsync(host).Result;
这是我们在将EnyimMemcached迁移至.NETCore时修改过的代码,之前调用的是同步方法:
varaddresses=System.Net.Dns.GetHostEntry(host);
由于在.NETCoreFramework的System.Net.Dns中没有同步方法,只有异步方法,所以我们只能这样调用异步方法。
看到上面的代码,你也许会诧异:怎么用.Result,为什么不用await?不死锁才怪呢。。。
你的诧异非常正确。我们也深知.Result的危害,在平时的代码中坚决不用。但当时在修改EnyimMemcached的代码时,由于这个方法是在MemcachedClient的构造函数中调用的,没法改为await调用,被迫用了.Result,然后又把这个地方的修改给忘了。。。昨天才刚刚发现,立马意识到罪魁祸首非常有可能就是这里的.Result,于是以此为突破口,想尽一切办法实现在同步方法中调用异步办法,并且在博问中寻求支援——在同步方法中调用异步方法时如何避免死锁问题。
结果,用尽一切能想到与能找到的同步方法调用异步方法的方法,都没能解决死锁问题。如果实在找不到解决方法,我们准备采用最后一招也是最丑陋的一招——不用Dns.GetHostAddressesAsync(),用ProcessStartInfo调用命令行命令解析IP,比如在Linux上用getenthosts主机名。
在准备放弃之前,今天又想了想还有哪些可能带来线索的地方漏掉了呢?突然想到有个重要地方竟然忘了,还没看Dns.GetHostAddressesAsync()的源代码实现。虽然不报太大希望,不就是个异步方法吗,但还是要看一下。
于是从github上签出corefx的源代码,打开Dns.GetHostAddressesAsync()源代码一看,感觉有点怪怪的,怎么用了Task.Factory.FromAsync()?
publicstaticTask<IPAddress[]>GetHostAddressesAsync(stringhostNameOrAddress) { NameResolutionPal.EnsureSocketsAreInitialized(); returnTask<IPAddress[]>.Factory.FromAsync( (arg,requestCallback,stateObject)=>BeginGetHostAddresses(arg,requestCallback,stateObject), asyncResult=>EndGetHostAddresses(asyncResult), hostNameOrAddress, null); }
开始没反应过来,只是把这段代码贴到博问的补充问题中,在贴完后突然反应过来了,咦,怎么没有async关键字?方法名最后是Async,我们一直以为是async方法,而且丝毫没有怀疑过。。。
没有async,只是返回参数是Task类型,那在同步方法中调用完全没问题,只要在访问.Result之前调用一下.Wait()方法就行了,于是改为下面的代码:
Task<IPAddress[]>task=System.Net.Dns.GetHostAddressesAsync(host); task.Wait(); varaddresses=task.Result;
死锁问题立马解决!
方法名以Async结尾,却不是async方法,当时的感想就是——你的眼睛背叛你的心。如果不是我自己的误解(只要以Async结尾,就应该是async方法),那就是一种流氓行为,就如HttpClient的流氓——实现了IDispose接口,却没真正Dispose。
不管怎么样,这个影响我们迁移至.NETCore的最大障碍终于消除了,值得庆祝!
支持.NETCore的EnyimMemcached的代码还需要一些修改与完善,等修改好了,我们会把源代码与NuGet包都发布出来。
通过此文希望能帮助到你解决这种问题,谢谢大家对本站的支持!