(八)分散式通信----主機Host

来源:https://www.cnblogs.com/hongwei918/archive/2019/09/01/11415390.html
-Advertisement-
Play Games

==>>點擊查看本系列文章目錄 上節中有談到的是通信主機(TransportHost),本節中主機(ServiceHost)負責管理服務的生命周期。 項目中將兩個主機拆分開,實現不同的功能: 通信主機:用於啟動通信監聽埠; 生命周期管理的主機:負責模塊功能的依賴註入,管理生命周期。 先看一下啟動服 ...


 ==>>點擊查看本系列文章目錄

 

上節中有談到的是通信主機(TransportHost),本節中主機(ServiceHost)負責管理服務的生命周期。

項目中將兩個主機拆分開,實現不同的功能:

通信主機:用於啟動通信監聽埠;

生命周期管理的主機:負責模塊功能的依賴註入,管理生命周期。

 

先看一下啟動服務端主機和客戶端主機後完成通信的效果圖:

 

 文件結構如下:

 

ServiceHost 主機由ServiceHostBuilder來構建。

過程如下:

先看調用圖: 

1.Program中Main() 調用 ServiceHostBuilder 的方法:MapServices、RegisterServices、ConfigureServices、Configure

  分別將委托填充到 List<Action<IContainer>>、List<Action<ContainerBuilder>>、List<Action<IServiceCollection>>、List<Action<IConfigurationBuilder>> 類型的容器中。

  其中 IContainer、ContainerBuilder 是 Autofac中的容器,IServiceCollection、IConfigurationBuilder 是 Microsoft中的容器。

2. Program中Main() 調用 ServiceHostBuilder 的方法 UseStartup<Startup>()  ,Startup 必須實現 IStartup,完成Startup 的單例註入(微軟中的 Startup 可以不實現 IStartup ,但是必須使用方法ConfigureServices、Configure)

3. Program中Main() 調用 ServiceHostBuilder 的方法 Build()

  (1)回調容器 List<Action<IContainer>>、List<Action<ContainerBuilder>>、List<Action<IServiceCollection>>、List<Action<IConfigurationBuilder>> 中的委托。

      容器生成過程: ConfigureServices   ---》  List<Action<IServiceCollection>>      ---》  IServiceCollection

            Configure      ---》  List<Action<IConfigurationBuilder>>      ---》  IConfigurationBuilder

            IServiceCollection + IConfigurationBuilder  ---》  IServiceCollection   ---》  ServiceProvider

            RegisterServices     ---》  List<Action<ContainerBuilder>>       ---》  ContainerBuilder

            ContainerBuilder + IServiceCollection         ---》  ContainerBuilder 

            MapServices      ---》  List<Action<IContainer>>

  (2)將上面紅色字體的對象通過構造函數傳給new 的 ServiceHost 對象。

  (3)調用ServiceHost .Initialize(),  該方法中執行如下過程

    a. 用ServiceProvider 解析出 Startup 對象

    b. 回調Startup的 IContainer ConfigureServices(ContainerBuilder builder) , 返回構建好的容器 IContainer

    c. 回調Startup的 void Configure(IContainer app) , IContainer中註入其它功能

  (4)將包含IContainer容器的ServiceHost 對象返回

4. ServiceHost.Run(), 回調主機中一直未執行的容器委托 List<Action<IContainer>> 

總結一下,整個過程就是將原來的四個委托的容器最後合併成一個 IContainer 容器。

解析容器中的服務,可以用 :

IContainer _container;
IDemoService service = _container.Resolve<IDemoService>(); 

 

服務端和客戶端啟動:

 

 

代碼:

我們先看客戶端和服務端代碼:

服務端:

namespace Leo.ServiceLaunch.Server
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Server, Hello World!");

            var host = new ServiceHostBuilder()
                .RegisterServices(builder =>
                {
                    builder.RegisterType<MessagePackTransportMessageCodecFactory>().As<ITransportMessageCodecFactory>().SingleInstance();
                    builder.RegisterType(typeof(HttpServiceExecutor)).As(typeof(IServiceExecutor)).Named<IServiceExecutor>("tcp").SingleInstance();
                    builder.Register(provider =>
                    {
                        return new DotNettyServerMessageListener(provider.Resolve<ILogger<DotNettyServerMessageListener>>(),
                              provider.Resolve<ITransportMessageCodecFactory>());
                    }).SingleInstance();
                    builder.Register(provider =>
                    {
                        var serviceExecutor = provider.ResolveKeyed<IServiceExecutor>("tcp");
                        var messageListener = provider.Resolve<DotNettyServerMessageListener>();
                        return new DotNettyTransportHost(async endPoint =>
                        {
                            await messageListener.StartAsync(endPoint);
                            return messageListener;
                        }, serviceExecutor);
                    }).As<ITransportHost>();
                })
                .UseServer()  // 指定監聽的埠
                .UseStartup<Startup>()
                .Build();

            using (host.Run())
            {
                Console.WriteLine($"服務端啟動成功,{DateTime.Now}。");
            }

            Console.ReadLine();
        }

    }
}
namespace Leo.ServiceLaunch.Server
{
    class Startup : IStartup
    {
        public IContainer ConfigureServices(ContainerBuilder builder)
        {
            return builder.Build();
        }

        public void Configure(IContainer app)
        {

        }
    }
}

客戶端:

namespace Leo.ServiceLaunch.Client
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Client, Hello World!");

            var host = new ServiceHostBuilder()
                .RegisterServices(builder =>
                {
                    builder.RegisterType<MessagePackTransportMessageCodecFactory>().As<ITransportMessageCodecFactory>().SingleInstance();
                    builder.Register(provider =>
                    {
                        IServiceExecutor serviceExecutor = null;  
                        if (provider.IsRegistered(typeof(IServiceExecutor)))  // 沒有註冊客戶端接收消息執行器,因此一直為空
                            serviceExecutor = provider.Resolve<IServiceExecutor>();
                        return new DotNettyTransportClientFactory(provider.Resolve<ITransportMessageCodecFactory>(),
                            provider.Resolve<ILogger<DotNettyTransportClientFactory>>(),
                            serviceExecutor);
                    }).As(typeof(ITransportClientFactory)).SingleInstance();
                })
                .UseStartup<Startup>()
                .Build();

            using (host.Run())
            {
                Startup.Test();
            }
            Console.ReadLine();
        }
    }
}
namespace Leo.ServiceLaunch.Client
{
    class Startup : IStartup
    {
        private static IContainer _container;
        public void Configure(IContainer app)
        {
        }

        public IContainer ConfigureServices(ContainerBuilder builder)
        {
            _container = builder.Build();
            return _container;
        }

        internal static void Test()
        {
            Task.Run(async () =>
            {
                do
                {
                    Console.WriteLine("正在迴圈 1萬次發送消息.....");

                    //1w次調用
                    var watch = Stopwatch.StartNew();
                    for (var i = 1; i < 10000; i++)
                    {
                        var invokeMessage = new TransportMessage
                        {
                            Id = i.ToString(),
                            ContentType = "string",
                            Content = "你好啊,這是客戶端發給服務端的消息"
                        };
                        try
                        {
                            var endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 981);
                            ITransportClientFactory transportClientFactory = _container.Resolve<ITransportClientFactory>();
                            var client = await transportClientFactory.CreateClientAsync(endPoint);
                            await client.SendAsync(invokeMessage);
                        }
                        catch (Exception exception)
                        {
                            Console.WriteLine(exception.ToString(), $"發起請求中發生了錯誤,服務Id:{invokeMessage.Id}。");
                            throw;
                        }
                    }
                    watch.Stop();
                    Console.WriteLine($"1萬次發送結束,執行時間:{watch.ElapsedMilliseconds}ms");
                    Console.WriteLine("Press any key to continue, q to exit the loop...");
                    var key = Console.ReadLine();
                    if (key.ToLower() == "q")
                        break;
                } while (true);
            }).Wait();
        }
    }
}

主機:

IServiceHost:

    public interface IServiceHost : IDisposable
    {
        IDisposable Run();

        IContainer Initialize();
    }

IServiceHostBuilder:

    public interface IServiceHostBuilder
    {
        IServiceHost Build();

        IServiceHostBuilder RegisterServices(Action<ContainerBuilder> builder);

        IServiceHostBuilder ConfigureServices(Action<IServiceCollection> configureServices);

        IServiceHostBuilder Configure(Action<IConfigurationBuilder> builder);

        IServiceHostBuilder MapServices(Action<IContainer> mapper);
    }

ServiceHost:

public class ServiceHost : IServiceHost
    {
        private readonly ContainerBuilder _builder;
        private IStartup _startup;
        private IContainer _applicationServices;
        private readonly IServiceProvider _hostingServiceProvider;
        private readonly List<Action<IContainer>> _mapServicesDelegates;

        public ServiceHost(ContainerBuilder builder,
            IServiceProvider hostingServiceProvider,
             List<Action<IContainer>> mapServicesDelegate)
        {
            _builder = builder;
            _hostingServiceProvider = hostingServiceProvider;
            _mapServicesDelegates = mapServicesDelegate;
        }

        public IContainer Initialize()
        {
            if (_applicationServices == null)
            {
                try
                {
                    if (_applicationServices == null)
                    {
                        if (_startup == null)
                        {
                            // 解析出 Startup 
                            _startup = _hostingServiceProvider.GetRequiredService<IStartup>();
                        }
                        //回調Startup中的 ConfigureServices,
                        _applicationServices = _startup.ConfigureServices(_builder);
                    }
                    if (_applicationServices == null)
                        _applicationServices = _builder.Build();
                    Action<IContainer> configure = _startup.Configure;
                    configure(_applicationServices);
                }
                catch (Exception ex)
                {
                    Console.Out.WriteLine("應用程式啟動異常: " + ex.ToString());
                    throw;
                }
            }
            return _applicationServices;
        }

        public IDisposable Run()
        {
            RunAsync().GetAwaiter().GetResult();
            return this;
        }

        public async Task RunAsync()
        {
            if (_applicationServices != null)
                MapperServices(_applicationServices);
        }

        private void MapperServices(IContainer mapper)
        {
            foreach (var mapServices in _mapServicesDelegates)
            {
                mapServices(mapper);
            }
        }

        public void Dispose()
        {
            (_hostingServiceProvider as IDisposable)?.Dispose();
        }
    }

ServiceHostBuilder:

public class ServiceHostBuilder : IServiceHostBuilder
    {
        private readonly List<Action<IServiceCollection>> _configureServicesDelegates;
        private readonly List<Action<ContainerBuilder>> _registerServicesDelegates;
        private readonly List<Action<IConfigurationBuilder>> _configureDelegates;
        private readonly List<Action<IContainer>> _mapServicesDelegates;

        public ServiceHostBuilder()
        {
            _configureServicesDelegates = new List<Action<IServiceCollection>>();
            _registerServicesDelegates = new List<Action<ContainerBuilder>>();
            _configureDelegates = new List<Action<IConfigurationBuilder>>();
            _mapServicesDelegates = new List<Action<IContainer>>();

        }

        public IServiceHost Build()
        {
            #region Microsoft原生的容器
            //執行 IServiceCollection 類型的委托
            var services = BuildCommonServices();
            //執行 IConfigurationBuilder 類型的委托
            var config = Configure();
            //日誌註入到 IServiceCollection
            services.AddLogging();
            //IConfigurationBuilder 註入到 IServiceCollection
            services.AddSingleton(typeof(IConfigurationBuilder), config);
            //用 IServiceCollection 生成 ServiceProvider 服務提供器
            var hostingServiceProvider = services.BuildServiceProvider();
            #endregion

            #region Autofac的容器
            //執行 ContainerBuilder 類型的委托
            var hostingServices = RegisterServices();
            #endregion

            //將 IServiceCollection 填充到 Autofac 的 ContainerBuilder 構建器中
            hostingServices.Populate(services);


            //把Autofac的ContainerBuild的容器構建器、Microsoft的ServiceProvider服務提供器、已有的IContainer容器的委托 都放入主機中
            var host = new ServiceHost(hostingServices, hostingServiceProvider, _mapServicesDelegates);
            //主機初始化以後返回的是IContainer容器
            var container = host.Initialize();
            return host;
        }

        public IServiceHostBuilder MapServices(Action<IContainer> mapper)
        {
            if (mapper == null)
            {
                throw new ArgumentNullException(nameof(mapper));
            }
            _mapServicesDelegates.Add(mapper);
            return this;
        }

        public IServiceHostBuilder RegisterServices(Action<ContainerBuilder> builder)
        {
            if (builder == null)
            {
                throw new ArgumentNullException(nameof(builder));
            }
            _registerServicesDelegates.Add(builder);
            return this;
        }

        public IServiceHostBuilder ConfigureServices(Action<IServiceCollection> configureServices)
        {
            if (configureServices == null)
            {
                throw new ArgumentNullException(nameof(configureServices));
            }
            _configureServicesDelegates.Add(configureServices);
            return this;
        }

        public IServiceHostBuilder Configure(Action<IConfigurationBuilder> builder)
        {
            if (builder == null)
            {
                throw new ArgumentNullException(nameof(builder));
            }
            _configureDelegates.Add(builder);
            return this;
        }

        private IServiceCollection BuildCommonServices()
        {
            var services = new ServiceCollection();
            foreach (var configureServices in _configureServicesDelegates)
            {
                configureServices(services);
            }
            return services;
        }

        private IConfigurationBuilder Configure()
        {
            //var config = new ConfigurationBuilder().SetBasePath(AppContext.BaseDirectory);
            var config = new ConfigurationBuilder();
            foreach (var configure in _configureDelegates)
            {
                configure(config);
            }
            return config;
        }

        private ContainerBuilder RegisterServices()
        {
            var hostingServices = new ContainerBuilder();
            foreach (var registerServices in _registerServicesDelegates)
            {
                registerServices(hostingServices);
            }
            return hostingServices;
        }
    }

IStartup:

    public interface IStartup
    {
        IContainer ConfigureServices(ContainerBuilder builder);

        void Configure(IContainer app);
    }

ServerExtensions:

    public static class ServerExtensions
    {
        public static IServiceHostBuilder UseServer(this IServiceHostBuilder hostBuilder)
        {
            return hostBuilder.MapServices(async mapper =>
            {
                int _port = 981;
                string _ip = "127.0.0.1";

                Console.WriteLine($"準備啟動服務主機,監聽地址:{_ip}:{_port}。");
                var transportHosts = mapper.Resolve<IList<ITransportHost>>();
                Task.Factory.StartNew(async () =>
                {
                    foreach (var transportHost in transportHosts)
                        await transportHost.StartAsync(_ip, _port);
                }).Wait();
            });
        }

        public static IServiceHostBuilder UseStartup<TStartup>(this IServiceHostBuilder hostBuilder) where TStartup : IStartup
        {
            return hostBuilder
                .ConfigureServices(services =>
                {
                    services.AddSingleton(typeof(IStartup), typeof(TStartup));
                });
        }
    }

 


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

-Advertisement-
Play Games
更多相關文章
  • StreamGraph <! more https://t.zsxq.com/qRFIm6I 博客 1、 "Flink 從0到1學習 —— Apache Flink 介紹" 2、 "Flink 從0到1學習 —— Mac 上搭建 Flink 1.6.0 環境並構建運行簡單程式入門" 3、 "Flin ...
  • 今日所學: /* 2019.08.19開始學習,此為補檔。 */ 流程式控制制 條件: 一重用if 二重用if ... else 三重用if ... else if ... else 多重用switch 例: 迴圈: while和do ... while的區別: while是先判斷後執行,do ... ...
  • 1 下載安裝包 1.1 壓縮包 "https://dev.mysql.com/downloads/mysql/" [外鏈圖片轉存失敗(img oesO8K09 1566652568838)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKA ...
  • 今日所學: /* 2019.08.19開始學習,此為補檔。 */ 三目(元)運算符 格式:(表達式)? 表達式為true返回值A : 表達式為false返回值B 例: 面向對象的基本概念 定義:以基於對象的思維去分析和解決問題,萬物皆對象。 三大特性:封裝,繼承,多態。 ...
  • 以上代碼第一個if後面的內容永遠被執行。 這裡需要考慮到優先順序問題,一般而言,算數運算>關係運算>邏輯運算>位運算>賦值運算。 上面代碼中,m==1返回false,false與3進行"或"運算,3被視為true。結果返回true.同理最後返回true,所以if後的語句就一直被執行了。 常見的還有下麵 ...
  • 1、JDK https://www.oracle.com/technetwork/java/javase/downloads/jdk8 downloads 2133151.html 目前主流的JDK版本還是JAVA8,我在阿裡用的也是Java8。 JDK里已經包含了JRE也就是Java虛擬機和運行環 ...
  • 在Web系統開發過程中,文件上傳是普遍的功能,本文主要以一個簡單的小例子,講解SpringMVC中文件上傳的使用方法,僅供學習分享使用,如有不足之處,還請指正。 ...
  • .NET Core ASP.NET Core Basic 1 2 本節內容為控制反轉與依賴註入 簡介 控制反轉IOC 這個內容事實上在我們的C 高級篇就已經有所講解,控制反轉是一種設計模式,你可以這樣理解控制反轉,假設有一個人他有一部A品牌手機,他用手機進行聽歌、打游戲,那麼你可以創建一個手機類和一 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...