RPC源码实现&解析 java

RPC源码实现&解析

写在前面:最近复习的时候,RPC协议不咋清楚,就去学习了下,看了博客还是不太懂,就跟着这一篇手动实现了下,在此记录下过程

实现过程

原理讲解:

  • RPC的全称是Remote Producedure Call-->远程过程调用,通俗的来讲就是客户端调用服务端的方法,并获取返回的结果
  • 如图
    • 不通过http(在应用层,很慢),使用socket编程实现
  • 然后服务端和客户端设计如下:
    • 服务端:
      • 注册中心,维护着注册的方法,需要提供方法名和需要的参数才能调用-->反射机制
      • 服务器:产生一个监听的服务器,有请求打过来就从线程池里抓线程去执行(非阻塞式)
      • 方法接口&实现类:服务器端需要有可能被调用的方法的具体实现
    • 客户端
      • 客户端实体(Socket实现,通过Scoket通信来实现参数传送以及服务器运行结果的获取)
      • 方法接口:其实也不需要接口,服务器上维护的方法池是kv结构的,k的类型是String,所以只需要接口的名字;本文用类的反射机制直接获取Name(这里有疑问,需要进一步查证,因为存在一个调用的过程)-->//20221013 22:25更新,发现不行,设置代理类的时候需要接口类文件,但是实现类确实是不需要的

具体实现代码

  • 方法接口
package RPC;

public interface ServiceProducer {
    String sendData(String data);
}
  • 方法具体实现:
package RPC;

public class ServiceProducerImpl implements ServiceProducer{
    @Override
    public String sendData(String data) {
        return "hello world:"+data;
    }
}
  • 客户端
package RPC;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.InetSocketAddress;
import java.net.Socket;

public class RPCClient<T> {
    /**
     * <T> T T表示泛型类型,<T>表示声明该方法是泛型方法
     * 本来类上面声明了<T> 方法上可以不用再声明,但是static方法不属于类的一部分,所以需要手动声明
     * Class<?>表示未知类型的类
     * @param serviceInterface
     * @param addr
     * @param <T>
     * @return
     */
    public static <T> T getRemoteProxyObj(final Class<?> serviceInterface,final InetSocketAddress addr){
        //1.将本地的接口调用转换成JDK的动态代理,在动态代理中实现接口的远程调用
        return (T) Proxy.newProxyInstance(serviceInterface.getClassLoader(),new Class<?>[]{serviceInterface},
                new InvocationHandler(){
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Socket socket = null;
                        ObjectOutputStream output = null;
                        ObjectInputStream input = null;
                        //2.创建Socket客户端,根据指定地址连接远程服务提供者
                        try {
                            socket = new Socket();
                            socket.connect(addr);//链接服务器

                            output = new ObjectOutputStream(socket.getOutputStream());//获取输出流
                            output.writeUTF(serviceInterface.getName());//字节流写出
                            output.writeUTF(method.getName());//调用方法的名字
                            output.writeObject(method.getParameterTypes());
                            output.writeObject(args);

                            //4.同步阻塞等待服务器返回应答,获取应答后返回
                            input = new ObjectInputStream(socket.getInputStream());//对接,获取输入流
                            return input.readObject();
                        }finally {
                            if(socket != null){
                                socket.close();
                            }
                            if(output !=null){
                                output.close();
                            }

                            if(input!=null){
                                input.close();
                            }
                        }
                    }
                }
        );
    }
}

  • 注册中心接口
package RPC;

import java.io.IOException;

public interface Server {
    void start() throws IOException;
    void register(Class serviceInterface,Class impl);
    boolean isRunning();
    int getPort();
    void stop();
}

  • 注册中心&服务器端(由于服务器端只是线程,写在一起了)
package RPC;


import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;
import java.util.concurrent.*;

public class ServiceCenter implements Server {//注册中心
    private static ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());//用于管理和实现多线程
    private static final HashMap<String,Class> serviceRegistry = new HashMap<String,Class>();
    private static boolean isRunning = false;
    private static int port;

    public ServiceCenter(int port){
        ServiceCenter.port = port;
    }

    @Override
    public void start() throws IOException {
        ServerSocket server = new ServerSocket();
        server.bind(new InetSocketAddress(port));//绑定端口,创建服务端
        System.out.println("Server Start .....");
        try{
            while(true){
                executor.execute(new ServiceTask(server.accept()));//丢到线程里去执行, server接受到的是client的socket
            }
        }finally{
            server.close();
        }
    }

    @Override
    public void register(Class serviceInterface, Class impl) {
        serviceRegistry.put(serviceInterface.getName(),impl);//传入接口的名字
    }

    @Override
    public boolean isRunning() {
        return isRunning;
    }

    @Override
    public int getPort() {
        return port;
    }

    @Override
    public void stop() {
        isRunning = false;
        executor.shutdown();
    }

    private static class ServiceTask implements Runnable{//包装程序,将服务器接收的信息转换成线程提交给executor执行
        Socket client = null;
        public ServiceTask(Socket client){
            this.client = client;
        }

        @Override
        public void run() {
            ObjectOutputStream output = null;
            ObjectInputStream input = null;
            try{
                input = new ObjectInputStream(client.getInputStream());
                String serviceName = input.readUTF();
                String methodName = input.readUTF();
                Class<?>[] parameterTypes = (Class<?>[])input.readObject();
                Object[] arguments = (Object[])input.readObject();//接收完毕
                Class serviceClass = serviceRegistry.get(serviceName);
                if(serviceClass == null){
                    throw new ClassNotFoundException(serviceName+"not found!");
                }
                Method method = serviceClass.getMethod(methodName,parameterTypes);
                Object result = method.invoke(serviceClass.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();
                    }
                }

                if(input!=null){
                    try {
                        input.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

                if(client!=null){
                    try {
                        client.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

  • 测试代码:
package RPC;

import java.io.IOException;
import java.net.InetSocketAddress;

public class RPCTest {
    public static void main(String[] args) throws IOException {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Server serviceServer = new ServiceCenter(8888);//设置8888端口来设置服务器
                    serviceServer.register(ServiceProducer.class, ServiceProducerImpl.class);
                    serviceServer.start();
                }catch(IOException e){
                    e.printStackTrace();
                }
            }
        }).start();//丢进线程,客户端也在此方法内测试


        ServiceProducer service = RPCClient.getRemoteProxyObj(ServiceProducer.class,new InetSocketAddress("localhost",8888));
        System.out.println(service);
        System.out.println(service.sendData("test"));
    }
}


以上
希望对后来者有所帮助,有说的不对的地方还请指出/抱拳

posted @ 2022-10-13 21:58  醉生梦死_0423  阅读(213)  评论(0编辑  收藏  举报