在我们将站点从 ASP.NET + Windows 迁移至 ASP.NET Core + Linux 的过程中,目前遇到的最大障碍就是 —— 没有可用的支持 .NET Core 的 memcached 客户端。

我们一直用的是 EnyimMemcached ,在没有其它选择的情况下,我们自己尝试着将 EnyimMemcached 迁移至 .NET Core。。。基于 .NET Core 修改好了代码,在开发环境下测试通过,在 Linux 服务器上自己访问很正常(没有并发访问量),但是只要接入一定的访问量就会发生死锁(deadlock),浏览器请求卡死。

这个问题困扰了我们很长时间,昨天才定位到是发生在将 memcached 服务器名称解析为 IP 地址的时候。

var addresses = System.Net.Dns.GetHostAddressesAsync(host).Result;

这是我们在将 EnyimMemcached 迁移至 .NET Core 时修改过的代码,之前调用的是同步方法:

var addresses = System.Net.Dns.GetHostEntry(host);

由于在 .NET Core Framework 的 System.Net.Dns 中没有同步方法,只有异步方法,所以我们只能这样调用异步方法。

看到上面的代码,你也许会诧异:怎么用 .Result ,为什么不用 await ?不死锁才怪呢。。。

你的诧异非常正确。我们也深知 .Result 的危害,在平时的代码中坚决不用。但当时在修改 EnyimMemcached 的代码时,由于这个方法是在 MemcachedClient 的构造函数中调用的,没法改为 await 调用,被迫用了 .Result ,然后又把这个地方的修改给忘了。。。昨天才刚刚发现,立马意识到罪魁祸首非常有可能就是这里的 .Result ,于是以此为突破口,想尽一切办法实现在同步方法中调用异步办法,并且在博问中寻求支援 —— 在同步方法中调用异步方法时如何避免死锁问题 。

结果,用尽一切能想到与能找到的同步方法调用异步方法的方法,都没能解决死锁问题。如果实在找不到解决方法,我们准备采用最后一招也是最丑陋的一招 —— 不用 Dns.GetHostAddressesAsync() ,用 ProcessStartInfo 调用命令行命令解析 IP ,比如在 Linux 上用 getent hosts 主机名 。

在准备放弃之前,今天又想了想还有哪些可能带来线索的地方漏掉了呢?突然想到有个重要地方竟然忘了,还没看 Dns.GetHostAddressesAsync() 的源代码实现。虽然不报太大希望,不就是个异步方法吗,但还是要看一下。

于是从 github 上签出 corefx 的源代码,打开 Dns.GetHostAddressesAsync() 源代码一看,感觉有点怪怪的,怎么用了 Task.Factory.FromAsync() ?

public static Task<IPAddress[]> GetHostAddressesAsync(string hostNameOrAddress)
{
NameResolutionPal.EnsureSocketsAreInitialized();
return Task<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();
var addresses = task.Result;

死锁问题立马解决!

方法名以 Async 结尾,却不是 async 方法,当时的感想就是 —— 你的眼睛背叛你的心。如果不是我自己的误解(只要以 Async 结尾,就应该是 async 方法),那就是一种流氓行为,就如 HttpClient 的流氓 —— 实现了 IDispose 接口,却没真正 Dispose 。

不管怎么样,这个影响我们迁移至 .NET Core 的最大障碍终于消除了,值得庆祝!

支持 .NET Core 的 EnyimMemcached 的代码还需要一些修改与完善,等修改好了,我们会把源代码与 NuGet 包都发布出来。

解决 .NET Core 中 GetHostAddressesAsync 引起的 EnyimMemcached 死锁问题的更多相关文章

  1. 你的眼睛背叛你的心:解决 .NET Core 中 GetHostAddressesAsync 引起的 EnyimMemcached 死锁问题

    在我们将站点从 ASP.NET + Windows 迁移至 ASP.NET Core + Linux 的过程中,目前遇到的最大障碍就是 —— 没有可用的支持 .NET Core 的 memcached ...

  2. 解决 .net core 中 nuget 包版本冲突问题

    今天在一个 asp.net core 项目中遇到了 nuget 包版本冲突的问题,错误信息如下: Version conflict detected for Microsoft.AspNet.WebA ...

  3. 解决 .net core 中 nuget 包版本冲突问题[转载]

    今天在一个 asp.net core 项目中遇到了 nuget 包版本冲突的问题,错误信息如下: Version conflict detected for Microsoft.AspNet.WebA ...

  4. .NET Core中遇到奇怪的线程死锁问题:内存与线程数不停地增长

    一个 asp.net core 站点,之前运行在Linux 服务器上,运行一段时间后有时站点会挂掉,在日志中记录很多“EMFILE too many open files”的错误: Microsoft ...

  5. 解决.NET Core中MailKit无法使用阿里云邮件推送服务的问题

    在博问中(.net core怎么实现邮件发送)知道了MailKit无法使用阿里云邮件推送服务发送邮件的问题,自已实测也遇到同样的问题,而用自己搭建的邮件服务器没这个问题. 于是,向阿里云提交了工单.. ...

  6. 巧用 Lazy 解决.NET Core中的循环依赖关系

    原文作者: Thomas Levesque 原文链接:https://thomaslevesque.com/2020/03/18/lazily-resolving-services-to-fix-ci ...

  7. 尝试解决在构造函数中同步调用Dns.GetHostAddressesAsync()引起的线程死锁

    (最终采用的是方法4) 问题详情见:.NET Core中遇到奇怪的线程死锁问题:内存与线程数不停地增长 看看在 Linux 与 Windows 上发生线程死锁的后果. Linux: Microsoft ...

  8. 工厂参观记:.NET Core 中 HttpClientFactory 如何解决 HttpClient 臭名昭著的问题

    在 .NET Framework 与 .NET Core 中 HttpClient 有个臭名昭著的问题,HttpClient 实现了 IDispose 接口,但当你 Dispose 它时,它不会立即关 ...

  9. 如何解决 ASP.NET Core 中的依赖问题

    依赖性注入是一种技术,它允许我们注入一个特定类的依赖对象,而不是直接创建这些实例. 使用依赖注入的好处显而易见,它通过放松模块间的耦合,来增强系统的可维护性和可测试性. 依赖注入允许我们修改具体实现, ...

随机推荐

  1. KMP算法 学习例题 POJ 3461Oulipo

    Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 37971   Accepted: 15286 Description The ...

  2. 简单的jquery实现tab切换

    $(document).ready(function(){ $(".nav-menu-ctn").find("a").click(function(){ $(t ...

  3. mac生成ssh keys

    打开终端 输入ssh-keygen,然后系统提示输入文件保存位置等信息,连续敲三次回车即可,生成的SSH key文件保存在中-/.ssh/id_rsa.pub

  4. mysql批量插入之提高插入效率

    INSERT INTO insert_table (datetime, uid, content, type) VALUES ('0', 'userid_0', 'content_0', 0); IN ...

  5. LeetCode 453. Minimum Moves to Equal Array Elements C#

    Given a non-empty integer array of size n, find the minimum number of moves required to make all arr ...

  6. 30分钟掌握ES6/ES2015核心内容

    30分钟掌握ES6/ES2015核心内容   ECMAScript 6(以下简称ES6)是JavaScript语言的下一代标准.因为当前版本的ES6是在2015年发布的,所以又称ECMAScript ...

  7. libev中timer时间事件监控器

    1.数据结构 #define ev_at(w) ((WT)(w))->at#define ev_active(w) ((W)(w))->active typedef ev_watcher_ ...

  8. Java 笔试面试 算法编程篇 一

    方法 1 /* ********************************************************************************** 1.编写一个程序, ...

  9. C#使用LitJson解析JSON(转)

    JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.它基于JavaScript(Standard ECMA-262 3rd Edition - Decembe ...

  10. Js的两种post方式

    第一种提交post的方式是传统方式,判断浏览器进行post请求. var xmlobj; //定义XMLHttpRequest对象 function CreateXMLHttpRequest() { ...