RPC简述

RPC简述

提问:什么是RPC?

PRC是一种远程过程调用框架,相比远程调用,我们可以先理解本地调用,本地调用就是方法A和方法B都写在同一个工程中,然后A可以调用B。但是如果A方法和B方法是属于不同工程中,则需要进行远程调用。

误解:如A工程中需要调用B工程中的方法,一般是将B工程打一个包,然后A中会引入这个包,然后就可以调用到B中的方法了,这种两个工程间没有网络通信则只能称为远程方法的引用,只有两个工程存在网络通信,A调用B的方法并且是B处理后返回给A,才能称为远程过程调用!!

RPC远程调用原理

一、RPC是怎么做到远程调用的,其原理是什么?

追根究底,R客户端与服务端建立TCP链接,相比于HTTP通信协议少去应用层的许多东西。数据的传输就是通过这个TCP的链接。

Client:服务消费方

Server:服务提供方

(1).Client以调用本地服务方式调用远程API

(2).Client Stub负责接收参数,方法等,将其封装(编码)成能够进行网络传输的消息体

(3).Client Stub负责网络寻址,将消息体发送对应的服务端

(4).Server Stub负责接收消息,并解码成服务端能够识别的信息,调用对应的服务端方法

(5).Server本地服务将调用结果发送给Server Stub

(6).Server Stub将返回结果包装成消息体返回给Client Stub

(7).Client Stub接收消息并进行解码

(8).Client获取到最终调用结果

二、实例运行

举例:实现两个本地工程之间的远程调用,如下是RpcProvider工程的代码

public interface BatterCakeService {
    public String sellBatterCake(String name);
}
public class BatterCakeServiceImpl implements  BatterCakeService{
    public String sellBatterCake(String name) {
        return name+"味道很赞";
    }
}
public class RpcProvider {
    //存储注册的服务列表
    private static List<Object> serviceList;

    /**
     * 发布rpc服务
     * @param
     * @param port
     * @throws Exception
     */
    public static void export(int port,Object... services) throws Exception {
        serviceList= Arrays.asList(services);
        //创建本地服务,端口20006
        ServerSocket server = new ServerSocket(port);
        Socket client = null;
        while (true) {
            //阻塞等待输入,一直监听,如果有服务请求,则返回,否则一直等待
            client = server.accept();
            //每一个请求,启动一个线程处理
            new Thread(new ServerThread(client,serviceList)).start();
        }
    }
}
public class ServerThread implements Runnable {
    private Socket client=null;
    private List<Object> serviceList=null;
    public ServerThread(Socket client,List<Object> service)
    {
        this.client=client;
        this.serviceList=service;
    }
    @Override
    public void run() {
        ObjectInputStream input=null;
        ObjectOutputStream output=null;
        try {
                //创建请求服务的输入输出流
                input=new ObjectInputStream(client.getInputStream());
                output=new ObjectOutputStream(client.getOutputStream());
            // 读取客户端要访问那个service
            Class serviceClass = (Class)input.readObject();

            // 找到该服务类
            Object obj = findService(serviceClass);
            if(obj==null)
            {
                output.writeObject(serviceClass.getName() + "服务未发现");
            }else {

                String methodName = input.readUTF();
                Class<?>[] parameterTypes = (Class<?>[]) input.readObject();
                Object[] arguments = (Object[]) input.readObject();
                Method method = obj.getClass().getMethod(methodName, parameterTypes);
                Object result = method.invoke(obj, arguments);
                output.writeObject(result);
            }

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
            try {
                client.close();
                input.close();
                output.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

            }


    }

    private Object findService(Class serviceClass) {
        // TODO Auto-generated method stub
        for (Object obj : serviceList) {
            boolean isFather = serviceClass.isAssignableFrom(obj.getClass());
            if (isFather) {
                return obj;
            }
        }
        return null;
    }

}

主函数:

public class RpcBootStrap {
    public static void main(String[] args) throws Exception {
        BatterCakeService batterCakeService =new BatterCakeServiceImpl();
        //发布卖煎饼的服务,注册在20006端口
        RpcProvider.export(20006,batterCakeService);
    }
}

运行主函数,保持服务提供方的监听

服务消费方consumer代码:consumer中会调用provider中的sellBatterCake(String name)方法,可以从如下看出,consumer中并没有sellBatterCake(String name)方法的实现

public interface BatterCakeService {
    public String sellBatterCake(String name);
}
public class ProxyHandler implements InvocationHandler {
    private String ip;
    private int port;

    public ProxyHandler(String ip, int port) {
        // TODO Auto-generated constructor stub
        this.ip = ip;
        this.port = port;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Socket socket = new Socket(this.ip, this.port);
        ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
        ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
        try {
            output.writeObject(proxy.getClass().getInterfaces()[0]);
            output.writeUTF(method.getName());
            output.writeObject(method.getParameterTypes());
            output.writeObject(args);
            output.flush();
            Object result = input.readObject();
            if(result instanceof Throwable) {
                throw (Throwable) result;
            }
            return result;
        } finally {
            socket.shutdownOutput();
        }
    }



}

public class RpcConsumer {
    public static <T> T getService(Class<T> clazz,String ip,int port) {
        ProxyHandler proxyHandler =new ProxyHandler(ip,port);
        return (T) Proxy.newProxyInstance(RpcConsumer.class.getClassLoader(), new Class<?>[] {clazz}, proxyHandler);
    }
}
public class RpcTest {
    public static void main(String[] args) {
        BatterCakeService batterCakeService=RpcConsumer.getService(BatterCakeService.class, "127.0.0.1", 20006);
        String result=batterCakeService.sellBatterCake("双蛋");
        System.out.println(result);
    }
}

运行consumer中的主函数:

上述可以看出是consumer远程调用了provider中的sellBatterCake()方法!!!

posted @ 2019-08-17 16:45  balvender  阅读(332)  评论(0编辑  收藏  举报