1. 程式人生 > >NET Core微服務之路:自己動手實現Rpc服務框架,基於DotEasy.Rpc服務框架的介紹和整合

NET Core微服務之路:自己動手實現Rpc服務框架,基於DotEasy.Rpc服務框架的介紹和整合

本篇內容屬於非實用性(拿來即用)介紹,如對框架設計沒興趣的朋友,請略過。

 

快一個月沒有寫博文了,最近忙著兩件事;    一:閱讀劉墉先生的《說話的魅力》,以一種微妙的,你我大家都會經常遇見的事物,來建議說話的“藝術和魅力”,對於我們從事軟體開發、不太善於溝通和表達的朋友來說,也算是一項軟技能了,推薦喜歡閱讀的朋友有時間閱讀,給你不一樣的閱讀體驗。 二:編寫基於Net Core的Rpc框架。之前有朋友說如何將Rpc等整個體系整合到dotnet框架中,我想這篇博文會給你一個答案。 哦,對了,我不建議直接將程式碼直接複製下來然後去執行的朋友,因為這樣你達不到學習的目的,也違背了筆者的初衷。謝謝理解。

 

一:簡單回顧一下之前的介紹

繼續貼上之前的一張圖片

 

 

根據上面圖,服務化原理可以分為3步:
  1. 服務端啟動並且向註冊中心傳送服務資訊,註冊中心收到後會定時監控服務狀態(常見心跳檢測);
  2. 客戶端需要開始呼叫服務的時候,首先去註冊中心獲取服務資訊;
  3. 客戶端建立遠端呼叫連線,連線後服務端返回處理資訊;
    第3步又可以細分,下面說說遠端過程呼叫的原理: 目標:客戶端怎麼呼叫遠端機器上的公開方法
  1. 服務發現,向註冊中心獲取服務(這裡需要做的有很多:拿到多個服務時需要做負載均衡,同機房過濾、版本過濾、服務路由過濾、統一閘道器等);
  2. 客戶端發起呼叫,將需要呼叫的服務、方法、引數進行組裝;
  3. 序列化編碼組裝的訊息,這裡可以使用json,也可以使用xml,也可以使用protobuf,也可以使用hessian,幾種方案的序列化速度還有序列化後佔用位元組大小都是選擇的重要指標,對內筆者建議使用高效的protobuf,它基於TCP/IP二進位制進行序列化,體積小,速度快。
  4. 傳輸協議,可以使用傳統的IO阻塞傳輸,也可以使用高效的nio傳輸(Netty);
  5. 服務端收到後進行反序列化,然後進行相應的處理;
  6. 服務端序列化response資訊並且返回;
  7. 客戶端收到response資訊並且反序列化;
    至於C類和S類之間的通訊方式,是採用RPC還是採用RESTful,讀者可以參考之前的介紹,根據實際業務進行決定 https://www.cnblogs.com/SteveLee/p/service_discovery_and_service_governance.html  

二:DotEasy.Rpc框架介紹

  單論Rpc框架市場,且不論Java上的Spring Boot和Spring Cloud這樣大名鼎鼎的開源框架,目前Net上的Rpc整合性框架確實並不多,我們Net程式設計師也要混口飯吃,不能總被Java甩掉好幾條街吧。   言歸正傳,一個遠端過程呼叫,會涉及到如下幾個方面的技術點(功能):
  1. 路由轉發:當服務部署在多個節點上時,呼叫方需要知道自己的目標服務在什麼地方。
  2. 通訊協議:當管道存在,還需要在管道的兩端建立處理程式(宿主),以處理管道中的資料包。DotEasy.Rpc基於DotNetty進行通訊處理和協議實現。
  3. 動態生成:我們知道,基於二進位制的RPC傳輸,每當新增介面,或修改介面,都需要生成相關協議的protobuf檔案(或 thrift 檔案),本框架基於protobuf-net的傳輸框架和Rosyln的預生成,動態生成相關的CS檔案。
  4. 執行時代理:本框架採用一次請求建立一個客戶端的模式,進行S端的服務請求,本框架基於Rosyln執行時建立客戶端代理。
  5. 介面掃描:需要實現多個介面的新增和註冊,本框架採用Attribute特性來掃描介面,並新增到相關的註冊中心,比如consul或etcd。

 

2.1 解決方案介紹:

  整體解決方案不做過多介紹,相信單詞的詞義已經表達了這個專案的作用。   小插曲:筆者曾用Easy.Rpc做為專案的主庫名稱,但上傳到nuget後才發現,原來也有名為easy.rpc的包,不過筆者並沒找到這個開源作者的網站,無賴之下,想到“”這個詞,也是dotnet的dot開頭部分,索性乾脆也叫DotEasy了吧。   筆者的目的,是想通過這個框架(或類庫集),來簡化微服務的部署和開發,讓希望從事微服務NET開發的朋友不再找不到從何入手的窘境。 在Nuget.org上能直接下載編譯過的包,地址:https://www.nuget.org/packages/DotEasy.Rpc/,推薦使用這種方式進行安裝。   如果喜歡研究原始碼,筆者同樣也貼上地址:https://github.com/steveleeCN87/doteasy.rpc,不過原始碼不包含任何依賴,如果編譯中出現版本問題,可聯絡筆者。目前Github和Nuget上就放上了DotEasy.Rpc核心庫和DotEasy.Rpc.Consul擴充套件包,etcd和entry還在測試階段,就不方便拿出來獻醜了。o(∩_∩)o 哈哈   當然,也歡迎廣大愛好開源的朋友加入,共同為NET開源專案做貢獻。

 

2.2 主專案介紹:

Attributes用於標註該介面為某一特性的方法體,當系統通過Microsoft.Extensions.DependencyInjection自動構建成功後,框架將自動掃描介面上標註過[RpcTagBundle]的介面,形成目標介面列表,以供下面的方法呼叫。

Core:該核心分為Server和Client以及核心通用三個部分組成:

  Client:主要用於通過Ip地址遠端呼叫的方法Invoke實現,以及遠端服務端的檢查檢查實現。

  Communally:通用方法庫,包括唯一ID生成器(用於標識每次請求所產生的唯一客戶端)、通用物件轉換器(將複雜物件轉換為喜歡預設物件)、異常處理器、序列化生成器。

  Server:提供服務宿主,服務執行者、服務入口處理、服務管理、服務定位、服務管理工廠的介面及預設實現。

Proxy:執行時預生成及客戶端動態代理模組的介面和方法實現。

Routing:提供地址定位、服務描述、服務路由的介面和方法實現。

Transport:基於protobuf-net和dotnettey構建的二進位制序列化、和通訊管道和宿主的介面和實現。

所有主要類均已介面的形式提供介面名稱,和已預設實現的具體方法體(虛方法),方便在這個基礎上進行擴充套件和重寫。

 

2.3 主專案介面

本節簡單介紹一下DotEasy.Rpc主框架的介面的作用。

Attribute:
|---RpcTagBundleAttribute.cs
Core:
|---Client:
|-------Address:
|-----------IAddressResolver.cs
|-------IRemoteInvokeService.cs
|---Server:
|-------IServiceEntryFactory.cs
|-------IServiceEntryLocate.cs
|-------IServiceEntryManager.cs
|-------IServiceEntryProvider.cs
|-------IServiceExecutor.cs
|-------IServiceHost.cs
Proxy:
|---IServiceProxyFactory.cs
|---IServiceProxyGenerater.cs
Routing:
|---IServiceRouteFactory.cs
|---IServiceRouteManager.cs
Transport:
|-------Codec:
|-----------ITransportMessageCodecFactory.cs
|-----------ITransportMessageDecoder.cs
|-----------ITransportMessageEncoder.cs
|---IMessageListener.cs
|---IMessageSender.cs
RpcTagBundleAttribute.cs:所有標記過[RpcTagBundle]特性的介面均會被掃描至doteasy.rpc框架中; IAddressResolver.cs:地址解析器,提供IPv4地址解析作用,用於IServiceRouteFactory和IRemoteInvokeService定位操作; IRemoteInvokeService.cs:遠端呼叫服務介面,提供遠端服務呼叫的關鍵介面,通過IServiceProxyFactory介面代理呼叫; IServiceEntryFactory.cs:服務入口工廠介面,對全域性服務入口的統一的工廠操作,例如新增,監聽,移除,修改服務入口等; IServiceEntryLocate.cs:服務入口定位介面,通過IAddressResolver過濾和解析,實現服務入口的定位; IServiceEntryManager.cs:服務入口管理全域性管理介面,功能同IServiceEntryFactory相似,但提供更多的服務管理操作,比如負載均衡(採用輪詢實現); IServiceEntryProvider.cs:服務入口提供者介面,一個簡單的服務入口提供者程式; IServiceExecutor.cs:執行服務方法介面,執行遠端服務的IRemoteInvokeService; IServiceHost.cs:服務宿主介面,DotNetty的服務宿主,類似own框架的自宿主程式,提供請求和響應操作; IServiceProxyFactory.cs:服務代理工廠介面,客戶端代理(預編譯)的所有操作實現; IServiceProxyGenerater.cs:服務代理生成介面,客戶端代理(預編譯)生成器; IServiceRouteFactory.cs:服務路由工廠介面; IServiceRouteManager.cs:服務路由全域性管理介面; ITransportMessageCodecFactory.cs:管道訊息傳輸工廠介面; ITransportMessageDecoder.cs:管道訊息解碼器介面; ITransportMessageEncoder.cs:管道訊息編碼器介面; IMessageListener.cs:管道訊息監聽介面,可實現一個訊息的接受者和處理程式; IMessageSender.cs:管道訊息傳送介面,可實現一個訊息的傳送者;     通過上面的框架和㢟就能實現客戶端到服務端的RPC通訊了嗎?當然不是,還需要服務註冊中心(例如Consul,etcd,zookeeper)來實現。上面只是提供了路由轉發,服務定位,客戶端預編譯的實現等等功能而已,而服務的註冊並沒提供,因為它不屬於基礎框架的範疇,筆者對zookeeper的笨重太反感(當然不是說它不好),而consul和etcd十分輕量級,特此又專門新增了兩個專案:DotEasy.Rpc.Consul和DotEasy.Rpc.Etcd,用於實現不同註冊中心的註冊(獲取)方法,和健康檢測機制。     當然,介紹Consul和Etcd如何實現不是本節的重點,DotEasy.Rpc這個框架的完全剖析也將在日後新開篇章中專門介紹如何去實現一個框架,想必大部分朋友關心的是這個框架能做什麼,有什麼樣的功能,那麼,接下來開始吧。

 

三:如何使用

本系列一直重複的那張圖片,噼裡啪啦噼裡啪啦......此處省略三百字。繞來繞去,難以入手,正如上一篇有朋友推薦如何在Asp.net core中整合、等等。 功能和特性如下:
  1. Apache許可證2協議開放原始碼;
  2. 統一元件裝配和構造;
  3. 基於protobuf-net實現位元組流序列化;
  4. 基於Rosyln的執行時客戶端代理生成;
  5. 基於Rosyln的預生成的客戶端代理;
  6. 基於DotNetty的傳輸通道;
  7. 支援客戶端以輪詢的方式實現負載平衡;
  8. Net Core結構及跨平臺;
就這麼點,不囉嗦,讓我們開始做DEMO。   3.1 建立服務介面和服務實現 既然是服務,那麼肯定需要以介面interface的方式實現對外暴露,並且, 介面的實現不能和介面封裝在同一個DLL中,不然還叫什麼遠端過程呼叫呢(RPC)呢,如果不瞭解什麼叫介面,去翻一翻C#語言規範。 先定義一個介面,介面方法簽名如下:
namespace doteasy.rpc.interfaces
{
    [RpcTagBundle]
    public interface IUserService
    {
        Task<string> GetUserName(int id);
        Task<bool> Exists(int id);
        Task<int> GetUserId(string userName);
        Task<DateTime> GetUserLastSignInTime(int id);
        Task<IDictionary<string, string>> GetDictionary();
        Task TryThrowException();
    }
}

 

很簡單,不解釋。 其中介面上有個重要特性叫[RpcTagBundle],該特性只允許標記在interface介面上,用於啟動時方便框架掃描所有標有該特性的介面。   在看實現類
namespace doteasy.rpc.implement
{
    public class UserService : IUserService
    {
        public Task<string> GetUserName(int id)
        {
            return Task.FromResult($"我傳了一個int數字{id}.");
        }

        public Task<bool> Exists(int id)
        {
            return Task.FromResult(true);
        }

        public Task<int> GetUserId(string userName)
        {
            return Task.FromResult(1);
        }

        public Task<DateTime> GetUserLastSignInTime(int id)
        {
            return Task.FromResult(DateTime.Now);
        }

        public Task<IDictionary<string, string>> GetDictionary()
        {
            return Task.FromResult<IDictionary<string, string>>(new Dictionary<string, string> { { "key", "value" } });
        }

        public Task TryThrowException()
        {
            throw new Exception("嘗試丟擲異常!");
        }
    }
}

 

再次重申:注意兩篇程式碼中的名稱空間,就是兩個不同的程式集(兩個專案),千萬不能以為筆者僅僅是為了區分而已。   3.2 建立asp.net core mvc應用程式 新建一個asp.net core mvc應用程式,模版預設webapi,新增一個控制器HeathController,當然,控制器名稱你也可以自由發揮,鍵入如下程式碼:
namespace doteasy.rpc.webserver.Controllers
{
    [Produces("application/json")]
    [Route("api/Health")]
    public class HealthController : Controller
    {
        [HttpGet]
        public IActionResult Get() => Ok("ok");
    }
}

 

也十分簡單,對外暴露一個路由地址為"api/Health"的API介面,提供GET方法,返回OK資訊。 這個介面的作用是用於Consul對服務的健康狀態檢查回撥地址,Consul可以基於HTTP做健康檢查,也可以通過gRPC驗證服務健康狀態。 至此,一個WebApi就建立完成,對外不在通過HTTP做任何介面暴露。 接下來我們新增一個IApplicationBuilder的擴充套件,用於啟動Rpc服務端,程式碼如下:
using System;
using doteasy.rpc.implement;
using doteasy.rpc.interfaces;
using DotEasy.Rpc.Entry;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace doteasy.rpc.webserver
{
    public static class ConsulServerExtensions
    {
        public static IApplicationBuilder UseConsulServerExtensions(this IApplicationBuilder app, IConfiguration configuration)
        {
            if (app == null) throw new ArgumentNullException(nameof(app));
            BaseServer baseServer = new BaseServer(configuration); //(1)
            baseServer.RegisterEvent += collection => collection.AddTransient<IUserService, UserService>(); //(2)
            baseServer.Start(); //(3)
            return app;
        }
    }
}
(1):例項化一個BaseServer的服務型別,並使用 IConfiguration作為引數。該BaseServer類是封裝太DotEasy.Rpc.Entry中的一個實現,主要是簡化呼叫者的程式碼構建能力,不然,截圖一個部分原始碼瞧瞧

相關推薦

NET Core服務自己動手實現Rpc服務框架基於DotEasy.Rpc服務框架介紹整合

本篇內容屬於非實用性(拿來即用)介紹,如對框架設計沒興趣的朋友,請略過。   快一個月沒有寫博文了,最近忙著兩件事;    一:閱讀劉墉先生的《說話的魅力》,以一種微妙的,你我大家都會經常遇見的事物,來建議說話的“藝術和魅力”,對於我們從事軟體開發、不太善於溝通

NET Core服務自己動手實現Rpc服務框架基於DotEasy.Rpc服務框架介紹整合...

本篇內容屬於非實用性(拿來即用)介紹,如對框架設計沒興趣的朋友,請略過。  快一個月沒有寫博文了,最近忙著兩件事;    一:閱讀劉墉先生的《說話的魅力》,以一種微妙的,你我大家都會經常遇見的事物,來建議說話的“藝術和魅力”,對於我們從事軟體開發、不太善

.NET Core服務利用DotNetty實現一個簡單的通訊過程

  上一篇我們已經全面的介紹過《基於gRPC服務發現與服務治理的方案》,我們先複習一下RPC的呼叫過程(筆者會在這一節的幾篇文章中反覆的強調這個過程呼叫方案),看下圖

.NET Core服務讓我們對上一個Demo通訊進行修改完成RPC通訊

 最近一段時間有些事情耽擱了更新,抱歉各位了。   上一篇我們簡單的介紹了DotNetty通訊框架,並簡單的介紹了基於DotNetty實現了迴路(Echo)通訊過程。   我們來回憶一下上一個專案的整個流程: 當服務端啟動後,繫結並監聽(READ)設定的埠,比如1889。

[ASP.NET MVC 小牛]05 - 使用 Ninject實現依賴註入

構造註入 ted ets pathinfo ref 最重要的 map ice prot 在[ASP.NET MVC 小牛之路]系列上一篇文章(依賴註入(DI)和Ninject)的末尾提到了在ASP.NET MVC中使用Ninject要做的兩件事情,續這篇文章之後,本文將用一

Python學習裝飾器實現

fun python run top 學習 pytho sleep light time() import time def timer(func):#timer(test1) func=test1 def deco(): start_time

Python學習裝飾器實現終極版

index type after color return 結果 python turn 調用 網站實現驗證功能裝飾器: import time user,passwd=‘alex‘,‘abc123‘ def auth(func): def wrapper(*ar

以前寫的兩本書《安全Web滲透技術及實戰案例解析(第2版)》《黑客攻防實戰加密與解密》

Web滲透技術及實戰案例解析 黑客攻防實戰加密與解密 應一些朋友的要求,我重新將書封面和購買地址發一下說明一下:www.antian365.com原來域名轉移到國外去了。現在國家對境外域名在國內訪問必須實名制,進行備份啥的,情況你懂的。最近正在制作《黑客攻防實戰加密與解密》的視頻課程,對黑客攻防過程遇

.NET Core 信公眾號小程式6種獲取UnionID方法你知道哪幾種?

前言 獲取UnionID是開發微信公眾號/小程式中很有必要的一個環節,特別是針對一個公司擁有多個公眾號小程式而推出的機制,實現打通賬戶一體化,用UnionID來區分多平臺的唯一性。 官方的解釋:如果開發者擁有多個移動應用、網站應用、和公眾帳號(包括小程式),可通過 UnionID 來區分使用者的唯一性,因

ASP.NET Core 服務初探[2]熔斷降級Polly

當我們從單體架構遷移到微服務模式時,其中一個比較大的變化就是模組(業務,服務等)間的呼叫方式。在以前,一個業務流程的執行在一個程序中就完成了,但是在微服務模式下可能會分散到2到10個,甚至更多的機器(微服務)上,這必然就要使用網路進行通訊。而網路本身就是不可靠的,並隨著每個服務都根據自身的情況進行的動態擴容,

ASP.NET Core 服務初探[1]服務發現Consul

原文: ASP.NET Core 微服務初探[1]:服務發現之Consul 在傳統單體架構中,由於應用動態性不強,不會頻繁的更新和釋出,也不會進行自動伸縮,我們通常將所有的服務地址都直接寫在專案的配置檔案中,發生變化時,手動改一下配置檔案,也不會覺得有什麼問題。但是在微服務模式下,服務會更細的拆分解耦,微服

.NET Core服務基於Consul實現服務治理

請求轉發 1.0 asp.net AC port prefix 我們 tle nan 一、Consul基礎介紹   Consul是HashiCorp公司推出的開源工具,用於實現分布式系統的服務發現與配置。與其他分布式服務註冊與發現的方案,比如 Airbnb的Smart

.NET Core服務基於Consul實現服務治理(續)

shell pla code tst 分層 編輯 set req \n 上一篇發布之後,這一篇把上一篇沒有弄到的東西補一下,也算是給各位前來詢問的朋友的一些回復吧。一、Consul服務註冊之配置文件方式1.1 重溫Consul實驗集群  這裏我們有三個Consul Serv

.net core 服務Api閘道器(Api Gateway)

微服務閘道器目錄 1、 微服務引子 2、使用Nginx作為api閘道器 3、自創api閘道器(重複輪子) 3.1、構建初始化 3.2、構建中介軟體 4、結語

Choerodon 的服務(二)服務閘道器

本文是 Choerodon 豬齒魚微服務系列文章的第二篇。在《Choerodon的微服務之路(一):如何邁出關鍵的第一步》中,我們瞭解到在微服務架構中,一個完整的單體應用被拆分成多個有著獨立部署能力的業務服務,每個服務可以使用不同的程式語言,不同的儲存介質,來保持最低限度的集中式管理。本篇將

Choerodon的服務(一)如何邁出關鍵的第一步

本文是 Choerodon 豬齒魚微服務系列文章的第一篇,在文章中將介紹當前比較流行的兩種微服務架構,即 Dubbo 和 Spring Cloud,同時將總結 Choerodon豬齒魚在選擇使用微服務架構中的一些實踐經驗,希望能夠給大家一些借鑑和啟迪。 ▌文章的主要內容包括:

Choerodon 的服務(三)服務註冊與發現

本文是 Choerodon 的微服務之路系列推文第三篇。在上一篇《Choerodon的微服務之路(二):微服務閘道器》中,介紹了Choerodon 在搭建微服務閘道器時考慮的一些問題以及兩種常見的微服務閘道器模式,並且通過程式碼介紹了Choerodon 的閘道器是如何實現的。本篇文章將介紹Choer

基於Apollo實現.NET Core服務統一配置(測試環境-單機) .NET Core服務基於Apollo實現統一配置中心

一、前言 注:此篇只是為測試環境下的快速入門。後續會給大家帶來生產環境下得實戰開發。 具體的大家可以去看官方推薦。非常的簡單明瞭。以下介紹引用官方內容: Apollo(阿波羅)是攜程框架部門研發的分散式配置中心,能夠集中化管理應用不同環境、不同叢集的配置,配置修改後能夠實時推送到應用端,並且具

.NET Core服務基於Steeltoe使用Eureka實現服務註冊與發現

一、關於Steeltoe與Spring Cloud    Steeltoe is an open source project that enables .NET developers to implement industry standard best practices when b

.NET Core服務基於Steeltoe整合Zuul實現統一API閘道器

一、關於Spring Cloud Zuul   API Gateway(API GW / API 閘道器),顧名思義,是出現在系統邊界上的一個面向API的、序列集中式的強管控服務,這裡的邊界是企業IT系統的邊界。   Zuul 是Netflix 提供的一個開源元件,致力於在雲平臺上提供動態路由,監