.Net版本依赖之坑引起的搜查

前言

今天上午,一个客户反馈XX消息没有推送到第三方连接。因而我查看了推送日志列表,并无今天的。接着登陆服务器查询文件日志,看到了记录。咱们的代码步骤是消息先推送到消息队列,消费消息队列时,记录文件日志,而后异步推送到第三方。javascript

调试排坑

通过一番寒彻骨的查询几个关键表,构造数据,并调试推送后,发现了问题源头,是Json版本依赖问题引起的坑,而后修改版本号发布解决了。下面让咱们用一个简易的Demo重现下问题所在。php

问题重现

构建环境

首先新建基于.NetFrameWork 4.5.1版本的类库ErrorSets.CommonE和控制台程序ConsoleApp1html

 
.Net FrameWork


而后ErrorSets.CommonE引入Newtonsoft.Json v11.0.2java

 
Newtonsoft.Json


而后模拟两个推送接口,一个是用Task包装的,一个是正常方法。git

 

public class SeriEx {
 public static void TaskPostThird(object obj)    {
      Task.Factory.StartNew(() =>
      {
       var data = JsonConvert.SerializeObject(obj);
       Console.WriteLine($"TaskPostThird:{data}");
      });
   }
 public static void PostThird(object obj)    {
     var data = JsonConvert.SerializeObject(obj);
      Console.WriteLine($"PostThird:{data}");
   }
}

ConsoleApp1引入Newtonsoft.Json v9.0.1github

 
image.png


引入调用代码json

 

namespace ConsoleApp1
{
 class Program {
 static void Main(string[] args)     {
      var user = new User() { Mobile = "12546423" };
 // SeriEx.PostThird(user);
      SeriEx.TaskPostThird(user);
      Console.WriteLine("End");
      Console.ReadKey();
    }
  }
}

查看运行结果

当Main调用SeriEx.TaskPostThird(user)时,结果令咱们失望。既没有报错,也没有执行方法体内输出。
服务器

 
image.png


改动TaskPostThird,加上异常捕获app

 

public static void TaskPostThird(object obj)
{
    Task.Factory.StartNew(() =>
    {
 try
      {
 var data = JsonConvert.SerializeObject(obj);
        Console.WriteLine($"TaskPostThird:{data}");
      }
 catch (Exception ex) {
        Console.WriteLine($"TaskPostThirdEx:{ex.Message}");
      }
   });
}

运行程序后,依然没有异常抛出,也没有任何结果输出。让咱们将Main调用改为不带Task的SeriEx.PostThird(user)异步

class Program {
 static void Main(string[] args)    {
    var user = new User() { Mobile = "12546423" };
    SeriEx.PostThird(user);
    Console.WriteLine("End");
    Console.ReadKey();
  }
}

执行结果以下,抛出了异常。结果符合预期,只要不是沉默的代码就好!

 

 
异常

 

根据异常提示,咱们在app.config追加以下配置oldVersion改为0.0.0.0-11.0.0.0,支持不一样版本。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-11.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

再运行程序,结果以下。达到咱们预期

 

 
image.png

 

为了更直观的显示Task内部异常问题,咱们用下面代码:

Task.Factory.StartNew(() =>
{
 throw new Exception("Exxx");
});

运行结果毫无反应,能够猜想Task.Factory内部屏蔽了异常?

NetCore下是否一样问题?

按照相同的套路,建一个基于.netCore2.1的类库ErrorSets.CommonCore和控制台程序ConsoleAppCore,并引入不一样版本的Newtonsoft.Json。编译结果以下:

 
编译报错


咱们能够看到.NetCore版本直接提示报错,编译失败。让咱们来继续测试下另一个问题Task.Factory.StartNew下异常问题。为了编译经过,先移除json。

 

static void Main(string[] args) {
   var user = new User() { Mobile = "12546423" };
   TaskThrowExcetption();
   Console.ReadKey();
}
static void TaskThrowExcetption() {
   Task.Factory.StartNew(() =>
   {
 throw new Exception("s");
   });
}

运行结果一片空白,并无跑错。说明.NetCore下也是屏蔽了异常。

中断的源码路

根据F12提示,看到Task引用的是System.Runtime.dll,因此我下载了corefx

#region 程序集 System.Runtime, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
// C:\Users\hp\.nuget\packages\microsoft.netcore.app\2.1.0\ref\netcoreapp2.1\System.Runtime.dll
#endregion

打开src\System.Runtime,解决方案搜索Factory,迎接个人是

public partial class TaskFactory {
 public TaskFactory() { }
 public System.Threading.Tasks.Task<TResult> StartNew<TResult>(System.Func<object, TResult> function, object state) { throw null; }
 public System.Threading.Tasks.Task<TResult> StartNew<TResult>(System.Func<object, TResult> function, object state, System.Threading.CancellationToken cancellationToken) { throw null; }
 public System.Threading.Tasks.Task<TResult> StartNew<TResult>(System.Func<object, TResult> function, object state, System.Threading.CancellationToken cancellationToken, System.Threading.Tasks.TaskCreationOptions creationOptions, System.Threading.Tasks.TaskScheduler scheduler) { throw null; }
 public System.Threading.Tasks.Task<TResult> StartNew<TResult>(System.Func<object, TResult> function, object state, System.Threading.Tasks.TaskCreationOptions creationOptions) { throw null; }
 public System.Threading.Tasks.Task<TResult> StartNew<TResult>(System.Func<TResult> function) { throw null; }
 public System.Threading.Tasks.Task<TResult> StartNew<TResult>(System.Func<TResult> function, System.Threading.CancellationToken cancellationToken) { throw null; }
 public System.Threading.Tasks.Task<TResult> StartNew<TResult>(System.Func<TResult> function, System.Threading.CancellationToken cancellationToken, System.Threading.Tasks.TaskCreationOptions creationOptions, System.Threading.Tasks.TaskScheduler scheduler) { throw null; }
 public System.Threading.Tasks.Task<TResult> StartNew<TResult>(System.Func<TResult> function, System.Threading.Tasks.TaskCreationOptions creationOptions) { throw null; }
}

使人失望的partial,使人失望的throw null;我打开了另一个src\System.Threading.Tasks,看到了项目一片空白!!!

Bing下生物

一片迷茫之下,我使用了Bing,国际版搜索“task.factory.startnew sourcecode”,第一条是https://referencesource.microsoft.com/#mscorlib/system/threading/Tasks/Task.cs,结果让我惊喜!
在微软的搜索框内,我输入TaskFactory,出现以下结果:

 
TaskFactory

 

public Task StartNew(Action action) {
   StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
   Task currTask = Task.InternalCurrent;
 return Task.InternalStartNew(currTask, action, null, m_defaultCancellationToken, GetDefaultScheduler(currTask),
m_defaultCreationOptions, InternalTaskOptions.None, ref stackMark);
}

Task.InternalStartNew以下:

internal static Task InternalStartNew( Task creatingTask, Delegate action, object state, CancellationToken cancellationToken, TaskScheduler scheduler, TaskCreationOptions options, InternalTaskOptions internalOptions, ref StackCrawlMark stackMark) {
// Validate arguments.
 if (scheduler == null)
   {
 throw new ArgumentNullException("scheduler");
  }
  Contract.EndContractBlock();
// Create and schedule the task. This throws an InvalidOperationException if already shut down.
// Here we add the InternalTaskOptions.QueuedByRuntime to the internalOptions, so that TaskConstructorCore can skip the cancellation token registration
   Task t = new Task(action, state, creatingTask, cancellationToken, options, internalOptions | InternalTaskOptions.QueuedByRuntime, scheduler);
   t.PossiblyCaptureContext(ref stackMark);
   t.ScheduleAndStart(false);
 return t;
}

查到这里,我累了。工做到此结束。。。

微软.net源码连接已收藏到.NetCore外国一些高质量博客分享,保持长期更新

源码

源码查看问题记录集,欢迎 Star

总结

今日发现了三个问题以下:

  • Task.Factory.StartNew方法体内不会抛出异常【缘由:主线程默认不捕获异步线程的异常】。
  • 针对json不一样版本,.net framework能够编译经过,.netcore编译失败。
  • .net framework能够经过配置文件解决版本问题,那么.netcore是如何解决的?
    今天最重要的是发现了微软.net源码网址,不在github,在他本身的老家https://referencesource.microsoft.com
  • 2018-10-19更新:referencesource的github地址: https://github.com/Microsoft/referencesource

谢谢观看,此篇完毕。

相关文章
相关标签/搜索