為什麼推薦Kestrel作為網路開發框架

2022-12-06 15:00:30

為什麼推薦Kestrel

網路框架千千萬萬,在dotnet平臺,我們可以直接手擼Socket,也可以基於dotnetty來開發,或者選擇某些第三方類似於dotnetty的網路庫,為何我要推薦Kestrel呢?

1 使用框架

網路程式設計是簡單的,簡單到大概就 new Socket(),Send()傳送資料,Receive()接收資料,這大概是初學者的大致感受。

網路程式設計是複雜的,讓Send()和Receive()穩定工作,花了老夫一年時間,每讓伺服器的效能提高10%又各花老夫兩年時間,這大概是手擼過Socket的大哥的感受。

網路程式設計是抽象的傳輸層加高效的緩衝區管理,我們需要把它提升到框架來,而不能停留在原始的Socket工具級別。這大概是我從dotnetty和kestrel裡悟出的道理。

2 框架的支撐者

選擇某個框架,咱首先要看看這個這個框架背後的支撐者的力量。Kestrel是asp.netcore的Server部分,如果asp.netcore說它是dotnet平臺上第二齣名的應用框架,那沒其它框架敢說第一是自己。我們可以通過commits來檢視有哪些大牛在孜孜不倦地維護kestrel,其中@JamesNK、@BrennanConroy、@davidfowl等世界級大牛一直很活躍。反觀其它網路框架,只有少量的社群力量甚至作者單個人的力量在貢獻。

3 Kestrel的影響

三流的框架在自詡,二流的框架在吸取新鮮技術的養分,一流的框架在推動相關領域技術前行。

3.1 推動System.Net.Socket

在dotnet core 2.0或以前,Kestrel使用Libuv取代dotnet的Socket來操作網路,因為彼時dotnet的Socket效能,要比Libuv要差一些,特別在unix上的表現。也正是因為asp.netcore的kestrel對Socket效能有強烈的需求,在2.1時runtime層開始對Socket的效能大力改進,Task和ValueTask的非同步傳送和接收內部實現融入了SocketAsyncEventArgs,Socket甚至為NetworkStream開了路燈,讓Socket與Libuv的效能直接平級。

3.2 推動System.IO.Pipelines

Pipelines誕生於.NET Core團隊為使Kestrel成為業內最快的Web伺服器之一所做的工作。最初是Kestrel內部的一個實現細節,後來發展成為一個可重用的API,它在dotnet coreapp 2.1 中作為一流的 BCL API(System.IO.Pipelines)提供給所有 .NET 開發人員。

正確解析來自Stream或Socket的資料的工作其實非常複雜,沉長和複雜的程式碼讓人難以閱讀和維護。再加上要實現高效能這條要求的話,就讓人更加吐血,而Pipelines旨在解決這種複雜性。
有關Pipelines的好,我就不班門弄斧了,這是@davidfowl寫的Pipelines介紹

3.3 對普通開發者的影響

曾經一個小小SocketAwaitableEventArgs class,讓多少開發者眼前一亮,驚歎無比。這不,現在已經不是最初實現了ICriticalNotifyCompletion介面了,轉為實現了IValueTaskSource<SocketOperationResult>,大家慢慢品吧。

4 Kestrel的魅力

4.1 單應用層多傳輸層

支援一個應用監聽多個埠,每個埠走不同傳輸層,最後到達同一個應用協定層。比如下面的設定,傳輸層分別是tcp和tls over tcp,應用層都是http,不管是哪種傳輸最終都是被我們的application層統一處理http,簡稱殊途同歸。

"Kestrel": {
    "Endpoints": {
        "http": {  
            "Url": "http://localhost:5000"
        },
        "https": {  
            "Url": "https://localhost:5001"
        } 
    },
    "Certificates": {
        "Default": {
        "Path": "",
        "Password": ""
        }
    }
}

4.2 單傳輸層多應用層

我們也可以使用某個監聽埠對應的傳輸層,分支不同的路由來實現多個應用協定application。常見的比如kestrel使用websocket做傳輸層,應用協定層為mqtt或signalr等。

// Mqtt over WebSocket
app.MapConnectionHandler<MqttConnectionHandler>("/mqtt");

// SingalR over Websocket
app.MapHub<SingalRHub>("/signalr");

4.3 自定義應用層

我們這裡說所的應用層協定,往往是我們在這層協定上構建了業務,而不拿它來做傳輸協定,而實際中,一種協定往往即可以做廣義的傳輸協定,也可以直接做構建業務的應用層協定(典型的WebSocket,甚至http也可以做傳輸協定)。在asp.netcore中,SingalR就是典型的一個不太複雜的應用層協定(相對http),我們也可以基於kestrel來開發telnet over tcp的服務,telnet做為應用層,tcp做傳輸層。

public class TelnetConnectionHandler : ConnectionHandler
{
    /// <summary>
    /// 收到Telnet連線後
    /// </summary>
    /// <param name="connection"></param>
    /// <returns></returns>
    public override async Task OnConnectedAsync(ConnectionContext connection)
    {
        var input = connection.Transport.Input;
        var output = connection.Transport.Output;

        // 從input解析telnet協定
        ...
    }
}
public static class ListenOptionsExtensions
{
    /// <summary>
    /// 使用TelnetConnectionHandler
    /// </summary>
    /// <param name="listen"></param>
    public static void UseTelnet(this ListenOptions listen)
    {
        listen.UseConnectionHandler<TelnetConnectionHandler>();
    }
} 
var section = context.Configuration.GetSection("Kestrel");
kestrel.Configure(section).Endpoint("Telnet", endpoint => endpoint.ListenOptions.UseTelnet());

4.4 增加傳輸層

假設我們需要telnet應用增加支援tls安全傳輸,我們可以再增加一個Telnets的EndPoint。在telnet協定之前插入https(實際準確是的叫tls)中介軟體。現在不管是未加密的telnet請求還是tls加密的telnet請求,我們的應用層TelnetConnectionHandler都能收到telnet請求內容。

var section = context.Configuration.GetSection("Kestrel");
kestrel.Configure(section).Endpoint("Telnets", endpoint => endpoint.ListenOptions.UseHttps().UseTelnet());

4.5 自定義傳輸層

在Stream設計模式裡,往往需要開發TransportStream,其包裝原始Stream且在自身的Read/Write方法裡做必要的資料解碼/編碼操作,比如SslStream(Stream inner),向SSlStream寫入[1,2,3,4]的資料,實際上是向inner Stream寫入了[1,2,3,4]加密後的資料。

Kestrel的傳輸層是IDuplexPipe型別的抽象物件,我們可以把IDuplexPipe物件轉換為Stream物件,然後與既有的Stream套娃模式結合,再把最後的Stream轉為IDuplexPipe型別,替換到kestrel的連線物件的傳輸層。

這是一個高階但不太常用的功能,想了解更多可以檢視KestrelApp.Transforms這個專案範例。

5 如何學習Kesrel

為了大家能學習Kestrel,我在github上開源了一個kestrel開發綜合範例專案。
喜歡拿程式碼說話的同學,可以直接食用;喜歡理論指導行動的同學,可以先慢慢看專案上的檔案連結,然後再一步步慢慢深入。
專案地址: https://github.com/xljiulang/KestrelApp