今天上午,一個客戶反饋XX消息沒有推送到第三方連接。因而我查看了推送日誌列表,並無今天的。接着登陸服務器查詢文件日誌,看到了記錄。咱們的代碼步驟是消息先推送到消息隊列,消費消息隊列時,記錄文件日誌,而後異步推送到第三方。javascript
通過一番寒徹骨的查詢幾個關鍵表,構造數據,並調試推送後,發現了問題源頭,是Json版本依賴問題引起的坑,而後修改版本號發佈解決了。下面讓咱們用一個簡易的Demo重現下問題所在。php
首先新建基於.NetFrameWork 4.5.1版本的類庫ErrorSets.CommonE和控制檯程序ConsoleApp1html
而後ErrorSets.CommonE引入Newtonsoft.Json v11.0.2java
而後模擬兩個推送接口,一個是用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
引入調用代碼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)時,結果令咱們失望。既沒有報錯,也沒有執行方法體內輸出。
服務器
改動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>
再運行程序,結果以下。達到咱們預期
爲了更直觀的顯示Task內部異常問題,咱們用下面代碼:
Task.Factory.StartNew(() =>
{
throw new Exception("Exxx");
});
運行結果毫無反應,能夠猜想Task.Factory內部屏蔽了異常?
按照相同的套路,建一個基於.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,國際版搜索「task.factory.startnew sourcecode」,第一條是https://referencesource.microsoft.com/#mscorlib/system/threading/Tasks/Task.cs,結果讓我驚喜!
在微軟的搜索框內,我輸入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
今日發現了三個問題以下:
謝謝觀看,此篇完畢。