聊聊RPC及其原理


  什么是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/接口/版本號/端口等信息,然后進行調用。

  好了,如果有不對的地方,請大家指正。

 


免責聲明!

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



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