helios架構詳解(一)伺服器端架構

来源:http://www.cnblogs.com/watermoon2/archive/2016/01/20/5141722.html
-Advertisement-
Play Games

看了“菜鳥耕地”的”.NET開源高性能Socket通信中間件Helios介紹及演示“,覺得這個東西不錯。但是由於沒有網路編程知識,所以高性能部分我就講不出來了,主要是想根據開源代碼跟大家分享下Helios的架構。源代碼下載地址:https://github.com/helios-io/helios首...


看了“菜鳥耕地”的”.NET開源高性能Socket通信中間件Helios介紹及演示“,覺得這個東西不錯。但是由於沒有網路編程知識,所以高性能部分我就講不出來了,主要是想根據開源代碼跟大家分享下Helios的架構。

源代碼下載地址:https://github.com/helios-io/helios

 首先我們獻上伺服器端結構圖:

這樣的一個大圖片,估計很多地方都挺迷糊的,我們就詳細的講解下期中的邏輯。

ServerBootstrap類

該類是伺服器端的核心類,伺服器端提供服務的就是ServerBootstrap對象(實際上是它的子類,並且子類是由這個對象創建的)。

 創建代碼時我們會使用代碼

 var serverFactory =
                new ServerBootstrap()
                    .SetTransport(TransportType.Tcp)
                    .Build();
  • 該類有三個核心屬性:IExecutor 、IServerFactory(IConnectionFactory)、NetworkEventLoop。
    public class ServerBootstrap : AbstractBootstrap
    {
        protected IExecutor InternalExecutor { get; set; }

        protected NetworkEventLoop EventLoop
        {
            get
            {
                return EventLoopFactory.CreateNetworkEventLoop(Workers, InternalExecutor);
            }
        }
        protected override IConnectionFactory BuildInternal()
        {
            switch (Type)
            {
                case TransportType.Tcp:
                    return new TcpServerFactory(this);
                case TransportType.Udp:
                    return new UdpServerFactory(this);
                default:
                    throw new InvalidOperationException("This shouldn't happen");
            }
        }

        public new IServerFactory Build()
        {
            return (IServerFactory) BuildInternal();
        }

    }
核心屬性和方法
  • 另外一個有特點的地方就是鏈式編程(可能借鑒於jquery),設置對象都返回個this指針。
    public class ServerBootstrap : AbstractBootstrap
    {
       public ServerBootstrap WorkersShareFiber(bool shareFiber)
        {
            UseSharedFiber = shareFiber;
            SetOption("proxiesShareFiber", UseSharedFiber);
            return this;
        }

        public new ServerBootstrap SetTransport(TransportType type)
        {
            base.SetTransport(type);
            return this;
        }

        public ServerBootstrap WorkerThreads(int workerThreadCount)
        {
            if (workerThreadCount < 1) throw new ArgumentException("Can't be below 1", "workerThreadCount");
            Workers = workerThreadCount;
            return this;
        }

        public ServerBootstrap BufferSize(int bufferSize)
        {
            if (bufferSize < 1024) throw new ArgumentException("Can't be below 1024", "bufferSize");
            BufferBytes = bufferSize;
            return this;
        }

        public ServerBootstrap WorkersAreProxies(bool useProxies)
        {
            UseProxies = useProxies;
            return this;
        }

        public ServerBootstrap Executor(IExecutor executor)
        {
            if (executor == null) throw new ArgumentNullException("executor");
            InternalExecutor = executor;
            return this;
        }

        public new ServerBootstrap SetConfig(IConnectionConfig config)
        {
            base.SetConfig(config);
            return this;
        }

        public new ServerBootstrap SetDecoder(IMessageDecoder decoder)
        {
            base.SetDecoder(decoder);
            return this;
        }

        public new ServerBootstrap SetEncoder(IMessageEncoder encoder)
        {
            base.SetEncoder(encoder);
            return this;
        }

        public new ServerBootstrap SetAllocator(IByteBufAllocator allocator)
        {
            base.SetAllocator(allocator);
            return this;
        }

        public new ServerBootstrap OnConnect(ConnectionEstablishedCallback connectionEstablishedCallback)
        {
            base.OnConnect(connectionEstablishedCallback);
            return this;
        }

        public new ServerBootstrap OnDisconnect(ConnectionTerminatedCallback connectionTerminatedCallback)
        {
            base.OnDisconnect(connectionTerminatedCallback);
            return this;
        }

        public new ServerBootstrap OnReceive(ReceivedDataCallback receivedDataCallback)
        {
            base.OnReceive(receivedDataCallback);
            return this;
        }
        public new ServerBootstrap OnError(ExceptionCallback exceptionCallback)
        {
            base.OnError(exceptionCallback);
            return this;
        }

        public new ServerBootstrap SetOption(string optionKey, object optionValue)
        {
            base.SetOption(optionKey, optionValue);
            return this;
        }
}
鏈式編程

我們調用最後,肯定是使用build方法,而build方法實際上調用的是BuildInternal內部方法,而該這又是一個工廠模式(和後滿ServerFactory組成抽象工廠??),會返回TcpServerFactory或者UdpServerFactory。

TcpServerFactory和UdpServerFactory

這倆個類其實沒有什麼核心代碼,但是你網上追溯父類的時候你會發現TcpServerFactory(UdpServerFactory)=>ServerFactoryBase => ServerBootstrap。它們依舊是ServerBootstrap對象。不過不同的地方就是,他們除了爹還有了一個媽媽ServerFactoryBase =>IServerFactory =>IConnectionFactory。

我們看下ServerFactoryBase 源碼:

    public abstract class ServerFactoryBase : ServerBootstrap, IServerFactory
    {
        protected ServerFactoryBase(ServerBootstrap other)
            : base(other)
        {
        }

        protected abstract ReactorBase NewReactorInternal(INode listenAddress);

        public IReactor NewReactor(INode listenAddress)
        {
            var reactor = NewReactorInternal(listenAddress);
            reactor.Configure(Config);

            if (ReceivedData != null)
                reactor.OnReceive += (ReceivedDataCallback)ReceivedData.Clone();
            if (ConnectionEstablishedCallback != null)
                reactor.OnConnection += (ConnectionEstablishedCallback)ConnectionEstablishedCallback.Clone();
            if (ConnectionTerminatedCallback != null)
                reactor.OnDisconnection += (ConnectionTerminatedCallback)ConnectionTerminatedCallback.Clone();
            if (ExceptionCallback != null)
                reactor.OnError += (ExceptionCallback) ExceptionCallback.Clone();

            return reactor;
        }

        public IConnection NewConnection()
        {
            return NewConnection(Node.Any());
        }

        public IConnection NewConnection(INode localEndpoint)
        {
            var reactor = (ReactorBase)NewReactor(localEndpoint);
            return reactor.ConnectionAdapter;
        }

        public IConnection NewConnection(INode localEndpoint, INode remoteEndpoint)
        {
            return NewConnection(localEndpoint);
        }
    }

 

發現它們母親(IConnectionFactory)要做的事都是通過IReactor來完成的。而它們(TcpServerFactory和UdpServerFactory)只是找到合適的IReactor對象而已,另一方面我們也可以看出真正負責網路連接的就是IReactor對象。它就是保證底層通訊的邏輯。

    public sealed class TcpServerFactory : ServerFactoryBase
    {
        public TcpServerFactory(ServerBootstrap other)
            : base(other)
        {
        }

        protected override ReactorBase NewReactorInternal(INode listenAddress)
        {
            if (UseProxies)
                return new TcpProxyReactor(listenAddress.Host, listenAddress.Port, EventLoop, Encoder, Decoder,
                    Allocator, BufferBytes);
            else
                throw new NotImplementedException("Have not implemented non-TCP proxies");
        }
    }
TcpServerFactory
    public sealed class UdpServerFactory : ServerFactoryBase
    {
        public UdpServerFactory(ServerBootstrap other) : base(other)
        {
        }

        protected override ReactorBase NewReactorInternal(INode listenAddress)
        {
            return new UdpProxyReactor(listenAddress.Host, listenAddress.Port, EventLoop, Encoder, Decoder, Allocator, BufferBytes);
        }
    }
UdpServerFactory

 IReactor們

 這裡包含TcpServerFactory內部使用的TcpProxyReactor、UdpServerFactory使用的UdpProxyReactor,以及他們的基類ProxyReactorBase、ReactorBase。他們之間的關係為:

  • TcpProxyReactor => ProxyReactorBase => ReactorBase =>IReactor
  • UdpProxyReactor => ProxyReactorBase => ReactorBase =>IReactor
  • ReactorConnectionAdapter =>IConnection(適配器模式,內部封裝IReactor)

*嚴格說ReactorConnectionAdapter 不算是IReactor,它只是適配器模式,使得IReactor對象能夠和IConnection對象模式適配 

雖然類不是很多,但估計helios的高效可能核心就和這部分有關係。但是我不太瞭解通訊相關內容,只能從構建的方式大致的講下,有興趣的人可以自己深入研究。

  • ReactorBase :定義了基本操作、事件。對於接收,發送提供預設操作
  • ProxyReactorBase :增加了ReactorResponseChannel對象,重載接收方法(ReceivedData,調用的是ReactorResponseChannel的OnReceive)
  • TcpProxyReactor :重載StartInternal方法,使用TcpReactorResponseChannel進行數據接收
  • UdpProxyReactor :重載StartInternal方法,使用ReactorProxyResponseChannel進行數據接收

ReactorResponseChannel們

 此處包含三個類ReactorResponseChannel、TcpReactorResponseChannel、ReactorProxyResponseChannel。

  • ReactorResponseChannel 基類,定義基礎操作。主要是Send方法
  • TcpReactorResponseChannel,TCP協議下的ReactorResponseChannel實現。
  • ReactorProxyResponseChannel,ReactorResponseChannel的代理,實際上就是把ReactorResponseChannel虛方法變成空方法而已。

ReactorResponseChannel中OnReceive方法調用的是”NetworkEventLoop.Receive(data, this);“,將數據發送到EventLoop消息隊列)中。

EventLoop(消息隊列)

消息隊列一共有三個層次繼承,分別是:NetworkEventLoop、ThreadedEventLoop、AbstractEventLoop

繼承關係為:NetworkEventLoop=> ThreadedEventLoop=> AbstractEventLoop。

  • AbstractEventLoop:內部使用IFiber對象,進行消息處理。所有的處理方法最終走的都是IFiber對象(實際上IFiber中維護一個列表,之後由IFiber對象決定如何處理)
  • ThreadedEventLoop:構造函數構建自己的IFiber對象(預設使用的是:DedicatedThreadPoolFiber)
  • NetworkEventLoop:將網路事件、數據接收事件用IFiber對象處理

IFiber們

 IFiber的作用不是處理接收的數據,而是在乎用什麼樣的方式處理數據,比如起幾個線程,同步還是非同步的處理。IFiber對象有好幾個,但是實際上真正用的只有1個(DedicatedThreadPoolFiber),但是不妨礙我們去看看這些對象。

  • DedicatedThreadPoolFiber使用hebios自己的線程池技術(DedicatedThreadPool),底層通過線程池來處理數據。
  • SynchronousFiber同步處理,當一個操作進入消息隊列的時候立即處理
  • ThreadPoolFiber使用線程池技術,底層通過線程池來處理數據
  • SharedFiber共用Fiber,當NetworkEventLoop.clone()的時候,只是簡單的將NetworkEventLoop的IFiber對象傳遞過來,以達到多個NetworkEventLoop共用IFiber的目的

 最後的處理類:BasicExecutor/TryCatchExecutor

在IFiber裡面,我們會預設構造BasicExecutor對象(TryCatchExecutor繼承自BasicExecutor,可以catch住異常),這個類會最終處理伺服器端的數據請求。

總結:伺服器端處理數據的順序為:創建ServerBootstrap對象,構建出它的子類(IConnectionFactory),之後分別進行網路通訊(IReactor),通訊管道(ReactorResponseChannel)對數據接收發送管理,之後數據進入消息隊列(EventLoop),伺服器端決定處理數據的線程技術(IFiber),最終將數據處理(BasicExecutor

*這不是真實的接送邏輯,而是我們沿著源代碼求索邏輯的順序。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...