.Net Core微服務入門全紀錄(一)——項目搭建

来源:https://www.cnblogs.com/xhznl/archive/2020/06/12/13071260.html
-Advertisement-
Play Games

前言 寫這篇博客主要目的是記錄一下自己的學習過程,只能是簡單入門級別的,因為水平有限就寫到哪算哪吧,寫的不對之處歡迎指正。 代碼放在:https://github.com/xiajingren/NetCoreMicroserviceDemo 什麼是微服務? 關於微服務的概念解釋網上有很多... 個人 ...


前言

寫這篇博客主要目的是記錄一下自己的學習過程,只能是簡單入門級別的,因為水平有限就寫到哪算哪吧,寫的不對之處歡迎指正。
代碼放在:https://github.com/xiajingren/NetCoreMicroserviceDemo

什麼是微服務?

關於微服務的概念解釋網上有很多...
個人理解,微服務是一種系統架構模式,它和語言無關,和框架無關,和工具無關,和伺服器環境無關...
微服務思想是將傳統的單體系統按照業務拆分成多個職責單一、且可獨立運行的介面服務,各個服務之間不耦合。至於服務如何拆分,沒有明確的定義。
幾乎任何後端語言都能做微服務開發。
微服務也並不是完美無缺的,微服務架構會帶來更多的問題,增加系統的複雜度,引入更多的技術棧...

創建項目


一個客戶端,一個產品服務,一個訂單服務。3個項目都是asp.net core web應用程式。創建項目的時候記得啟用一下Docker支持,或者後面添加也行。

為產品、訂單服務添加一些基礎代碼,就簡單的返回一下 服務名稱,當前時間,服務的ip、埠。

在Docker中運行服務

為了方便,我使用Docker來運行服務,不用Docker也行,關於docker的安裝及基本使用就不介紹了。

  • build鏡像:

在項目根目錄打開PowerShell視窗執行:docker build -t productapi -f ./Product.API/Dockerfile .


Successfully代表build成功了。

  • 運行容器:

執行:docker run -d -p 9050:80 --name productservice productapi

執行:docker ps查看運行的容器:

沒問題,使用瀏覽器訪問一下介面:

也沒問題,其中的ip埠是Docker容器內部的ip埠,所以埠是80,這個無所謂。

  • 產品服務部署好了,下麵部署一下訂單服務,也是同樣的流程,就把指令簡單貼一下吧:

build鏡像:docker build -t orderapi -f ./Order.API/Dockerfile .
運行容器:docker run -d -p 9060:80 --name orderservice orderapi
瀏覽器訪問一下:

OK,訂單服務也部署完成了。

客戶端調用

客戶端我這裡只做了一個web客戶端,實際可能是各種業務系統、什麼PC端、手機端、小程式。。。這個明白就好,為了簡單就不搞那麼多了。

  • 因為客戶端需要http請求服務端介面,所以需要一個http請求客戶端,我個人比較習慣RestSharp,安利一波:https://github.com/restsharp/RestSharp

  • 添加基礎代碼:

IServiceHelper.cs:

    public interface IServiceHelper
    {
        /// <summary>
        /// 獲取產品數據
        /// </summary>
        /// <returns></returns>
        Task<string> GetProduct();

        /// <summary>
        /// 獲取訂單數據
        /// </summary>
        /// <returns></returns>
        Task<string> GetOrder();
    }

ServiceHelper.cs:

    public class ServiceHelper : IServiceHelper
    {
        public async Task<string> GetOrder()
        {
            string serviceUrl = "http://localhost:9060";//訂單服務的地址,可以放在配置文件或者資料庫等等...

            var Client = new RestClient(serviceUrl);
            var request = new RestRequest("/orders", Method.GET);

            var response = await Client.ExecuteAsync(request);
            return response.Content;
        }

        public async Task<string> GetProduct()
        {
            string serviceUrl = "http://localhost:9050";//產品服務的地址,可以放在配置文件或者資料庫等等...

            var Client = new RestClient(serviceUrl);
            var request = new RestRequest("/products", Method.GET);

            var response = await Client.ExecuteAsync(request);
            return response.Content;
        }
    }

Startup.cs:

    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();
            
            //註入IServiceHelper
            services.AddSingleton<IServiceHelper, ServiceHelper>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }

HomeController.cs:

    public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;
        private readonly IServiceHelper _serviceHelper;

        public HomeController(ILogger<HomeController> logger, IServiceHelper serviceHelper)
        {
            _logger = logger;
            _serviceHelper = serviceHelper;
        }

        public async Task<IActionResult> Index()
        {
            ViewBag.OrderData = await _serviceHelper.GetOrder();
            ViewBag.ProductData = await _serviceHelper.GetProduct();

            return View();
        }

        public IActionResult Privacy()
        {
            return View();
        }

        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult Error()
        {
            return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
        }
    }

Index.cshtml:

@{
    ViewData["Title"] = "Home Page";
}

<div class="text-center">
    <h1 class="display-4">Welcome</h1>
    <p>
        @ViewBag.OrderData
    </p>
    <p>
        @ViewBag.ProductData
    </p>
</div>

代碼比較簡單,這裡就不用docker了,直接控制台啟動,使用瀏覽器訪問:

  • 一切正常。進行到這裡,各個服務也獨立運行了,客戶端也能正常調用了,貌似算是完成一個簡易的微服務了。但是,微服務架構最重要的原則就是——“高可用”。以上的做法明顯不能滿足高可用性,因為任何一個服務掛掉,所有依賴這個服務的業務系統都會受影響。

停止一下訂單服務:docker stop orderservice


訂單服務停止,導致客戶端業務系統無法獲取訂單數據。
要解決這個問題,很容易想到:集群。

簡單的服務集群

既然單個服務實例有掛掉的風險,那麼部署多個服務實例就好了嘛,只要大家不同時全掛就行。

  • 使用docker運行多個服務實例:
    docker run -d -p 9061:80 --name orderservice1 orderapi
    docker run -d -p 9062:80 --name orderservice2 orderapi
    docker run -d -p 9051:80 --name productservice1 productapi
    docker run -d -p 9052:80 --name productservice2 productapi

現在訂單服務和產品服務都增加到3個服務實例。

  • 那麼稍微改造一下客戶端代碼吧:
    ServiceHelper.cs:
public class ServiceHelper : IServiceHelper
    {
        public async Task<string> GetOrder()
        {
            string[] serviceUrls = { "http://localhost:9060", "http://localhost:9061", "http://localhost:9062" };//訂單服務的地址,可以放在配置文件或者資料庫等等...

            //每次隨機訪問一個服務實例
            var Client = new RestClient(serviceUrls[new Random().Next(0, 3)]);
            var request = new RestRequest("/orders", Method.GET);

            var response = await Client.ExecuteAsync(request);
            return response.Content;
        }

        public async Task<string> GetProduct()
        {
            string[] serviceUrls = { "http://localhost:9050", "http://localhost:9051", "http://localhost:9052" };//產品服務的地址,可以放在配置文件或者資料庫等等...

            //每次隨機訪問一個服務實例
            var Client = new RestClient(serviceUrls[new Random().Next(0, 3)]);
            var request = new RestRequest("/products", Method.GET);

            var response = await Client.ExecuteAsync(request);
            return response.Content;
        }
    }

當然拿到這些服務地址可以自己做複雜的負載均衡策略,比如輪詢,隨機,權重等等 都行,甚至在中間弄個nginx也可以。這些不是重點,所以就簡單做一個隨機吧,每次請求來了隨便訪問一個服務實例。

  • 瀏覽器測試一下:

    可以看到請求被隨機分配了。但是這種做法依然不安全,如果隨機訪問到的實例剛好掛掉,那麼業務系統依然會出問題。
    簡單處理思路是:
    1.如果某個地址請求失敗了,那麼換一個地址接著執行。
    2.如果某個地址的請求連續多次失敗了,那麼就移除這個地址,下次就不會訪問到它了。
    。。。。。。
    業務系統實現以上邏輯,基本上風險就很低了,也算是大大增加了系統可用性了。

  • 然後思考另一個問題:

實際應用中,上層的業務系統可能非常多,為了保證可用性,每個業務系統都去考慮服務實例掛沒掛掉嗎?
而且實際應用中服務實例的數量或者地址大多是不固定的,例如雙十一來了,流量大了,增加了一堆服務實例,這時候每個業務系統再去配置文件里配置一下這些地址嗎?雙十一過了又去把配置刪掉嗎?顯然是不現實的,服務必須要做到可靈活伸縮。

  • 這時候就引入一個名詞:服務註冊與發現

未完待續...


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

-Advertisement-
Play Games
更多相關文章
  • Asp.Net WebApi 上傳文件方法 實現功能: 1.原生js調用api上傳 2.jq ajax調用api上傳 後端 Model /// <summary> /// 上傳文件(如果遇到不明白的或者發現BUG請加入QQ群:Java .Net Go PHP UI群:574879752 直接@群主) ...
  • @ 簡介 什麼是Dikeko.ORM? Dikeko.ORM是一個簡單的.NET輕量級的ORM,目前僅支持SqlServer資料庫。 安裝 .NET版:https://www.nuget.org/packages/Dikeko.ORM PM>Install-Package Dikeko.ORM .N ...
  • Blazor WebAssembly可以在瀏覽器上跑C#代碼,但是很多時候顯然還是需要跟JavaScript打交道。比如操作dom,當然跟angular、vue一樣不提倡直接操作dom;比如瀏覽器的後退導航。反之JavaScript也有可能需要調用C#代碼來實現一些功能,畢竟客戶的需求是千變萬化的, ...
  • ps:本文需要先把abp的源碼下載一份來下,跟著一起找實現,更容易懂 在abp中,對於許可權和菜單使用靜態來管理,菜單的載入是在登陸頁面的地方(具體是怎麼知道的,瀏覽器按F12,然後去sources中去找) 這個/AbpScripts/GetScripts是獲取需要初始化的script,源自AbpSc ...
  • 本文基於 AutoMapper 9.0.0 AutoMapper 是一個對象-對象映射器,可以將一個對象映射到另一個對象。 官網地址:http://automapper.org/ 官方文檔:https://docs.automapper.org/en/latest/ 1 入門例子 public cl ...
  • 系列文章 基於 abp vNext 和 .NET Core 開發博客項目 - 使用 abp cli 搭建項目 基於 abp vNext 和 .NET Core 開發博客項目 - 給項目瘦身,讓它跑起來 基於 abp vNext 和 .NET Core 開發博客項目 - 完善與美化,Swagger登場 ...
  • 前言 在本章中,主要是藉機這個C#基礎篇的系列整理過去的學習筆記、歸納總結並更加理解透徹。 在上一篇文章,我們已經對委托有了進一步瞭解,委托相當於用方法作為另一方法參數,同時,也可以實現在兩個不能直接調用的方法中做橋梁。 下麵我們來回顧一下委托的例子。 public delegate void Ex ...
  • 前言 從題目我們可以看的出,今天是五部曲的第三部,你可能會好奇,為啥沒有見到前兩部呢?這裡我簡單說下: 1、跨平臺第一部曲:MySql 如果你看我的所有開源項目,應該能發現我已經全部遷移到了Mysql資料庫了,這裡不是說SqlServer就不是不跨平臺了,只是MySql用著可能更好些,當然,如果你是 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...