手写RPC第一版

前言

通过学习,将自己掌握的的技术记录下来,以便后面学习。

创建项目

创建两个项目,一个用户系统user-server,一个商品系统goods-server。系统goods-server系统有两个Module分别是goods-api(商品系统的接口模块),goods-provider(提供具体的服务)。user-server和goods-server分别部署到不同的服务器上,模拟goods-server服务中提供一个IGoodsService接口,接口中描述两个方法,queryGoodsList(),queryGoodsById(String id)。user-server服务通过socket远程调用GoodsServer中的api。
接下来我们先写goods-server这个服务

goods-api

创建一个RpcRequest类,该类作为user-server和goods-server两个服务间通过Socket远程调用时传输的数据,所以实现Serializable,序列化及反序列化。

import java.io.Serializable;
import java.util.Arrays;

/**
 * 用于socket传输数据,所以要实现Serializable接口。
 */
public class RpcRequest implements Serializable {

    // 类名称
    private String className;
    // 方法名称
    private String methodName;
    // 方法参数
    private Object[] args;
    // 参数类型
    private Class[] types;

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

    public Object[] getArgs() {
        return args;
    }

    public void setArgs(Object[] args) {
        this.args = args;
    }

    public Class[] getTypes() {
        return types;
    }

    public void setTypes(Class[] types) {
        this.types = types;
    }

    @Override
    public String toString() {
        return "RpcRequest{" +
                "className='" + className + '\'' +
                ", methodName='" + methodName + '\'' +
                ", args=" + Arrays.toString(args) +
                ", types=" + Arrays.toString(types) +
                '}';
    }
}

创建一个接口,用于客户端调用。

/**
 * 用户客户端调用的api
 */
public interface IGoodsService {

    String queryGoodsList();

    String queryGoodsById(String id);
}

goods-provider

创建一个GoodsServiceImpl,实现IGoodsService中的方法

public class GoodsServiceImpl implements IGoodsService {
    @Override
    public String queryGoodsList() {
        return "EXECUTE QUERY_LIST METHOD";
    }

    @Override
    public String queryGoodsById(String id) {
        return "EXECUTE QUERY_INFO METHOD";
    }
}

创建一个RpcProxyServer类,用来提供发布服务的方法publisher(),其中正常的Socket通信,会产生IO阻塞(客户端A调用服务,处于阻塞状态时,客户端B是无法调用服务的),所以第一版本采用线程池的方式,去提高服务端处理多客户端请求的性能。ProcessHandler这个类用来处理服务端接收到的请求以及返回客户端的数据。

public class RpcProxyServer {

    private final ExecutorService executorService = Executors.newCachedThreadPool();

    public void publisher(Object service, int port) {
        ServerSocket serverSocket = null;
        try {
            serverSocket = new ServerSocket(port);// 阻塞

            while (true) {
                // 获得Socket对象
                Socket socket = serverSocket.accept();
                // 通过线程池提供服务处理客户端远程调用的能力
                executorService.execute(new ProcessHandler(service, socket));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

ProcessHandler 类在run方法中通过ObjectInputStream获取客户端请求的参数RpcRequest,在通过invoke方法反射调用服务端的服务,在通过ObjectOutputStream将result返回给客户端。

public class ProcessHandler implements Runnable {

    private Object service;
    private Socket socket;

    public ProcessHandler(Object service, Socket socket) {
        this.service = service;
        this.socket = socket;
    }

    @Override
    public void run() {
        ObjectInputStream inputStream = null;
        ObjectOutputStream outputStream = null;

        try {
            // 读取客户端请求过来的数据
            inputStream = new ObjectInputStream(socket.getInputStream());
            RpcRequest request = (RpcRequest) inputStream.readObject();
            // 根据客户端请求参数调用服务端具体的服务
            Object result = invoke(request);
			System.out.println("服务端处理的结果:" + result);

            // 将服务端返回的数据写会给客户端
            outputStream = new ObjectOutputStream(socket.getOutputStream());
            outputStream.writeObject(result);
            outputStream.flush();

        } catch (Exception 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();
                }
            }
        }
    }

    // 根据客户端请求参数调用服务端具体的服务
    private Object invoke(RpcRequest request) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        // 获取请求的类名
        String className = request.getClassName();
        // 获得具体的类
        Class<?> clazz = Class.forName(className);
        // 通过方法名称及参数类型,获得具体的方法
        Method method = clazz.getMethod(request.getMethodName(), request.getTypes());
        // 通过反射调用方法
        return method.invoke(service, request.getArgs());
    }
}

user-server

接下来我们先写user-server这个服务
首先写一个动态代理类,提供clientProxy方法,该方法需要传入客户端Socket连接的Ip,和端口,以及要代理哪个类

public class RpcProxyClient {

    public <T> T clientProxy(final Class<T> interfaceCls, final String host, final int port) {
        return (T) Proxy.newProxyInstance(interfaceCls.getClassLoader(), new Class<?>[] {interfaceCls}, new RemoteInvocationHandler(host, port));
    }
}

代理对象的具体操作在RemoteInvocationHandler中去实现。首先组装RpcRequest参数,然后通过RpcNetTransport远程调用服务端

public class RemoteInvocationHandler implements InvocationHandler {
    private String host;
    private int port;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 组装RpcRequest
        RpcRequest request = new RpcRequest();
        request.setClassName(method.getDeclaringClass().getName());
        request.setMethodName(method.getName());
        request.setArgs(args);
        request.setTypes(method.getParameterTypes());
        // 调用远程服务
        RpcNetTransport netTransport = new RpcNetTransport(host, port);
        Object result = netTransport.send(request);
        return result;
    }
}

RpcNetTransport 提供一个newSocket()方法,通过host,port获取Socket连接。然后提供send()方法,远程调用服务端。

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 {
        Socket socket = new Socket(host, port);
        return socket;
    }

    public Object send(RpcRequest request) {
        ObjectOutputStream outputStream = null;
        ObjectInputStream inputStream = null;

        try {
            // 获得Socket连接
            Socket socket = newSocket();

            // 向服务器写数据
            outputStream = new ObjectOutputStream(socket.getOutputStream());
            outputStream.writeObject(request);
            outputStream.flush();

            // 读取服务器返回数据
            inputStream = new ObjectInputStream(socket.getInputStream());
            Object result = inputStream.readObject();
            return result;

        } catch (IOException e) {
            e.printStackTrace();
        } catch (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;
    }
}

服务端测试类

public class App {
    public static void main( String[] args ) {
        IGoodsService goodsService = new GoodsServiceImpl();
        RpcProxyServer server = new RpcProxyServer();
        server.publisher(goodsService, 8080);
    }
}

客户端测试类

public class App {
    public static void main( String[] args ) {
        RpcProxyClient client = new RpcProxyClient();
        IGoodsService goodsService = client.clientProxy(IGoodsService.class, "localhost", 8080);
        System.out.println(goodsService.queryGoodsById("test"));
        System.out.println(goodsService.queryGoodsList());
        System.out.println(goodsService.queryGoodsList());
        System.out.println(goodsService.queryGoodsList());
        System.out.println(goodsService.queryGoodsList());
        System.out.println(goodsService.queryGoodsList());
        System.out.println(goodsService.queryGoodsList());
        System.out.println(goodsService.queryGoodsList());
    }
}

服务端打印结果:

服务端处理的结果:EXECUTE QUERY_INFO METHOD
服务端处理的结果:EXECUTE QUERY_LIST METHOD
服务端处理的结果:EXECUTE QUERY_LIST METHOD
服务端处理的结果:EXECUTE QUERY_LIST METHOD
服务端处理的结果:EXECUTE QUERY_LIST METHOD
服务端处理的结果:EXECUTE QUERY_LIST METHOD
服务端处理的结果:EXECUTE QUERY_LIST METHOD
服务端处理的结果:EXECUTE QUERY_LIST METHOD

客户端打印结果:

EXECUTE QUERY_INFO METHOD
EXECUTE QUERY_LIST METHOD
EXECUTE QUERY_LIST METHOD
EXECUTE QUERY_LIST METHOD
EXECUTE QUERY_LIST METHOD
EXECUTE QUERY_LIST METHOD
EXECUTE QUERY_LIST METHOD
EXECUTE QUERY_LIST METHOD

最后

本文是自己学习后,变成自己的知识,默写出来的。主要是用来以后自己查看方便使用。下篇文章将基于这个版本实现通过注解的方式调用和发布服务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值