《ASP.NET Core 高效能系列》致敬偉大的.NET鬥士甲骨文!

月明時勤發表於2019-07-14

寫在開始

  三年前,曾寫過一篇文章:從.NET和Java之爭談IT這個行業,當時遭到某些自認為懂得java就了不起的Javaer抨擊,

現在可以致敬偉大的.NET鬥士甲骨文了

  (JDK8以上都需要收費,Android棄用java作為第一語言,別高興:OpenJDK是甲骨文的).

  《ASP.NET Core 高效能系列》是一套如何編寫高效能Web應用技術系列文章,

我們將從.NET 2開始全面升入.其中我們會討論網際網路當今熱門的技術話題:容器、容器編排、服務治理、RPC等

此文是第一篇,用於此係列的開門篇,後面每週會持續釋出.

一、Core 2中需要知道的新鮮東西

Core系列有兩個主要產品:

第一個是.NET Core

  它是一個低階別的提供基本庫的框架。 它可以用來編寫控制檯應用程式,它也是

更高階別的應用程式框架的基礎。 

第二個是ASP.NET Core

  它是一個用於構建Web的跨平臺框架

另外.NET Core中的更改也將適用於ASP.NET Core,因為這是基礎 

二、.NET Core 2的新東西

2.1 API範圍更加廣泛

  .NET Core 2的主要焦點是API範圍的大幅增加,在1.*的基礎上增加了兩倍的API,

而且支援.net standard,您也可以引用.NET Framework程式集而無需重新編譯,

只要程式集中的API已在.NET Core中實現就可以正常工作。這意味著更多的Nuget包可以在.NET Core中工作,

ASP.NET Core Library and Framework 支援情況的一個統計站點:https://ANCLAFS.com(有點跟不上了)

2.2 效能的大幅度提升

.NET Core 2.0中一些更有趣的變化是在原始的.NET FrameworkAPI上效能改進

,已經對許多框架的實現進行了調整了資料結構。 

下面是一些已經看到快速改進的類和方法,已經記憶體開銷的減少包括:

List<T>
Queue<T>
SortedSet<T>
ConcurrentQueue<T>
Lazy<T>
Enumerable.Concat()
Enumerable.OrderBy()
Enumerable.ToList()
Enumerable.ToArray()
DeflateStream
SHA256
BigInteger
BinaryFormatter
Regex
WebUtility.UrlDecode()
Encoding.UTF8.GetBytes()
Enum.Parse()
DateTime.ToString()
String.IndexOf()
String.StartsWith()
FileStream
Socket
NetworkStream
SslStream
ThreadPool
SpinLock 

  另外,對於.NET Core 2RyuJIT Just In Time編譯器進行了改進。

僅作為一個示例就能說明說明其優秀之處,finally塊現在幾乎與不使用異常一樣高效,

這在沒有丟擲異常的正常情況下是有益的。您現在沒有理由不使用tryusing{}塊,以及checked檢查

三、ASP.NET Core 2的新東西 

  ASP.NET Core 2利用了.NET Core 2的所有改進,不僅通過即時編譯處理程式縮短了啟動時間,

涵蓋了增加了輸出快取,本地及分散式快取(SQLSERVER,REDIS).

3.1 metapackage

  .NET Core包含了一個新metapackage,所以你只用引用一個NuGet項就可以得到所有的東西,metapackage仍然由單個獨立的包組成,

一個新的包修剪功能(new package-trimming)確保如果您不使用包,那麼它的二進位制檔案將不會包含在您的部署中,

即使您使用metapackage 來引用它。設定Web主機配置也有合理的預設設定。

您無需單獨新增logging, Kestrel, and IISlogging也變得更簡單,因為它是建立的在,你再也沒有任何藉口不一開始就使用它

3.2 Razor Pages

  無控制器Razor Pages。 這正是它聽起來的樣子,而且它允許您使用Razor模板編寫頁面。

它類似於Web Pages產品,不必和WebForm混淆。 大家其實覺得是WebForm的捲土重來,個人感覺是滿懷希望的捲土重來,

架構做了更多抽象和思考,不會像之前一樣,帶來如此多的狀態與之相伴.

3.3 新的authentication 模型

  新的 authentication 模型能讓更好地使用了依賴注入, ASP.NETCore Identity 允許你使用OpenID OAuth 2

來為你的API獲取訪問tokens .當然你可以研究Identity Server 4 專案,它提供了相同的功能.

3.4 表單請求自動防偽

  你不必再為表單新增防偽token(防止跨越偽造請求) (之前你不得不新增一個attributePost方法中進行驗證),

現在這一切都是自動的.

3.5效能提升

   ASP.NET Core有一些額外的與.NET Core無關的效能提升:

啟動時間通過即時編譯處理明顯減少,雖然這不是ASP.NET Core 2的新功能;

 output caching依然可用,在1.0時,只有response caching*(簡化了如何設定http header),

1.1 時,新增了memory cache,現在你可以還使用分散式快取()SQL Server or Redis) 

.NET Standard 2.0

  .NET Standard 是一套正式的 .NET API 規範,目標是在所有 .NET 實現中推出。 推出 .NET Standard 的背後動機是要提高 .NET 生態系統中的一致性。

ECMA 335 持續為 .NET 實現行為建立統一性,但適用於 .NET 庫實現的 .NET 基類庫 (BCL) 沒有類似的規範。

.NET Framework 4.6.1實現了.NET Standard  2.0.

.NET Standard 可實現以下重要情境:

1.為要實現的所有 .NET 實現定義一組統一的、與工作負荷無關的 BCL API

2.使開發人員能夠通過同一組 API 生成可在各種 .NET 實現中使用的可移植庫。

3.減少甚至消除由於 .NET API 方面的原因而對共享原始碼進行的條件性編譯(僅適用於 OS API)。 

五、C# 6.0語言級別的新東西

5.1 屬性可以連帶賦值

public DateTime BirthDay { get; set; } = DateTime.Now.AddYears(-20);

 

5.2 匯入靜態類

using static System.Math;

Console.WriteLine($"匯入後可直接使用方法: {Pow(4, 2)}");

 

5.3 字串格式化的變化

Console.WriteLine(string.Format("當前時間:{0}",DateTime.Now.ToString()));

 

5.4 空值運算子

Console.WriteLine(name?.ToString()); // 程式不會報錯,也不會輸出任何值

 

5.5 物件初始化器

可以通過索引的方式進行賦值

IDictionary<int, string> dictNew = new Dictionary<int, string>()
{
       [4] = "first",
       [5] = "second"
};

  

5.6 異常過濾器

int exceptionValue = 10;
try
{
       Int32.Parse("s");
}
catch (Exception e) when (exceptionValue > 1)//滿足條件才進入catch
{
       Console.WriteLine("catch");
}

5.7 nameof表示式

Console.WriteLine(nameof(People));

5.8 在屬性/方法裡面使用Lambda表示式

 public void Print() => Console.WriteLine(Name);

 

六、C# 7.0語言級別的新東西

6.1 out變數不需要申明瞭

  var input = ReadLine();
  if (int.TryParse(input, out var result))
  {

      WriteLine("您輸入的數字是:{0}",result);
  }
  else
  {
      WriteLine("無法解析輸入...");
  }

  

6.2元組

 元組(Tuple)在 .Net 4.0 的時候就有了,但元組也有些缺點,如:

   1)Tuple 會影響程式碼的可讀性,因為它的屬性名都是:Item1,Item2.. 。

   2)Tuple 還不夠輕量級,因為它是引用型別(Class)。

   備註:上述所指 Tuple 還不夠輕量級,是從某種意義上來說的或者是一種假設,即假設分配操作非常的多。

 C# 7 中的元組(ValueTuple)解決了上述兩個缺點:

   1)ValueTuple 支援語義上的欄位命名。

   2)ValueTuple 是值型別(Struct)。

 

傳統的建立方式

  var tuple = (1, 2);                           // 使用語法糖建立元組

  var tuple2 = ValueTuple.Create(1, 2);         // 使用靜態方法【Create】建立元組 

  var tuple3 = new ValueTuple<int, int>(1, 2);  // 使用 new 運算子建立元組 WriteLine($"first:{tuple.Item1}, second:{tuple.Item2}, 上面三種方式都是等價的。");

//建立給欄位命名的元組
// 左邊指定欄位名稱
  (int one, int two) tuple = (1, 2);
  WriteLine($"first:{tuple.one}, second:{tuple.two}");
  // 右邊指定欄位名稱
  var tuple2 = (one: 1, two: 2);
  WriteLine($"first:{tuple2.one}, second:{tuple2.two}"); 
  // 左右兩邊同時指定欄位名稱
  (int one, int two) tuple3 = (first: 1, second: 2);   
 /* 此處會有警告:由於目標型別(xx)已指定了其它名稱,因為忽略元組名稱xxx */
  WriteLine($"first:{tuple3.one}, second:{tuple3.two}");

6.3 解構

6.3.1解構元組 

var (one, two) = GetTuple();
WriteLine($"first:{one}, second:{two}");
static (int, int) GetTuple() {
  return (12, 2);
} 

6.3.2解構可以應用於 .Net 的任意型別,

但需要編寫 Deconstruct 方法成員(例項或擴充套件)

public void Deconstruct(out type variable1, out type variable2...)
public static void Deconstruct(this type instance, out type variable1, out type variable2...)

 6.4 模式匹配

6.4.1 is 表示式(is expressions

static int GetSum(IEnumerable<object> values)
  {
      var sum = 0;
      if (values == null) return sum;  
      foreach (var item in values)    
{
if (item is short) // C# 7 之前的 is expressions { sum += (short)item; } else if (item is int val) // C# 7 的 is expressions { sum += val; } else if (item is string str && int.TryParse(str, out var result)) // is expressions 和 out variables 結合使用 { sum += result; } else if (item is IEnumerable<object> subList) { sum += GetSum(subList); } } return sum; }  

6.4.2 switch語句

switch (item)
  {
      case type variable1:
          // processing...
          break;
      case type variable2 when predicate:
          // processing...
          break;
      default:
          // processing...
          break;
  }

6.5 Ref locals and returns

static ref int GetLocalRef(int[,] arr, Func<int, bool> func)
  {
      for (int i = 0; i < arr.GetLength(0); i++)
      {
          for (int j = 0; j < arr.GetLength(1); j++)
          {
              if (func(arr[i, j]))
              {
                  return ref arr[i, j];
              }
          }
      }
      throw new InvalidOperationException("Not found");
  }
  int[,] arr = { { 10, 15 }, { 20, 25 } };
  ref var num = ref GetLocalRef(arr, c => c == 20);
  num = 600; 
  Console.WriteLine(arr[1, 0]);  

 總結:雖然 C# 7 中提供了區域性引用和引用返回,但為了防止濫用所以也有諸多約束,如:

 1. 你不能將一個值分配給 ref 變數,如:

1   ref int num = 10;   // error:無法使用值初始化按引用變數

 2. 你不能返回一個生存期不超過方法作用域的變數引用,如:

1 public ref int GetLocalRef(int num) => ref num;   // error: 無法按引用返回引數,因為它不是 ref out 引數

 3. ref 不能修飾 “屬性” 和 “索引器”。 

1   var list = new List<int>();2   ref var n = ref list.Count;  // error: 屬性或索引器不能作為 out ref 引數傳遞

 原理解析:非常簡單就是指標傳遞,並且個人覺得此語法的使用場景非常有限,都是用來處理大物件的,目的是減少GC提高效能。

6.5 區域性函式

static IEnumerable<char> GetCharList(string str)
   {
       if (IsNullOrWhiteSpace(str))

           throw new ArgumentNullException(nameof(str));

       return GetList();   

       IEnumerable<char> GetList()
       {
           for (int i = 0; i < str.Length; i++)
           {
               yield return str[i];
           }
       }
   } 

6.6. 擴充套件非同步返回型別(Generalized async return types 

 以前非同步的返回型別必須是:Task、Task<T>、void,現在 C# 7 中新增了一種型別:ValueTask<T>,如下所示:

  public async ValueTask<int> Func()
  {
     await Task.Delay(3000);
     return 100;
  }

  總結:ValueTask<T> 與 ValueTuple 非常相似,所以就不列舉: ValueTask<T> 與 Task 之間的異同了,\但它們都是為了優化特定場景效能而

 新增的型別。  使用 ValueTask<T> 則需要匯入:

Install - Package System.Threading.Tasks.Extensions

6.7. 數字文字語法的改進(Numeric literal syntax improvements 

 C# 7 還包含兩個新特性:二進位制文字、數字分隔符,如下所示:

var one = 0b0001;
var sixteen = 0b0001_0000;
long salary = 1000_000_000;
decimal pi = 3.141_592_653_589m;

 注:二進位制文字是以0b(零b)開頭,字母不區分大小寫;數字分隔符只有三個地方不能寫:開頭,結尾,小數點前後。

 總結:二進位制文字,數字分隔符 可使常量值更具可讀性。

七、非同步的優化

  儘管如此,非同步方法可以返回的內容一些小改進,但是可以在某些情況下提供巨大的效能提升。 你不再需要返回一個Task,如果值已經可用這可以減少開銷,使用async方法來建立Task物件。

八、總結

  在這個介紹性章節中,您看到了一個簡短而高階的摘要,與先前版本相比,.NET Core 2和ASP.NET Core 2眾多變化。

現在,你也知道了.NET Standard 2及其用途。我們展示了C#6和C#7中可用的一些新功能的例子。

這些可能非常有用的是讓你用更少的東西編寫更多東西,並使你的程式碼更易讀和更容易維護。

       《ASP.NET Core 高效能系列》這是一本關於一般Web應用程式效能改進的文章,而且很多無視語言或框架的知識。

  下一章中,您將瞭解效能如何重要,並瞭解嶄新的新.NET Core開發棧, 我們還將看到可用的工具,並瞭解用圖表展示硬體效能。

 

相關文章