Redola.Rpc 集成 Consul 服务发现
Redola.Rpc 解決了什么問題?
Redola.Rpc 是一個使用 C# 開發(fā)的 RPC 框架,代碼開源在?GitHub?上。目前版本僅支持?.NET Framework 4.6?以上版本,未來待系統(tǒng)穩(wěn)健后再考慮移植?.NET Standard?和?.NET Core。
Redola.Rpc?在 0.3.2 版本中,嘗試解決幾個 RPC 設計問題:
我是誰?(Local Actor)
如何告訴別人我是誰?(Actor Directory)
我提供什么服務?(Service Catalog Provider)
如何告訴別人我提供什么服務?(Service Directory)
我需要的服務在哪里?(Service Discovery)
如何調(diào)用該服務?(Service Dynamic Proxy)
如何找到該服務?(Actor Directory)
如何發(fā)消息給該服務?(Remote Actor)
Actor 是什么?
Redola 定義的 Actor 模型代表著一個通信節(jié)點,使用 ActorIdentity 描述,包括節(jié)點類型 Type、節(jié)點名稱 Name、節(jié)點地址 Address、節(jié)點端口 Port。
Actor 與 Actor 之間是基于 TCP Socket 通信的,Actor 并不區(qū)分 TCP 的 Server/Client 端,它將 Server 和 Client 封裝在底層,為上層應用提供更便捷的傳輸定義和調(diào)用接口。Actor 模型提供了面向通道 Channel 的雙工通道,可以接收來自對端的消息,也可以發(fā)送消息給對端。
Actor 收發(fā)的消息是面向二進制數(shù)組的,它不關(guān)心具體發(fā)送的是什么消息,也不關(guān)心序列化格式。Actor 使用 ActorFrameHeader 定義傳輸消息頭,Header 攜帶消息體長度。
Actor 一旦建立連接,生成的?Channel 通道會自動進行 KeepAlive 雙向保活機制。通過 Actor 服務發(fā)現(xiàn),可以與任意的 Actor 進行通信,無需再配置對端節(jié)點地址和端口。并且,針對相同 Type 的 Actor,還可以實現(xiàn)消息分發(fā)的負載均衡功能。
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?類庫,穩(wěn)定使用 protobuf-net?v2.0.0.668?版本。
RPC 消息信封
使用 ActorMessageEnvelope 封裝消息信封,攜帶如下信息:
| ? 屬性名稱 | ?屬性類型? | ?屬性描述? |
?MessageID | string | ?消息 ID,唯一 ID,通常使用 GUID。 |
?MessageTime | DateTime | ?消息產(chǎn)生時間 |
?CorrelationID | string | ?如果是 Response 則回填 Request 的 MessageID。? |
?CorrelationTime? | DateTime? | ?如果是 Response 則回填 Request 的 MessageTime。 |
?SourceEndpoint? | ?ActorEndpoint? | ?發(fā)送端節(jié)點描述,消息路由使用,默認不需要填寫。 |
?TargetEndpoint | ActorEndpoint | ?目的端節(jié)點描述,消息路由使用,默認不需要填寫。? |
?MessageType | string | ?消息類型,使用字符串描述。 |
?MessageData | byte[] | ?消息體,消息序列化后的二進制數(shù)組。 |
RPC 消息定義
RPC 消息分為 2 類:
InvokeMethodRequest / InvokeMethodResponse?用于定義請求回復模型的方法調(diào)用;
InvokeMethodMessage?用于定義請求無回復模型的方法調(diào)用;
通常 RPC 消息會包含如下屬性信息:
| ? 屬性名稱 | ?屬性類型? | ?屬性描述? |
?MethodLocator | string | ?RPC 方法描述,使用字符串描述。 |
?MethodArguments? | object[] | ?RPC 方法的入?yún)?#xff0c;object 對象數(shù)組。 |
例如,對于 ICalcService 中的 Add 方法:
MethodLocator = "Rodola.Rpc.TestContracts.ICalcService/Add_AddRequest";
MethodArguments = new object[] { new AddRequest(1, 2)};
鑒于 protobuf 本身是面向契約設計的,而 object[] 中的 object 是有不確定性的,并不能具體描述一個契約,則要求每一個 Argument 都需要支持 protobuf 的序列化,傳輸時系統(tǒng)會攜帶該 Argument 類型的?AssemblyQualifiedName,在對端通過反射進行反序列化。
Actor Directory 節(jié)點目錄
Actor Directory 負責注冊本地 Local Actor 到注冊中心,Local Actor 也可以在 Shutdown 時將自己從注冊中心移除掉。
通過 Actor Directory,Local Actor 可以使用?Type 和 Name 進行 Remote Actor 的檢索,進而進行 Channel 的建立和通信。
Actor Directory 通過 IActorDirectory 的抽象定義,可以與不同的目錄方案進行集成。例如,自實現(xiàn)基于 Actor 的 CenterActorDirectory,使用 XML 配置文件的 LocalXmlFileActorDirectory,使用 Consul 進行中心注冊的 ConsulActorDirectory。
使用 Consul 時,實際上是調(diào)用了?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 接口的不同實現(xiàn),進行不同方式的本地服務發(fā)現(xiàn)和注冊。例如,可以使用?Attribute 標記服務,通過對 Assembly 進行反射進行服務的實例化。
Service Directory 服務目錄
本地服務聚集到 Catalog 中后,系統(tǒng)會將服務逐個注冊到 Service Directory 服務目錄中,使得其他節(jié)點可以檢索服務進行使用。
通過 IServiceDirectory 的抽象定義,可以與不同的目錄方案進行集成。例如,使用 XML 配置文件的 LocalXmlFileServiceDirectory,使用 Consul 進行中心注冊的 ConsulServiceDirectory。
使用 Consul 時,注冊服務的 log 如下所示。
當 Redola 將服務注冊至 Consul 中后,可通過 Consul 內(nèi)置的 UI 進行查看。
http://localhost:8881/ui/#/dc1/servicesService Discovery 服務發(fā)現(xiàn)
通過 ConsulServiceDiscovery 實現(xiàn) IServiceDiscovery 服務發(fā)現(xiàn)接口,從?Consul?檢索指定服務類型的服務。
通過 Postman 測試 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 測試 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} ]
服務檢索方,可通過指定?IServiceLoadBalancingStrategy 的具體實現(xiàn)實施不同的負載均衡策略,默認指定的是?IServiceLoadBalancingStrategy 隨機選擇。
Service Dynamic Proxy 動態(tài)代理
為簡化?RPC 調(diào)用發(fā)起方的封裝,通常會使用 Dynamic Proxy 動態(tài)代理技術(shù)來動態(tài)生成給定契約的服務實例,將整體 RPC 的過程透明化。
例如,通過下面的代碼來動態(tài)生成 ICalcService 的動態(tài)代理。
var calcClient = rpcNode.Resolve<ICalcService>();目前 Redola.Rpc 默認集成了?Castle.Core?中的?Dynamic Proxy?模塊,通過對實例方法的 Intercept 攔截進行 RPC 消息的收發(fā)處理。
當然,如需集成其他 Dynamic Proxy 類庫,可通過 ISeviceProxyGenerator 接口進行方案實現(xiàn)。
Redola.Rpc 類庫依賴
Redola.Rpc 當前實現(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>總結(jié)
以上是生活随笔為你收集整理的Redola.Rpc 集成 Consul 服务发现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: DDD理论学习系列(13)-- 模块
- 下一篇: 分布式事务,EventBus 解决方案: