Redola.Rpc 集成 Consul 服務(wù)發(fā)現(xiàn)
Redola.Rpc 解決了什么問題?
Redola.Rpc 是一個(gè)使用 C# 開發(fā)的 RPC 框架,代碼開源在?GitHub?上。目前版本僅支持?.NET Framework 4.6?以上版本,未來待系統(tǒng)穩(wěn)健后再考慮移植?.NET Standard?和?.NET Core。
Redola.Rpc?在 0.3.2 版本中,嘗試解決幾個(gè) RPC 設(shè)計(jì)問題:
- 我是誰?(Local Actor)
- 如何告訴別人我是誰?(Actor Directory)
- 我提供什么服務(wù)?(Service Catalog Provider)
- 如何告訴別人我提供什么服務(wù)?(Service Directory)
- 我需要的服務(wù)在哪里?(Service Discovery)
- 如何調(diào)用該服務(wù)?(Service Dynamic Proxy)
- 如何找到該服務(wù)?(Actor Directory)
- 如何發(fā)消息給該服務(wù)?(Remote Actor)
Actor 是什么?
Redola 定義的 Actor 模型代表著一個(gè)通信節(jié)點(diǎn),使用 ActorIdentity 描述,包括節(jié)點(diǎn)類型 Type、節(jié)點(diǎn)名稱 Name、節(jié)點(diǎn)地址 Address、節(jié)點(diǎn)端口 Port。
Actor 與 Actor 之間是基于 TCP Socket 通信的,Actor 并不區(qū)分 TCP 的 Server/Client 端,它將 Server 和 Client 封裝在底層,為上層應(yīng)用提供更便捷的傳輸定義和調(diào)用接口。Actor 模型提供了面向通道 Channel 的雙工通道,可以接收來自對(duì)端的消息,也可以發(fā)送消息給對(duì)端。
Actor 收發(fā)的消息是面向二進(jìn)制數(shù)組的,它不關(guān)心具體發(fā)送的是什么消息,也不關(guān)心序列化格式。Actor 使用 ActorFrameHeader 定義傳輸消息頭,Header 攜帶消息體長(zhǎng)度。
Actor 一旦建立連接,生成的?Channel 通道會(huì)自動(dòng)進(jìn)行 KeepAlive 雙向保活機(jī)制。通過 Actor 服務(wù)發(fā)現(xiàn),可以與任意的 Actor 進(jìn)行通信,無需再配置對(duì)端節(jié)點(diǎn)地址和端口。并且,針對(duì)相同 Type 的 Actor,還可以實(shí)現(xiàn)消息分發(fā)的負(fù)載均衡功能。
RPC 契約定義
Redola.Rpc?是基于契約模型通信的,使用?Protobuf 2?格式定義 IDL,并通過自動(dòng)生成工具生成 Contract 契約定義。
例如,下面是定義 ICalcService 服務(wù)的 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 進(jìn)行消息序列化,默認(rèn)集成?protobuf-net?類庫,穩(wěn)定使用 protobuf-net?v2.0.0.668?版本。
RPC 消息信封
使用 ActorMessageEnvelope 封裝消息信封,攜帶如下信息:
| ? 屬性名稱 | ?屬性類型? | ?屬性描述? |
?MessageID | string | ?消息 ID,唯一 ID,通常使用 GUID。 |
?MessageTime | DateTime | ?消息產(chǎn)生時(shí)間 |
?CorrelationID | string | ?如果是 Response 則回填 Request 的 MessageID。? |
?CorrelationTime? | DateTime? | ?如果是 Response 則回填 Request 的 MessageTime。 |
?SourceEndpoint? | ?ActorEndpoint? | ?發(fā)送端節(jié)點(diǎn)描述,消息路由使用,默認(rèn)不需要填寫。 |
?TargetEndpoint | ActorEndpoint | ?目的端節(jié)點(diǎn)描述,消息路由使用,默認(rèn)不需要填寫。? |
?MessageType | string | ?消息類型,使用字符串描述。 |
?MessageData | byte[] | ?消息體,消息序列化后的二進(jìn)制數(shù)組。 |
RPC 消息定義
RPC 消息分為 2 類:
- InvokeMethodRequest / InvokeMethodResponse?用于定義請(qǐng)求回復(fù)模型的方法調(diào)用;
- InvokeMethodMessage?用于定義請(qǐng)求無回復(fù)模型的方法調(diào)用;
通常 RPC 消息會(huì)包含如下屬性信息:
| ? 屬性名稱 | ?屬性類型? | ?屬性描述? |
?MethodLocator | string | ?RPC 方法描述,使用字符串描述。 |
?MethodArguments? | object[] | ?RPC 方法的入?yún)ⅲ琽bject 對(duì)象數(shù)組。 |
例如,對(duì)于 ICalcService 中的 Add 方法:
- MethodLocator = "Rodola.Rpc.TestContracts.ICalcService/Add_AddRequest";
- MethodArguments = new object[] { new AddRequest(1, 2)};
鑒于 protobuf 本身是面向契約設(shè)計(jì)的,而 object[] 中的 object 是有不確定性的,并不能具體描述一個(gè)契約,則要求每一個(gè) Argument 都需要支持 protobuf 的序列化,傳輸時(shí)系統(tǒng)會(huì)攜帶該 Argument 類型的?AssemblyQualifiedName,在對(duì)端通過反射進(jìn)行反序列化。
Actor Directory 節(jié)點(diǎn)目錄
Actor Directory 負(fù)責(zé)注冊(cè)本地 Local Actor 到注冊(cè)中心,Local Actor 也可以在 Shutdown 時(shí)將自己從注冊(cè)中心移除掉。
通過 Actor Directory,Local Actor 可以使用?Type 和 Name 進(jìn)行 Remote Actor 的檢索,進(jìn)而進(jìn)行 Channel 的建立和通信。
Actor Directory 通過 IActorDirectory 的抽象定義,可以與不同的目錄方案進(jìn)行集成。例如,自實(shí)現(xiàn)基于 Actor 的 CenterActorDirectory,使用 XML 配置文件的 LocalXmlFileActorDirectory,使用 Consul 進(jìn)行中心注冊(cè)的 ConsulActorDirectory。
使用 Consul 時(shí),實(shí)際上是調(diào)用了?Consul HTTP API?中的 Agent Register Service 接口 '/v1/agent/service/register',通過指定 ServiceID 和 ServiceName 進(jìn)行注冊(cè)。
通過如下 cmd 啟動(dòng) 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
下面為啟動(dòng)本地 Consul 進(jìn)行測(cè)試的配置文件。
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 服務(wù)提供者
作為 RPC Service 的 Provider 提供方,需要顯式定義指定 Contract 的服務(wù)實(shí)例。例如,下面將不同的服務(wù)契約與服務(wù)實(shí)例進(jìn)行了注冊(cè)。
var serviceCatalog = new ServiceCatalogProvider(); serviceCatalog.RegisterService(new HelloService()); serviceCatalog.RegisterService (new CalcService()); serviceCatalog.RegisterService (new OrderService());
實(shí)際上,可以通過對(duì)于 IServiceCatalogProvider 接口的不同實(shí)現(xiàn),進(jìn)行不同方式的本地服務(wù)發(fā)現(xiàn)和注冊(cè)。例如,可以使用?Attribute 標(biāo)記服務(wù),通過對(duì) Assembly 進(jìn)行反射進(jìn)行服務(wù)的實(shí)例化。
Service Directory 服務(wù)目錄
本地服務(wù)聚集到 Catalog 中后,系統(tǒng)會(huì)將服務(wù)逐個(gè)注冊(cè)到 Service Directory 服務(wù)目錄中,使得其他節(jié)點(diǎn)可以檢索服務(wù)進(jìn)行使用。
通過 IServiceDirectory 的抽象定義,可以與不同的目錄方案進(jìn)行集成。例如,使用 XML 配置文件的 LocalXmlFileServiceDirectory,使用 Consul 進(jìn)行中心注冊(cè)的 ConsulServiceDirectory。
使用 Consul 時(shí),注冊(cè)服務(wù)的 log 如下所示。

當(dāng) Redola 將服務(wù)注冊(cè)至 Consul 中后,可通過 Consul 內(nèi)置的 UI 進(jìn)行查看。
http://localhost:8881/ui/#/dc1/services

Service Discovery 服務(wù)發(fā)現(xiàn)
通過 ConsulServiceDiscovery 實(shí)現(xiàn) IServiceDiscovery 服務(wù)發(fā)現(xiàn)接口,從?Consul?檢索指定服務(wù)類型的服務(wù)。
通過 Postman 測(cè)試 GET?/v1/catalog/services,得到如下 JSON 數(shù)據(jù)。
http://localhost:8881/v1/catalog/services
{ "Redola.Rpc.TestContracts.ICalcService": [], "Redola.Rpc.TestContracts.IHelloService": [], "Redola.Rpc.TestContracts.IOrderService": [], "consul": [], "server": [] }
通過 Postman 測(cè)試 GET?/v1/catalog/service,得到如下 JSON 數(shù)據(jù)。
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 } ]
服務(wù)檢索方,可通過指定?IServiceLoadBalancingStrategy 的具體實(shí)現(xiàn)實(shí)施不同的負(fù)載均衡策略,默認(rèn)指定的是?IServiceLoadBalancingStrategy 隨機(jī)選擇。
Service Dynamic Proxy 動(dòng)態(tài)代理
為簡(jiǎn)化?RPC 調(diào)用發(fā)起方的封裝,通常會(huì)使用 Dynamic Proxy 動(dòng)態(tài)代理技術(shù)來動(dòng)態(tài)生成給定契約的服務(wù)實(shí)例,將整體 RPC 的過程透明化。
例如,通過下面的代碼來動(dòng)態(tài)生成 ICalcService 的動(dòng)態(tài)代理。
var calcClient = rpcNode.Resolve();
目前 Redola.Rpc 默認(rèn)集成了?Castle.Core?中的?Dynamic Proxy?模塊,通過對(duì)實(shí)例方法的 Intercept 攔截進(jìn)行 RPC 消息的收發(fā)處理。
當(dāng)然,如需集成其他 Dynamic Proxy 類庫,可通過 ISeviceProxyGenerator 接口進(jìn)行方案實(shí)現(xiàn)。
Redola.Rpc 類庫依賴
Redola.Rpc 當(dāng)前實(shí)現(xiàn)依賴了如下開源類庫。
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>作者:匠心十年
原文鏈接:https://www.cnblogs.com/gaochundong/p/redola_rpc_consul_integration.html
本文轉(zhuǎn)自博客園網(wǎng),版權(quán)歸原作者所有。
