在我们将站点从 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;

上面的代码在并发请求下会造成死锁,改为下面的代码可解决死锁问题,但主机名解析会失败,详见 .NET Core中遇到奇怪的线程死锁问题:内存与线程数不停地增长

Task<IPAddress[]> task = System.Net.Dns.GetHostAddressesAsync(host);
if (task.Wait())
{
var addresses = task.Result;
}

死锁问题立马解决!

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

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

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

[9月26日更新]:支持 .NET Core 的memcached 客户端 EnyimMemcachedCore 的 NuGet 包下载地址: https://www.nuget.org/packages/EnyimMemcachedCore

[12月5日更新]:最终解决方法见:尝试解决.NET Core Framework中Dns.GetHostAddressesAsync()引起的线程死锁

你的眼睛背叛你的心:解决 .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中MailKit无法使用阿里云邮件推送服务的问题

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

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

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

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

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

  7. 解决vi/vim中粘贴会在行首多很多缩进和空格的问题

    解决vi/vim中粘贴会在行首多很多缩进和空格的问题 secureCRT会将你原来的文本原封不动的按照字符串的样式发送给服务器.所以当你的服务器上的vim设置为autoindent的话,在i模式下,那 ...

  8. 解决spring配置中的bean类型的问题:BeanNotOfRequiredTypeException

    解决spring配置中的bean类型的问题:BeanNotOfRequiredTypeException这个问题出现的原因:一般在使用annotation的方式注入spring的bean 出现的,具体 ...

  9. 解决关于jquery中$.get()方法总是报“HierarchyRequestError: Node cannot be inserted at the specified point in the hierarchy”错的方法

    解决关于jquery中$.get()方法总是报“HierarchyRequestError: Node cannot be inserted at the specified point in the ...

随机推荐

  1. JS--该死的&&和||

    近段时间搞前端js,零零星星的看了一些框架源码,发现大量存在&&和||,其语法看的我是头冒青烟,也不知道怎么回事,度娘搜吧,现在写下来,以防忘掉. 先来约束一下用词:有一表达式A : ...

  2. DDD建模案例----“视频课程”场景

    接触领域驱动设计DDD有一年多的时间了,中间看过不少书,参与过一些讨论(ENode QQ群).目前对DDD的认知还停留在理论阶段,所以对领域建模非常感兴趣,这里说的建模是指以DDD的思想为指导再加上D ...

  3. 2 Unique Binary Search Trees II_Leetcode

    Given n, generate all structurally unique BST's (binary search trees) that store values 1...n. For e ...

  4. 忘记XP密码的解决方案

    仅供教学与研究用,后果自负! !! USE AT YOUR OWN RISK !! !! ONLY FOR EDUCATIONAL PURPOSE !! 介绍 获取SYSTEM权限.测试通过. 进入G ...

  5. maven权威指南学习笔记(一)——简介

    maven是什么?有什么用? Maven是一个项目管理工具,它包含了     一个项目对象模型 (Project Object Model),     一组标准集合,     一个项目生命周期(Pro ...

  6. Puppet自动化部署-前期环境准备(2)

    在安装Puppet环境之前需要配置好机器的基本配置,如规范网络地址IP.hostname,certname认证名称,ntp时间同步等配置完毕,完善的搭建自动化环境. 1.环境介绍 此处实现部署的环境是 ...

  7. unity 利用ugui 制作技能冷却效果

    using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; ...

  8. Visual Studio 2013执行项目报错:HTTP 错误 500.22

    转至:http://www.codingwhy.com/410.html 具体报错 HTTP 错误 500.22 - Internal Server Error 检测到在集成的托管管道模式下不适用的 ...

  9. JavaScript对象状态

    有限状态机(Finite-state machine)是一个非常有用的模型,可以模拟世界上大部分事物. 简单说,它有三个特征: * 状态总数(state)是有限的. * 任一时刻,只处在一种状态之中. ...

  10. Unity内存优化(贴图层面)

    聊聊近况: 距离上一篇文章已经过了好久,主要原因是我懒了.公司项目也到了开始优化的阶段,上网找的资料,看过了就忘.还是想把它整理一下,写出来.其实我说的东西,网上都有,我只是搬运工而已. 贴图压缩: ...