zoukankan      html  css  js  c++  java
  • 聊聊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/接口/版本号/端口等信息,然后进行调用。

      好了,如果有不对的地方,请大家指正。

  • 相关阅读:
    SqlServer怎样获取查询语句的成本
    Testcase中Debug 提示
    cmd batch use variable
    主流数据库默认端口
    Usage of doskey
    操作系统shell的比较/Comparison of command shells
    延长windows激活时间
    一道面试题和一个结果.
    注册表操作CMD(reg.exe)
    adb 查看固件版本
  • 原文地址:https://www.cnblogs.com/huangqingshi/p/7803642.html
Copyright © 2011-2022 走看看