手写简易版RPC框架基于Socket

什么是RPC框架?

RPC就是远程调用过程,实现各个服务间的通信,像调用本地服务一样。

RPC有什么优点?

- 提高服务的拓展性,解耦。
- 开发人员可以针对模块开发,互不影响。
- 提升系统的可维护性及高可用等。

基于socket思考:

- 怎么建立网络通信?

- 服务端怎么暴露服务并处理客户端请求?

- 客户端怎么去拿到服务并调用?

基于socket的通信的展示图: 流程 1---->2---->3---->4

 

 上面的图形根据自己的理解画的--有些不足,但理解就行。

 创建父工程order-server,子order-api,子order-producer,user-consumer的maven工程(也可以自己创建成独立的工程)。

 创建过程省略....

 项目工程图如下:

 

order-api工程:

  • OrderApi类
package com.sqp.example.rpc;
/**
 * @author sqp
 * @date 2020/7/17 17:38
 */
public interface OrderApi {
    String getOrderName(String name);
}
  • RpcRequest类
package com.sqp.example.rpc;
import java.io.Serializable;
import lombok.Getter;
import lombok.Setter;
/**
 * @author sqp
 * @date 2020/7/17 17:57
 */
@Setter
@Getter
public class RpcRequest implements Serializable {

    /**
     * 方法名
     */
    private String methodName;

    /**
     * 方法的入参参数
     */
    private Object[] args;

    /**
     * 方法的路径
     */
    private String className;

    /**
     * 参数类型
     */
    private Class[] types;

order-producer工程:

  • OrderController类
package com.sqp.example.rpc;
/**
 * @author sqp
 * @date 2020/7/17 17:39
 */
public class OrderController implements OrderApi {
    @Override
    public String getOrderName(String name) {
        return "商品名称【"+name+"】";
    }
}
  • ProxySocketServer类
package com.sqp.example.rpc;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 客户端监听
 * @author sqp
 * @date 2020/7/17 17:41
 */
public class ProxySocketServer {

    private final ExecutorService executorService = Executors.newCachedThreadPool();

    public void publisher(Object service, int port){
        ServerSocket serverSocket = null;
        try {
            // 创建一个serverSocket实例
            serverSocket = new ServerSocket(port);
            // 一直监听客户端的请求
            while(true){
                // socket是一个阻塞IO还有连接阻塞
                Socket socket =serverSocket.accept();
                // 使用线程处理阻塞IO,这样接收消息这边就不需要阻塞 ,但受限于线程
                executorService.submit(new ProcessorHandler(socket,service));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            if(serverSocket != null){
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  • ProcessorHandler类
package com.sqp.example.rpc;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.Socket;

/**
 * 线程处理类
 * @author sqp
 * @date 2020/7/17 17:46
 */
public class ProcessorHandler implements Runnable {

    /**
     * 监听返回的socket连接
     */
    private Socket socket;

    /**
     * 发布的服务
     */
    private Object service;

    /**
     * 构造方法
     * @param socket
     * @param service
     */
    public ProcessorHandler(Socket socket, Object service) {
        this.socket =socket;
        this.service=service;
    }

    @Override
    public void run() {
        ObjectInputStream inputStream = null;
        ObjectOutputStream outputStream = null;
        try{
            // 获取接收的数据
            inputStream = new ObjectInputStream(socket.getInputStream());
            // 反序列化
            RpcRequest rpcRequest = (RpcRequest) inputStream.readObject();
            // 反射调用
            Object res = invoke(rpcRequest);

            outputStream = new ObjectOutputStream(socket.getOutputStream());
            outputStream.writeObject(res);
            outputStream.flush();
        }catch(Exception e){
            e.printStackTrace();
        }finally {
            if(outputStream!= null){
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(inputStream != null){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 通过反射进行服务的调用
     * @param rpcRequest
     * @return*/
    private Object invoke(RpcRequest rpcRequest) throws ClassNotFoundException, NoSuchMethodException,
            InvocationTargetException, IllegalAccessException {
        // 获取class
        Class clazz = Class.forName(rpcRequest.getClassName());
        // 找到目标的方法, 通过传入方法名称和参数类型
        Method method = clazz.getMethod(rpcRequest.getMethodName(), rpcRequest.getTypes());
        // 传入服务和参数
        return method.invoke(service, rpcRequest.getArgs());
    }
}
  • App类
package com.sqp.example.rpc;

public class App {
    public static void main( String[] args ) {
        // new一个实例
        OrderApi orderApi = new OrderController();
        ProxySocketServer proxySocketServer = new ProxySocketServer();
        // 发布服务
        proxySocketServer.publisher(orderApi, 8080);
    }
}

user-consumer工程:

  • ProxySocketClient类
package com.sqp.example.rpc;
import java.lang.reflect.Proxy;

/**
 * @author sqp
 * @date 2020/7/17 18:22
 */
public class ProxySocketClient {
   // 动态代理 
    public <T> T proxyClient(final Class<T> clazz, String host, int port){
        return (T)Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[] {clazz}, new RemInvocationHandler(host, port));
    }
}
  • RemInvocationHandler类
package com.sqp.example.rpc;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @author sqp
 * @date 2020/7/17 18:33
 */
public class RemInvocationHandler implements InvocationHandler {
    private String host;
    private int port;

    public RemInvocationHandler(String host, int port) {
        this.host = host;
        this.port = port;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        RpcNetTransport rpcNetTransport = new RpcNetTransport(host, port);
        // 封装请求的参数,方法,类型,类名
        RpcRequest rpcRequest = new RpcRequest();
        rpcRequest.setMethodName(method.getName());
        rpcRequest.setClassName(method.getDeclaringClass().getName());
        rpcRequest.setArgs(args);
        rpcRequest.setTypes(method.getParameterTypes());
        return rpcNetTransport.send(rpcRequest);
    }
}
  • RpcNetTransport类
package com.sqp.example.rpc;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

/**
 * @author sqp
 * @date 2020/7/17 18:41
 */
public class RpcNetTransport {
    private String host;
    private int port;

    public RpcNetTransport(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public Socket newSocket() throws IOException {
        return new Socket(host, port);
    }
    
    public Object send(RpcRequest rpcRequest) {
        ObjectInputStream inputStream = null;
        ObjectOutputStream outputStream = null;
        try {
            // 建立连接
            Socket socket = newSocket();
            outputStream = new ObjectOutputStream(socket.getOutputStream());
            outputStream.writeObject(rpcRequest);
            outputStream.flush();

            inputStream = new ObjectInputStream(socket.getInputStream());
            return inputStream.readObject();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }finally {
            if (inputStream != null){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(outputStream != null){
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }
}
  • App类
package com.sqp.example.rpc;

public class App {
    public static void main( String[] args ) {
        ProxySocketClient proxySocketClient = new ProxySocketClient();
        OrderApi orderApi = proxySocketClient.proxyClient(OrderApi.class, "localhost", 8080);
        System.out.println(orderApi.getOrderName("文具盒"));
    }
}

测试结果:

 总结:

  • 客户端建立通信,实现动态代理去获取代理类,将请求的消息内容组装发送到网络通信中,因为只有通过远程通信才能进行服务的调用。
  • 服务端接收客户端请求连接是在serverSocket.accept(),然后对请求的数据进行处理,反序列化,通过拿到的内容进行反射调用。

  虽然这个简易版的RPC有很多局限性,大家不必过多纠结,但是符合我们RPC的通信原理。

  针对上面的可以改造成用spring的注解方式。下期我再修改,这里我感谢下我的MIC老师。

  写的不好大家别喷,大家多包涵,不过有错误希望大家指出来,一起学习,一起快乐。多手写代码记忆更深刻!

posted @ 2020-07-21 15:43  ╭似天涯╯  阅读(271)  评论(0)    收藏  举报