什么是RPC? RPC是Remote Procedure Call的縮寫,像Client-Servier一樣的遠程過程調用,也就是調用遠程服務就跟調用本地服務一樣方便,一般用於將程序部署在不同的機器上,供客戶端進行調用。就像一個request-response調用系統一樣簡單。在面向對象編程的程序中,RPC也可以用Remote method invocation(RMI)來展現。為什么用它呢,因為隨着分布式結構的普遍,越來越多的應用需要解耦,將不同的獨立功能部署發布成不同的服務供調用。
它的主要流程是Client -> Client Stub -> Network -> Server Stub -> Server 執行完成之后再進行返回。
這里邊比較重要的就是Clint Stub和Server Stub,他們主要的作用就是將調用的方法和參數進行編碼(Marshalling)序列化,將序列化后的數據通過網絡發送給Server Stub,然后等待Server回執。Server Stub將受到的序列化字節進行解碼(Unmarshaling)反序列化,然后再將參數傳入到對應到的方法中執行,將得出的結果計算出來之后再進行返回,返回的過程和正向的過程類似。
那么這個結構里邊的內容這么復雜,而且還需要保證數據的完整,網絡等因素,所以這塊有一個通訊的標准就是IDL(Interface Description Language)接口定義語言,因為很多程序采用了不同的編程語言(Java,C, C++ etc.)和不同的操作系統(Windows, CentOs, RHEL etc.),要保證數據能夠正常通訊,並且不受語言和操作系統等限制,就有了它。比如Apache的Thrift 、Avro和Google的 Protocol Buffers、阿里的Dubbo等。
其實很多人早已經應用了它了,但是可能不知道,比如web service的WSDL。下面我就用一個小程序來創建一個簡單基於的WSDL的RPC。使用JDK的JAX-WS實現
接口類(定義了一個非常簡單的方法):
package com.hqs.rpc; import javax.jws.WebMethod; import javax.jws.WebService; import javax.jws.soap.SOAPBinding; import javax.jws.soap.SOAPBinding.Style; /** * 接口 * @author hqs * */ @WebService @SOAPBinding(style = Style.RPC) public interface IRPCService { @WebMethod public String RPCMethod(String str); }
實現類:
package com.hqs.rpc; import javax.jws.WebService; /** * 實現類 * @author hqs * */ @WebService (endpointInterface = "com.hqs.rpc.IRPCService") public class RPCServiceImpl implements IRPCService { @Override public String RPCMethod(String str) { System.out.println("service received:" + str); return "RPC Method invoked: " + str; } }
服務發布類:
package com.hqs.rpc; import javax.xml.ws.Endpoint; /** * 發布類 * @author hqs * */ public class RPCPublisher { public static void main(String[] args) { //自己定義地址 Endpoint.publish("http://localhost:9966/rpc", new RPCServiceImpl()); } }
這個時候啟動服務類,然后就可以通過地址http://localhost:9966/rpc?wsdl訪問WSDL文件了了:
<?xml version="1.0" encoding="UTF-8"?> <!-- Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.2.4-b01. --> <!-- Generated by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.2.4-b01. --> -<definitions name="RPCServiceImplService" targetNamespace="http://rpc.hqs.com/" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://rpc.hqs.com/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:wsp1_2="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> <types/> -<message name="RPCMethod"> <part type="xsd:string" name="arg0"/> </message> -<message name="RPCMethodResponse"> <part type="xsd:string" name="return"/> </message> -<portType name="IRPCService"> -<operation name="RPCMethod"> <input message="tns:RPCMethod" wsam:Action="http://rpc.hqs.com/IRPCService/RPCMethodRequest"/> <output message="tns:RPCMethodResponse" wsam:Action="http://rpc.hqs.com/IRPCService/RPCMethodResponse"/> </operation> </portType> -<binding type="tns:IRPCService" name="RPCServiceImplPortBinding"> <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> -<operation name="RPCMethod"> <soap:operation soapAction=""/> -<input> <soap:body namespace="http://rpc.hqs.com/" use="literal"/> </input> -<output> <soap:body namespace="http://rpc.hqs.com/" use="literal"/> </output> </operation> </binding> -<service name="RPCServiceImplService"> -<port name="RPCServiceImplPort" binding="tns:RPCServiceImplPortBinding"> <soap:address location="http://localhost:9966/rpc"/> </port> </service> </definitions>
接下來我們就可以寫客戶端類:
package com.hqs.rpc; import java.net.MalformedURLException; import java.net.URL; import javax.xml.namespace.QName; import javax.xml.ws.Service; /** * 客戶端類 * @author hqs * */ public class RPCClient { public static void main(String[] args) { try { URL url = new URL("http://localhost:9966/rpc?wsdl"); QName qname = new QName("http://rpc.hqs.com/","RPCServiceImplService"); Service service = Service.create(url, qname); IRPCService irpc = service.getPort(IRPCService.class); System.out.println(irpc.RPCMethod("client")); } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } RPC Method invoked: client
簡單的RPC就這么實現了。其實目前實現上一些公司實現的比較復雜,分為服務分為provider和consumer,以及負責監控管理的provider,provider一般采用zookeeper單數集群,用於管理和監控provider的服務注冊,因為provider可能部署在同一台機器上的不同端口或者不同機器上,consumer通過zookeeper就可以拿到provider的IP/接口/版本號/端口等信息,然后進行調用。
好了,如果有不對的地方,請大家指正。
