Redola.Rpc 集成 Consul 服務發現


Redola.Rpc 解決了什么問題?

Redola.Rpc 是一個使用 C# 開發的 RPC 框架,代碼開源在 GitHub 上。目前版本僅支持 .NET Framework 4.6 以上版本,未來待系統穩健后再考慮移植 .NET Standard.NET Core

Redola.Rpc 在 0.3.2 版本中,嘗試解決幾個 RPC 設計問題:

  • 我是誰?(Local Actor)
  • 如何告訴別人我是誰?(Actor Directory)
  • 我提供什么服務?(Service Catalog Provider)
  • 如何告訴別人我提供什么服務?(Service Directory)
  • 我需要的服務在哪里?(Service Discovery)
  • 如何調用該服務?(Service Dynamic Proxy)
  • 如何找到該服務?(Actor Directory)
  • 如何發消息給該服務?(Remote Actor)

Actor 是什么?

Redola 定義的 Actor 模型代表着一個通信節點,使用 ActorIdentity 描述,包括節點類型 Type、節點名稱 Name、節點地址 Address、節點端口 Port。

Actor 與 Actor 之間是基於 TCP Socket 通信的,Actor 並不區分 TCP 的 Server/Client 端,它將 Server 和 Client 封裝在底層,為上層應用提供更便捷的傳輸定義和調用接口。Actor 模型提供了面向通道 Channel 的雙工通道,可以接收來自對端的消息,也可以發送消息給對端。

Actor 收發的消息是面向二進制數組的,它不關心具體發送的是什么消息,也不關心序列化格式。Actor 使用 ActorFrameHeader 定義傳輸消息頭,Header 攜帶消息體長度。

Actor 一旦建立連接,生成的 Channel 通道會自動進行 KeepAlive 雙向保活機制。通過 Actor 服務發現,可以與任意的 Actor 進行通信,無需再配置對端節點地址和端口。並且,針對相同 Type 的 Actor,還可以實現消息分發的負載均衡功能。

RPC 契約定義

Redola.Rpc 是基於契約模型通信的,使用 Protobuf 2 格式定義 IDL,並通過自動生成工具生成 Contract 契約定義。

例如,下面是定義 ICalcService 服務的 IDL 定義。

package Redola.Rpc.TestContracts;

message AddRequest
{
    required int32 X = 10;
    required int32 Y = 20;
}

message AddResponse
{
    required int32 Result = 10;
}

service CalcService
{
    rpc Add (AddRequest) returns (AddResponse);
}

上述 IDL 生成的 ICalcService 接口定義為:

public interface ICalcService
{
  AddResponse Add(AddRequest request);
}

RPC 消息序列化

Redola.Rpc 選擇使用 Protobuf 2 進行消息序列化,默認集成 protobuf-net 類庫,穩定使用 protobuf-net v2.0.0.668 版本。

RPC 消息信封

使用 ActorMessageEnvelope 封裝消息信封,攜帶如下信息:

  屬性名稱

 屬性類型 

 屬性描述 

 MessageID

string

 消息 ID,唯一 ID,通常使用 GUID。

 MessageTime

DateTime

 消息產生時間

 CorrelationID

string

 如果是 Response 則回填 Request 的 MessageID。 

 CorrelationTime 

DateTime 

 如果是 Response 則回填 Request 的 MessageTime。

 SourceEndpoint 

 ActorEndpoint   發送端節點描述,消息路由使用,默認不需要填寫。

 TargetEndpoint

ActorEndpoint  目的端節點描述,消息路由使用,默認不需要填寫。 

 MessageType

string  消息類型,使用字符串描述。

 MessageData

byte[]  消息體,消息序列化后的二進制數組。

RPC 消息定義

RPC 消息分為 2 類:

  1. InvokeMethodRequest / InvokeMethodResponse 用於定義請求回復模型的方法調用;
  2. InvokeMethodMessage 用於定義請求無回復模型的方法調用;

通常 RPC 消息會包含如下屬性信息:

  屬性名稱

 屬性類型 

 屬性描述 

 MethodLocator

string

 RPC 方法描述,使用字符串描述。

 MethodArguments 

object[]

 RPC 方法的入參,object 對象數組。

例如,對於 ICalcService 中的 Add 方法:

  • MethodLocator = "Rodola.Rpc.TestContracts.ICalcService/Add_AddRequest";
  • MethodArguments = new object[] { new AddRequest(1, 2)};

鑒於 protobuf 本身是面向契約設計的,而 object[] 中的 object 是有不確定性的,並不能具體描述一個契約,則要求每一個 Argument 都需要支持 protobuf 的序列化,傳輸時系統會攜帶該 Argument 類型的 AssemblyQualifiedName,在對端通過反射進行反序列化。

Actor Directory 節點目錄

Actor Directory 負責注冊本地 Local Actor 到注冊中心,Local Actor 也可以在 Shutdown 時將自己從注冊中心移除掉。

通過 Actor Directory,Local Actor 可以使用 Type 和 Name 進行 Remote Actor 的檢索,進而進行 Channel 的建立和通信。

Actor Directory 通過 IActorDirectory 的抽象定義,可以與不同的目錄方案進行集成。例如,自實現基於 Actor 的 CenterActorDirectory,使用 XML 配置文件的 LocalXmlFileActorDirectory,使用 Consul 進行中心注冊的 ConsulActorDirectory。

使用 Consul 時,實際上是調用了 Consul HTTP API 中的 Agent Register Service 接口 '/v1/agent/service/register',通過指定 ServiceID 和 ServiceName 進行注冊。

通過如下 cmd 啟動 Consul Server 和 Consul Agent。

consul.exe agent -config-dir "C:\Consul\config\server-01" -bootstrap -ui
consul.exe agent -config-dir "C:\Consul\config\client-01" -join 192.168.1.133:7774 -ui

下面為啟動本地 Consul 進行測試的配置文件。

server-01.json

{
  "datacenter": "dc1",
  "data_dir": "C:\\Consul\\data\\server-01",
  "log_level": "INFO",
  "node_name": "server-01",
  "server": true,
  "ports": {
    "http": 7771,
    "rpc": 7772,
    "dns": 7773,
    "serf_lan": 7774,
    "serf_wan": 7775,
    "server": 7776
  }
}

client-01.json

{
  "datacenter": "dc1",
  "data_dir": "C:\\Consul\\data\\client-01",
  "log_level": "INFO",
  "node_name": "client-01",
  "ports": {
    "http": 8881,
    "rpc": 8882,
    "dns": 8883,
    "serf_lan": 8884,
    "serf_wan": 8885,
    "server": 8886
  }
}

Service Catalog Provider 服務提供者

作為 RPC Service 的 Provider 提供方,需要顯式定義指定 Contract 的服務實例。例如,下面將不同的服務契約與服務實例進行了注冊。

var serviceCatalog = new ServiceCatalogProvider();
serviceCatalog.RegisterService<IHelloService>(new HelloService());
serviceCatalog.RegisterService<ICalcService>(new CalcService());
serviceCatalog.RegisterService<IOrderService>(new OrderService());

實際上,可以通過對於 IServiceCatalogProvider 接口的不同實現,進行不同方式的本地服務發現和注冊。例如,可以使用 Attribute 標記服務,通過對 Assembly 進行反射進行服務的實例化。

Service Directory 服務目錄

本地服務聚集到 Catalog 中后,系統會將服務逐個注冊到 Service Directory 服務目錄中,使得其他節點可以檢索服務進行使用。

通過 IServiceDirectory 的抽象定義,可以與不同的目錄方案進行集成。例如,使用 XML 配置文件的 LocalXmlFileServiceDirectory,使用 Consul 進行中心注冊的 ConsulServiceDirectory。

使用 Consul 時,注冊服務的 log 如下所示。

當 Redola 將服務注冊至 Consul 中后,可通過 Consul 內置的 UI 進行查看。

http://localhost:8881/ui/#/dc1/services

Service Discovery 服務發現

通過 ConsulServiceDiscovery 實現 IServiceDiscovery 服務發現接口,從 Consul 檢索指定服務類型的服務。

通過 Postman 測試 GET /v1/catalog/services,得到如下 JSON 數據。

http://localhost:8881/v1/catalog/services
{
    "Redola.Rpc.TestContracts.ICalcService": [],
    "Redola.Rpc.TestContracts.IHelloService": [],
    "Redola.Rpc.TestContracts.IOrderService": [],
    "consul": [],
    "server": []
}

通過 Postman 測試 GET /v1/catalog/service,得到如下 JSON 數據。

http://localhost:8881/v1/catalog/service/Redola.Rpc.TestContracts.ICalcService
[
    {
        "ID": "359e8dfe-262d-6eb7-260c-e6e3ad208a14",
        "Node": "client-01",
        "Address": "192.168.1.133",
        "Datacenter": "dc1",
        "TaggedAddresses": {
            "lan": "192.168.1.133",
            "wan": "192.168.1.133"
        },
        "NodeMeta": {},
        "ServiceID": "redola/server/server-33333/Redola.Rpc.TestContracts.ICalcService",
        "ServiceName": "Redola.Rpc.TestContracts.ICalcService",
        "ServiceTags": [],
        "ServiceAddress": "localhost",
        "ServicePort": 33333,
        "ServiceEnableTagOverride": true,
        "CreateIndex": 2147,
        "ModifyIndex": 2151
    }
]

服務檢索方,可通過指定 IServiceLoadBalancingStrategy 的具體實現實施不同的負載均衡策略,默認指定的是 IServiceLoadBalancingStrategy 隨機選擇。

Service Dynamic Proxy 動態代理

為簡化 RPC 調用發起方的封裝,通常會使用 Dynamic Proxy 動態代理技術來動態生成給定契約的服務實例,將整體 RPC 的過程透明化。

例如,通過下面的代碼來動態生成 ICalcService 的動態代理。

var calcClient = rpcNode.Resolve<ICalcService>();

目前 Redola.Rpc 默認集成了 Castle.Core 中的 Dynamic Proxy 模塊,通過對實例方法的 Intercept 攔截進行 RPC 消息的收發處理。

當然,如需集成其他 Dynamic Proxy 類庫,可通過 ISeviceProxyGenerator 接口進行方案實現。

Redola.Rpc 類庫依賴

Redola.Rpc 當前實現依賴了如下開源類庫。

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Consul" version="0.7.2.3" targetFramework="net46" />
  <package id="Cowboy.Sockets" version="1.3.14.0" targetFramework="net46" />
  <package id="protobuf-net" version="2.0.0.668" targetFramework="net46" />
  <package id="Castle.Core" version="4.1.0" targetFramework="net46" />
  <package id="Logrila.Logging" version="1.0.3.0" targetFramework="net46" />
  <package id="Logrila.Logging.NLogIntegration" version="1.0.3.0" targetFramework="net46" />
  <package id="NLog" version="4.2.3" targetFramework="net46" />
</packages>

 

版權聲明:本篇文章《Redola 集成 Consul 服務發現》由作者 Dennis Gao 發表自博客園個人技術博客,未經作者本人同意禁止以任何的形式轉載,任何自動的或人為的爬蟲轉載行為均為耍流氓。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM