手写简易RPC框架

    分布式技术的本质可以简单地概括为两部分—— 任务拆分 + RPC

    1. 将一个任务流程拆分成几个部分,需要扛压的部分做成集群

    2. RPC调用 (特别是通信)

    再概括点说分布式的本质就是RPC,也不算过分。

 

    我们手写一个RPC。


一、 RPC框架

    以本次Demo代码为例, 讲解下RPC框架和流程。   

Server端通过publish()发布一个可用的计算服务CalculateService,告诉别人我可以提供计算服务。

1.  Client端说:“我要计算!” 它通过动态代理技术得到Server端发布的CalculateService服务的动态代理,就可以调用Server端的计算服务了,然后它说要算一个 “ 3 + 8 ” 。(因为网络只能传二进制,所以它们说话就要经过序列化和反序列化来转换

2.  Server端接收Client端发送来的计算请求信息,然后调用加法功能算出 3 + 8 = 11,将结果返回给Client端。

3. Client端接收到返回的结果11,一次RPC调用就愉快地完成了。


三、 代码

1. Server端

(1)声明计算服务接口

package rpcserver;

public interface CalculateService {
    public int add(int param1, int param2);
    public int minus(int param1, int parm2);
    public int multi(int param1, int param2);
    public int divide(int param1, int param2);
}

(2)计算服务实现,真正用来干活的部分

package rpcserver;

public class CalculateServiceImpl implements CalculateService {
    @Override
    public int add(int param1, int param2) {
        return param1 + param2;
    }

    @Override
    public int minus(int param1, int param2) {
        return param1 - param2;
    }

    @Override
    public int multi(int param1, int param2) {
        return param1 * param2;
    }

    @Override
    public int divide(int param1, int param2) {
        if (0 == param2){
            System.out.println("invalid param " + param2);
            return 0;
        }
        return param1/param2;
    }
}

(3)封装请求类  (为了方便信息发送和演示序列化)

package rpcserver;

import java.io.Serializable;

public class Request implements Serializable {
    private static final long serialVersionUID = 123456L;
    private String methodName;
    private Class<?>[] argTypes;
    private Object args;

    public Request(String methodName, Object args) {
        this.methodName = methodName;
        this.args = args;
    }

    public String getMethodName() {
        return methodName;
    }

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

    public Class<?>[] getArgeTypes() {
        return argTypes;
    }

    public void setArgeTypes(Class<?>[] argeTypes) {
        this.argTypes = argeTypes;
    }

    public Object getArgs() {
        return args;
    }

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

(4)Server端主框架,用来接收Client端信息和调用本地服务

package rpcserver;

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.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ServerProcedure {
    private static ExecutorService pool = Executors.newFixedThreadPool(10);

    public static void publish(Object service, int port){
        try {
            ServerSocket serverSocket = new ServerSocket(port);
            Socket socket = null;
            while (true){
                //不断获得socket连接
                socket = serverSocket.accept();
                pool.execute(new Handler(service, socket));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            pool.shutdown();
        }
    }

    private static class Handler implements Runnable{
        private Object service;
        private Socket socket;

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

        @Override
        public void run() {
            ObjectInputStream ois = null;
            ObjectOutputStream oos = null;
            try {
                //get参数,反序列化
                ois = new ObjectInputStream(socket.getInputStream());

                //调用
                Object result = doInvoke(ois.readObject());
                //返回结果
                oos = new ObjectOutputStream(socket.getOutputStream());
                oos.writeObject(result);
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }finally {
                try {
                    ois.close();
                    oos.close();
                    if (null != socket){
                        socket.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        private Object doInvoke(Object request){
            Request req = (Request)request;
            String methodName = req.getMethodName();
            Class<?>[] argeTypes = req.getArgeTypes();
            Object[] args = (Object[]) req.getArgs();

            try {
                Method method = service.getClass().getMethod(methodName, argeTypes); //利用反射取得method
                Object result = method.invoke(service, args);
                return result;
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
            return null;
        }
    }
}

(5)Server端main方法

package rpcserver;

public class ServerDemo {
    public static void main(String[] args) {
        CalculateService cal = new CalculateServiceImpl();
        ServerProcedure.publish(cal, 8888);
    }
}

 

2. Client端

(1)计算服务接口声明(其实这些应写在三方包里)

package rpcserver;

public interface CalculateService {
    int add(int param1, int param2);
    int minus(int param1, int parm2);
    int multi(int param1, int param2);
    int divide(int param1, int param2);
}

(2)请求类

package rpcserver;

import java.io.Serializable;

public class Request implements Serializable {
    private static final long serialVersionUID = 123456L;
    private String methodName;
    private Class<?>[] argTypes;
    private Object args;

    public Request(String methodName, Object args) {
        this.methodName = methodName;
        this.args = args;
    }

    public String getMethodName() {
        return methodName;
    }

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

    public Class<?>[] getArgeTypes() {
        return argTypes;
    }

    public void setArgeTypes(Class<?>[] argeTypes) {
        this.argTypes = argeTypes;
    }

    public Object getArgs() {
        return args;
    }

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

(3)生成动态代理和与Server端交互信息

package rpcserver;

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.Socket;

public class ClientProxy {
    private static String serverHost;
    private static int serverPort;

    private static class Handler implements InvocationHandler{
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            ObjectOutputStream oos = null;
            ObjectInputStream ois = null;
            Socket socket = null;
            Object result = null;

            //serialize
            Request request = new Request(method.getName(), args);
            request.setArgeTypes(method.getParameterTypes());

            try {
                //get socket, send request
                socket = new Socket(serverHost, serverPort);
                oos = new ObjectOutputStream(socket.getOutputStream());
                oos.writeObject(request);

                //get result
                ois = new ObjectInputStream(socket.getInputStream());
                result = ois.readObject();
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                oos.close();
                ois.close();
                if (null != socket){
                    socket.close();
                }
            }
            return result;
        }
    }


    public static <T> T  getProxy(Class<T> classValue, String host, int port){
        serverHost = host;
        serverPort = port;

        Object proxy = Proxy.newProxyInstance(classValue.getClassLoader(), new Class[]{classValue}, new Handler());
        return (T) proxy;
    }
}

(4)Client端main方法

package rpcserver;

public class ClientDemo {
    public static void main(String[] args) {
        CalculateService proxy = ClientProxy.getProxy(CalculateService.class, "localhost", 8888);
        Object result = proxy.add(8, 3);
        System.out.println("the result = " + result.toString());
    }
}

调用结果

 

  其实Client端也可以不用写动态代理,直接将方法名和参数以字符串的格式发送到Server端也能解析还原,但是需要手动封装方法名和参数,不如使用动态代理直接调用方便。

 

 

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值