zoukankan      html  css  js  c++  java
  • RPC简易学习

    0.RPC简介

      RPC,   英文全称:Remote Process Call.   中文全称:远程过程调用.

      客户端通过网络请求调用远程服务端对外暴露服务。常用的两种RPC协议:TCP、HTTP。

      举例描述:两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。

      最简单的场景:通过百度搜索所需要的信息。

      当我们在百度的搜索栏中填入所需要查找的信息时,浏览器客户端会创建一个网络请求,将所要查询的信息标识传给百度的远程服务器端。服务器得到请求之后,调用自身内部的处理操作,再将查询到的信息返回给浏览器,浏览器解析展示给用户。

     1.RPC的优缺点

       优点:

        a) 解决单应用的性能瓶颈。

        b) 实现应用之间的解耦,服务拆分。

        c)  提升功能代码的复用性。

      缺点:

        a) 网络交互

        b) 增加应用的复杂性

    2.RPC实现流程

    • 首先,要解决通讯的问题,主要是通过在客户端和服务器之间建立TCP连接,远程过程调用的所有交换的数据都在这个连接里传输。连接可以是按需连接,调用结束后就断掉,也可以是长连接,多个远程过程调用共享同一个连接。
    • 第二,要解决寻址的问题,也就是说,A服务器上的应用怎么告诉底层的RPC框架,如何连接到B服务器(如主机或IP地址)以及特定的端口,方法的名称名称是什么,这样才能完成调用。比如基于Web服务协议栈的RPC,就要提供一个endpoint URI,或者是从UDDI服务上查找。如果是RMI调用的话,还需要一个RMI Registry来注册服务的地址。
    • 第三,当A服务器上的应用发起远程过程调用时,方法的参数需要通过底层的网络协议如TCP传递到B服务器,由于网络协议是基于二进制的,内存中的参数的值要序列化成二进制的形式,也就是序列化(Serialize)或编组(marshal),通过寻址和传输将序列化的二进制发送给B服务器。
    • 第四,B服务器收到请求后,需要对参数进行反序列化(序列化的逆操作),恢复为内存中的表达方式,然后找到对应的方法(寻址的一部分)进行本地调用,然后得到返回值。
    • 第五,返回值还要发送回服务器A上的应用,也要经过序列化的方式发送,服务器A接到后,再反序列化,恢复为内存中的表达方式,交给A服务器上的应用

      

    3.Code实现

      定义接口:

    /**
     * Rpc接口
     * 
     * @author apple
     */
    public interface HelloService {
    
        
            String hello(String str);
        
    }

      接口实现:

    public class HelloServiceImpl implements HelloService {
    
        @Override
        public String hello(String str) {
            return return "hello " + str;
        }
    
    }

      RPC服务端:

    public class RpcExporter {
    
        static Executor executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
        
        public static void exporter(String hostName,int port) throws Exception{
            
            ServerSocket server = new ServerSocket();
            
            server.bind(new InetSocketAddress(hostName, port));
            
            try{
                while(true){
                    executor.execute(new ExporterTask(server.accept()));
                }
            }finally{
                server.close();
            }
            
        }
        
        private static class ExporterTask implements Runnable{
            
            Socket client = null;
            
            public ExporterTask(Socket client){
                this.client = client;
            }
    
            @Override
            public void run() {
                
                ObjectInputStream input = null;
                ObjectOutputStream output = null;
                
                try {
                    input = new ObjectInputStream(client.getInputStream());
                    String interfaceName  = input.readUTF();
                    Class<?> service = Class.forName(interfaceName);
                    String methodName = input.readUTF();
                    Class<?>[] parameterTypes = (Class<?>[])input.readObject();                
                    Object[] arguments = (Object[])input.readObject();
                    Method method = service.getMethod(methodName, parameterTypes);
                    Object result = method.invoke(service.newInstance(),arguments);
                    output = new ObjectOutputStream(client.getOutputStream());
                    output.writeObject(result);
                } catch (Exception e) {
                    e.printStackTrace();
                }finally {
                    if(output != null){
                        try {
                            output.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }finally{
                            output = null;
                        }
                    }
                    if(input != null){
                        try {
                            input.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }finally{
                            input = null;
                        }
                    }
                    if(client != null){
                        try {
                            client.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }finally{
                            input = null;
                        }
                    }
                }
                
            }
                    
        }
        
    }

      RPC客户端:

    public class RpcImporter<S> {
    
        
        @SuppressWarnings("unchecked")
        public S importer(final Class<?> serviceClass,final InetSocketAddress addr){
            
            
            return (S) Proxy.newProxyInstance(serviceClass.getClassLoader(), new Class<?>[]{serviceClass.getInterfaces()[0]}, new InvocationHandler() {
                
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    Socket socket = null;
                    ObjectOutputStream output = null;
                    ObjectInputStream input = null;
                    
                    try{
                        socket = new Socket();
                        socket.connect(addr);
                        output = new ObjectOutputStream(socket.getOutputStream());
                        output.writeUTF(serviceClass.getName());
                        output.writeUTF(method.getName());
                        output.writeObject(method.getParameterTypes());
                        output.writeObject(args);
                        input = new ObjectInputStream(socket.getInputStream());
                        return input.readObject();
                    }finally{
                        if(socket != null)
                            socket.close();
                        if(output != null)
                            output.close();
                        if(input != null)
                            input.close();
                    }
                }
            });
        }
        
    }

      测试类:

    public class RpcTest {
    
        public static void main(String[] args) {
            
            
            new Thread(new Runnable() {
                
                @Override
                public void run() {
                    
                    try {
                        RpcExporter.exporter("localhost", 8088);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    
                }
                
            }).start();
            
            
            RpcImporter<HelloService> importer = new RpcImporter<HelloService>();
            HelloService service = importer.importer(HelloServiceImpl.class, new InetSocketAddress("localhost", 8088));
            while(true){
                System.out.println(service.hello("word"));
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            
        }
        
    }

      执行结果:

    4.常用的RPC框架

      

      Java RMI

      Web Service

      Hessian

      Thrift

      Http Rest API

      Dubbo

    5.参考资料

      

      谁能用通俗的语言解释一下什么是 RPC 框架?

          https://www.zhihu.com/question/25536695

  • 相关阅读:
    YUI(YUIcompressor)压缩参数选项
    js进制转换两则
    软件代码生成工具软工厂V2.0版本上线!欢迎新老用户免费使用!
    软件代码自动化生成工具我们该不该用!
    软件代码生成工具软工厂V2.0版本免费使用地址+教学视频,快速完成开发任务。
    转发在.NET上使用ZeroMQ
    . Net环境下消息队列(MSMQ)对象的应用
    消息队列软件产品大比拼
    ubuntu服务器安装指南
    简单的分布式应用程序日志记录器(logger)-基于MSMQ(消息队列)
  • 原文地址:https://www.cnblogs.com/LuisYang/p/6047529.html
Copyright © 2011-2022 走看看