RPC远程过程调用小demo

该博客介绍了如何使用Java的CGlib和JDK动态代理实现AOP,通过Socket通信在客户端和服务端之间进行方法的远程调用和增强。客户端通过注册方法信息,服务端接收并实现这些方法,再将结果返回给客户端。整个过程涉及动态代理、反射、Socket通信和序列化等技术。
摘要由CSDN通过智能技术生成

1.流程简述

客户端通过调用本地增强的方法,将想要增强的方法的信息发送给服务端,服务端解析信息后把方法进行实现,将返回值返回

2.实现

2.0导包

就一个cglib的包

        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>

2.1:客户端实现


public class AOPClient {
    private String ip;
    private int port;

    private final Map<String, Class> map = new HashMap<>();

    public AOPClient(String ip, int port) {
        this.ip = ip;
        this.port = port;
    }

    /**
     * 注册方法
     *
     * @param c 要代理的类
     */
    public void register(Class c) {
        map.put(c.getName(), c);
    }

    /**
     * 注册方法
     *
     * @param name 要代理的类的名字
     * @param c    要代理的类
     */
    public void register(String name, Class c) {
        map.put(name, c);
    }

    /**
     * 远程实现方法
     *
     * @param name 要实现的类名
     * @return 增强后的方法,如果不使用的话不会运行
     * @throws IOException
     */
    public Object send(String name, boolean j2c) throws IOException {
        Class aClass = map.get(name);
        if (aClass == null) {
            throw new RuntimeException("没注册这个方法");
        }
        Socket socket = new Socket(ip, port);
        return j2c ? getProxy(socket, aClass, name) : getProxy2(socket, aClass, name);
    }

    /**
     * 不注册直接进行远端实现
     *
     * @param ip     ip
     * @param port   端口
     * @param aClass 要实现的类名
     * @return 增强后的方法,如果不使用的话不会运行
     * @throws IOException
     */
    public Object send(String ip, int port, Class aClass) throws IOException {
        Socket socket = new Socket(ip, port);
        return getProxy(socket, aClass);
    }

    /**
     * 不注册直接进行远端实现
     *
     * @param ip     ip
     * @param port   端口
     * @param aClass 要实现的类名
     * @param name   给要实现的类名起个名,要和服务端想要调用的相同
     * @return 增强后的方法,如果不使用的话不会运行
     * @throws IOException
     */
    public Object send(String ip, int port, Class aClass, String name) throws IOException {
        Socket socket = new Socket(ip, port);
        return getProxy(socket, aClass);
    }

    /**
     * JDK动态代理
     *
     * @param socket 套接字
     * @param aClass 代理的类
     * @return 增强后的方法,如果不使用的话不会运行
     */
    public Object getProxy(Socket socket, Class aClass) {
        return getProxy(socket, aClass, aClass.getName());
    }

    /**
     * JDK动态代理
     *
     * @param socket 套接字
     * @param aClass 代理的类
     * @param name   代理的类在远端实现的方法的名字
     * @return 代理类实现后的值
     */
    public Object getProxy(Socket socket, Class aClass, String name) {
        return Proxy.newProxyInstance(aClass.getClassLoader(), new Class<?>[]{aClass},
                (proxy, method, args) -> {
                    return WriteAndRead(socket, name, method, args);
                }
        );
    }

    /**
     * cglib动态代理
     *
     * @param socket 套接字
     * @param aClass 代理的类
     * @return 代理类实现后的返回值
     */
    public Object getProxy2(Socket socket, Class aClass) {
        return getProxy2(socket, aClass, aClass.getName());
    }

    /**
     * cglib动态代理
     *
     * @param socket 套接字
     * @param aClass 代理的类
     * @param name   代理的类在远端实现的方法的名字
     * @return 代理类实现后的值
     */
    public Object getProxy2(Socket socket, Class aClass, String name) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(aClass);
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                return WriteAndRead(socket, name, method, objects);
            }
        });
        return enhancer.create();
    }

    /**
     * 两个不同的代理共有的方法
     * 通过socket把要实现的类信息发送并将返回值接收
     *
     * @param socket  套接字
     * @param name    类名
     * @param method  方法
     * @param objects 方法参数s
     * @return 方法运行的返回值
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public Object WriteAndRead(Socket socket, String name, Method method, Object[] objects) throws IOException, ClassNotFoundException {
        ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
        /*把要实现的接口信息发送出去*/
        objectOutputStream.writeObject(new NetData(name, method.getName(), objects, method.getParameterTypes()));
        /*接收对面发送的返回值*/
        Object object = objectInputStream.readObject();
        /*关流,关闭外层流,会自动关闭内层,关流后socket会自动关闭*/
        objectInputStream.close();
        objectOutputStream.close();
        /*将返回值返回*/
        return object;
    }
}

2.2:服务端实现

public class AOPServer {
    private final ServerSocket server;
    private volatile boolean flage = true;
    /*
     * key 接口全路径
     * value 接口对应的实现类
     * */
    private final Map<String, Object> map = new HashMap<>();

    public AOPServer(int port) throws IOException {
        this.server = new ServerSocket(port);
    }

    /**
     * 添加暴露的方法
     *
     * @param o 方法
     */
    public void expose(Object o) {
        //启动后不让改了
        if (flage) {
            String name = o.getClass().getName();
            map.put(name.substring(0, name.length() - 3), o);
        }
    }

    /**
     * 自定义暴露的方法的名称
     *
     * @param name 名称
     * @param o    方法
     */
    public void expose(String name, Object o) {
        map.put(name, o);
    }

    /**
     * 启动
     */
    public void start() throws Exception {
        System.out.println("服务启动了");
        while (flage) {
            Socket socket = server.accept();

            ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
            ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
            //将调用的方法执行并将结果返回
            objectOutputStream.writeObject(enhance(objectInputStream));
            /*关流
             * 当Socket的流关闭时,Socket会自动关闭
             * */
            objectOutputStream.close();
            objectInputStream.close();
            //stop();
        }
    }

    /**
     * 停止
     */
    public void stop() {
        flage = false;
    }

    /**
     * 根据远程发送的信息找到本地的实现,然后执行方法,将返回值返回
     *
     * @param oin 从套接字(Socket)得到的输入流
     * @return 增强的方法的返回值
     */
    public Object enhance(ObjectInputStream oin) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        /*直接读取发送过来的NetData*/
        NetData netData = (NetData) oin.readObject();
        System.out.println("发送过来的数据 = " + netData);
        /*
         * ------------由于是实现的接口,所以方法不会是私有的,即使不是接口,私有方法也不能让其他人随意调用
         * 从map根据接口类名得到实现类
         * */
        Class<?> aClass = map.get(netData.getClassName()).getClass();
        /*反射得到方法*/
        Method method = aClass.getMethod(netData.getFunctionName(), netData.getParameterTypes());
        /*反射执行方法并返回方法的返回值*/
        return method.invoke(map.get(netData.getClassName()), netData.getParameters());
    }
}

2.3:网络中传输的实体类


/**
 * 因为要在网络中传输,所以要实现Serializable接口
 */
public class NetData implements Serializable {
    private String className;
    private String functionName;
    private Object[] parameters;
    private Class<?>[] parameterTypes;

    public String getClassName() {
        return className;
    }

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

    public String getFunctionName() {
        return functionName;
    }

    public void setFunctionName(String functionName) {
        this.functionName = functionName;
    }

    public NetData() {
    }

    /**
     * 存放类的信息
     *
     * @param className      类名
     * @param functionName   代理的方法名
     * @param parameters     参数列表
     * @param parameterTypes 参数的类型列表
     */
    public NetData(String className, String functionName, Object[] parameters, Class<?>[] parameterTypes) {
        this.className = className;
        this.functionName = functionName;
        this.parameters = parameters;
        this.parameterTypes = parameterTypes;
    }

    public Object[] getParameters() {
        return parameters;
    }

    public void setParameters(Object[] parameters) {
        this.parameters = parameters;
    }

    public Class<?>[] getParameterTypes() {
        return parameterTypes;
    }

    public void setParameterTypes(Class<?>[] parameterTypes) {
        this.parameterTypes = parameterTypes;
    }

    @Override
    public String toString() {
        return "NetData{" +
                "className='" + className + '\'' +
                ", functionName='" + functionName + '\'' +
                ", parameters=" + Arrays.toString(parameters) +
                ", parameterTypes=" + Arrays.toString(parameterTypes) +
                '}';
    }
}

2.4:一个待增强的接口和两个(一个半?)实现类

接口:

public interface DataServiceI {
    public String getData(Long i);
}

实现类:

public class DataServiceImpl implements DataServiceI {

    @Override
    public String getData(Long i) {
        return "实现:算术题,两边的时间差" + (System.currentTimeMillis() - i);
    }
}

实现类(伪)

public class DataServiceImplClone {

    public String getData(Long i) {
        return "--实现:你的方法已经被我NTR啦" + new Date(i);
    }
}

2.5 测试类

客户端


public class ClientTest {
    public static void main(String[] args) throws IOException {
        AOPClient aopClient = new AOPClient("127.0.0.1", 8080);
        aopClient.register("asdf", DataServiceI.class);
        aopClient.register(DataServiceI.class);

        //这个是为了测试jdk动态代理和cglib代理非接口的类
        aopClient.register("impl", DataServiceImpl.class);
        //JDK动态代理,自定义名字
        DataServiceI dataServiceI1 = (DataServiceI) aopClient.send("asdf", true);
        //JDK动态代理,类全路径
        DataServiceI dataServiceI2 = (DataServiceI) aopClient.send(DataServiceI.class.getName(), true);
        //cglib动态代理,自定义名字
        DataServiceI dataServiceI3 = (DataServiceI) aopClient.send("asdf", false);
        //cglib动态代理,类全路径
        DataServiceI dataServiceI4 = (DataServiceI) aopClient.send(DataServiceI.class.getName(), false);

        System.out.println("J自定义:" + dataServiceI1.getData(System.currentTimeMillis()));
        System.out.println("J" + dataServiceI2.getData(System.currentTimeMillis()));
        System.out.println("C自定义:" + dataServiceI3.getData(System.currentTimeMillis()));
        System.out.println("C" + dataServiceI4.getData(System.currentTimeMillis()));

        /**
         * 测试jdk和cglib代理非接口的类
         */
        DataServiceI dataServiceI5 = (DataServiceI) aopClient.send("impl", false);
        System.out.println(dataServiceI5.getData(123456789l));
        //这里是jdk动态代理,很明显,代理不成功,因为jdk动态代理就是不能这么干,
        // 不过因为远端代理做的就是实现接口这个活,所以还是用jdk动态代理
        //DataServiceI dataServiceI6 = (DataServiceI) aopClient.send("impl", true);
        //System.out.println(dataServiceI6.getData(987654321l));
    }
}

服务端


public class ServerClient {
    public static void main(String[] args) throws Exception {
        /*在哪个端口启动*/
        AOPServer aopServer = new AOPServer(8080);
        /*
         * 需要暴露哪个方法
         * 这个名字叫什么其实随便,只要另一边知道这个名字是这个类就行
         * 甚至还可以更过分,只要你这边的方法名、参数列表、参数类型和那边传过来的相同,随便你用哪个类
         * */
        aopServer.expose("ServiceI.DataServiceI", new DataServiceImpl());
        aopServer.expose("asdf", new DataServiceImplClone());
        aopServer.expose("impl",new DataServiceImplClone());
        /*启动*/
        aopServer.start();
    }
}

3.测试

客户端:
在这里插入图片描述
服务端
在这里插入图片描述

4.总结

用到的东西还是挺多的
动态代理
反射
Socket
序列化

实现这个的重点就是在代理时把方法偷梁换柱换成连接远端取得结果就行了
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值