前言
在 [上一篇](C#使用Thrift作為RPC框架入門(一) - 楊凱2020 - 博客園 (cnblogs.com)) 文章中我們講述了Thrif的基本知識,包括在C#語言下使用需要用到的工具以及使用nuget安裝thrift開發包,還描述了它支持的數據類型,以及它支持IDL的描述文件,和一個簡單的例子。
接上文,我這里再補充兩點關於IDL描述文件的:
-
Thrift支持Byte類型,我們需要用i8來表示,它對應C#中的sbyte類型,如果我們用byte關鍵字,我們會看到一個【WARNING】
-
在使用字節數組時,我們需要用binary類型,如果我們用list<byte>,也會看到一個【WARNING】
生成的代碼--兩個接口兩個類
我們從上一篇文章最后那個簡單的例子中可以看到,Thrift框架把我們標記為service的結構生成了對應的一個類,這個類中有兩個內部接口兩個內部類。這就是我們使用該框架的重點部分。
兩個內部接口
這兩組接口代表了框架給我生成的兩組方法,這兩組方法中一組(ISync)是用於同步調用的方法,另一組(Iface)是用於異步調用的方法,而Iface又繼承了ISync接口。
-
- Iface接口被一個為簽名為Client的類繼承,這個類是我們使用RPF框架調用的客戶端。
-
- ISync接口被作為一個簽名為Processor類的構造函數的參數,**Processor**作為響應我們客戶端請求的處理器類,我們需要自己實現具體的Handler,去處理客戶端的請求,該實現是作為**Processor**的構造函數的實參
兩個內部類2
在講兩個內部接口的時候,我們以及提過了這兩個內部類的是干什么用的,接下來我們將通過一個具體的示例,來感受一下這個兩個類的使用方法
示例
客戶端代碼:
static void Main(string[] args) { TTransport framedTransport = new TSocket("127.0.0.1",9999); Thrift.Protocol.TCompactProtocol compactProtocol = new Thrift.Protocol.TCompactProtocol(framedTransport); ThriftIDL.Services.PeopleService.Client client = new ThriftIDL.Services.PeopleService.Client(compactProtocol); framedTransport.Open(); People people = new People(); client.SetPeople(people); }
服務器端代碼:
static void Main(string[] args) { Thrift.Transport.TServerSocket serverSocket = new Thrift.Transport.TServerSocket(9999,1000); TProcessor processor= new ThriftIDL.Services.PeopleService.Processor(new PeoperServiceHandler()); Thrift.Server.TSimpleServer server = new Thrift.Server.TSimpleServer(processor,serverSocket); server.Serve(); }
服務器端處理器對應的代碼:
public class PeoperServiceHandler : ThriftIDL.Services.PeopleService.Iface { public People GEtPeople() { throw new NotImplementedException(); } public void SetPeople(People people) { throw new NotImplementedException(); } }
我這里只做演示,並沒有實現具體的方法。
多路復用處理器
對我們服務器端的代碼細細品味之后,我們會突然發現,如果我們有多個**service**,那么我們就要寫多個這樣的服務端代碼,這個似乎是我們不能接收,更要命的是,我們要每個**service**都要對應一個監聽端口,這個有點恐怖。
示例2
服務器端代碼:
TMultiplexedProcessor multiplexedProcessor = new TMultiplexedProcessor(); multiplexedProcessor.RegisterProcessor("serviceName1" , new Service1.Processor(new Service1Handler())); multiplexedProcessor.RegisterProcessor("serviceName2" , new Service2.Processor(new Service2Handler())); multiplexedProcessor.RegisterProcessor("serviceName3" , new Service3.Processor(new Service3Handler())); TServerSocket serverSocket = new Thrift.Transport.TServerSocket(Port, 4000); TThreadPoolServer server = new TThreadPoolServer(multiplexedProcessor, serverSocket); server.Serve();
代碼中的Service1.Processor、Service2.Processor、Service3.Processor是我們定義的service生成的代碼類對應的客戶端調用代碼如下:
Thrift.Transport.TSocket socket = new Thrift.Transport.TSocket(remoteAddress, port, 4000); TCompactProtocol compactProtocol = new TCompactProtocol(socket); TMultiplexedProtocol multiplexedProtocol = new TMultiplexedProtocol(compactProtocol, "serverName1"); Service1.Client client1=new Service1.Client(multiplexedProtocol); socket.Open();
RPC:ServiceName2:
Thrift.Transport.TSocket socket = new Thrift.Transport.TSocket(remoteAddress, port, 4000); TCompactProtocol compactProtocol = new TCompactProtocol(socket); TMultiplexedProtocol multiplexedProtocol = new TMultiplexedProtocol(compactProtocol, "serverName2"); Service2.Client client1=new Service2.Client(multiplexedProtocol); socket.Open();
RPC:ServiceName3:
Thrift.Transport.TSocket socket = new Thrift.Transport.TSocket(remoteAddress, port, 4000); TCompactProtocol compactProtocol = new TCompactProtocol(socket); TMultiplexedProtocol multiplexedProtocol = new TMultiplexedProtocol(compactProtocol, "serverName3"); Service3.Client client1=new Service3.Client(multiplexedProtocol); socket.Open();
以上就是多路復用處理器,在開發過程中的使用方法,代碼用使用到的類型,我們會在下一節中講解。
那么,現在問題又來了,如果service數量巨多的話,這樣我們會得到大量的重復的代碼,我們應該怎樣處理這種情況呢?對!我們對Client和Processor做進一步的封裝,這里的封裝我使用了**約定勝於配置**的架構策略。
封裝后的多路復用
服務器端代碼:
public class RPCServer { TThreadPoolServer server = null; TMultiplexedProcessor multiplexedProcessor = null; public RPCServer(int Port) { multiplexedProcessor = new TMultiplexedProcessor(); TServerSocket serverSocket = new Thrift.Transport.TServerSocket(Port, 4000); server = new TThreadPoolServer(multiplexedProcessor , serverSocket,new Thrift.Transport.TTransportFactory() ,new Thrift.Protocol.TCompactProtocol.Factory()); } public void RegisterProcessor(string serverName, TProcessor processor) { multiplexedProcessor.RegisterProcessor(serverName,processor); } public void RegisterProcessor<T>(object ProcessorHandler) where T: TProcessor { string[] strArray = typeof(T).FullName.Split(new string[] { ".", "+" } , StringSplitOptions.RemoveEmptyEntries); string serverName = strArray[strArray.Length - 2].ToUpper(); ConstructorInfo[] constructorInfos = typeof(T).GetConstructors(); TProcessor processor = (T)constructorInfos[0].Invoke(new object[] { ProcessorHandler }); multiplexedProcessor.RegisterProcessor(serverName, processor); } /// <summary> /// 會阻塞當前線程 /// </summary> public void Start() { server.Serve(); } public void Stop() { server.Stop(); } }
客戶端代碼:
public class RPCClient<T> : IDisposable { public T Instance; Thrift.Transport.TSocket socket = null; public RPCClient(string remoteAddress, int port) { socket = new Thrift.Transport.TSocket(remoteAddress, port, 4000); TCompactProtocol compactProtocol = new TCompactProtocol(socket); string[] strArray = typeof(T).FullName.Split(new string[] { ".", "+" } , StringSplitOptions.RemoveEmptyEntries); string serverName = strArray[strArray.Length - 2].ToUpper(); TMultiplexedProtocol multiplexedProtocol = new TMultiplexedProtocol(compactProtocol, serverName); ConstructorInfo constructorInfo = typeof(T) .GetConstructor(new Type[] { typeof(TProtocol) }); Instance = (T)constructorInfo.Invoke(new object[] { multiplexedProtocol }); } public void Open() { socket.Open(); } public void Close() { socket.Close(); } ......
我們使用一下代碼進行服務器端service的注冊:
RPCProxy.RPCServer rPCServer = new RPCProxy.RPCServer(52364); rPCServer.RegisterProcessor("ServiceName1" , new ServiceName1.Processor(new ServiceName1Handler())); rPCServer.RegisterProcessor<ServiceName2.Processor>(,); rPCServer.RegisterProcessor<ServiceName3.Processor>(,); rPCServer.Start();
客戶端的使用 用一下代碼:
RPCProxy.RPCClient<ServiceName1.Client> rPCClient1 = new RPCProxy.RPCClient<ServiceName1.Client>("127.0.0.1", 52364); RPCProxy.RPCClient<ServiceName2.Client> rPCClient2 = new RPCProxy.RPCClient<ServiceName2.Client>("127.0.0.1", 52364); ......
結尾
這一小節我們詳細講解了Thrift框架生成的代碼在Client和Server的使用方法,已經多路復用處理器的使用方法,我們也對多路復用進行了封裝優化,使其更易用,其中我們使用了一下Thrift提供的類,我們並沒有詳細的講解。我們將在下一節講thrift的框架設計時來對這些類進行說明。