Java简单实现RPC原理

Java简单实现RPC原理

RPC(Remote Procedure Call)远程过程调用,简单的理解是一个节点(A)通过网络向另一个节点(B)请求服务

实现的基本思路

首先,我们需要一个Client端(A节点)和一个Server端(B节点)进行远程的通信
其次,我们需要在两个节点建立统一的接口(相当于约定好,我们需要生产哪些方法和消费哪些方法)
然后,我们需要利用流完成对象在网络中的序列化和反序列化,以及利用反射技术(最重要的)完成对方法的调用
最后,特别感谢马士兵老师对RPC的讲解。
现在就让我们动手做起来

项目结构

在这里插入图片描述

各模块结构

1.模块rpcApi定义了接口
在这里插入图片描述
实体类

/**
 * @description: User
 * @author: dyy
 * @date: 2020/7/7 23:04
 * @Version: 1.0
 */
@Data
public class User implements Serializable {
    private static final long serialVersionUID = 1L; 
    private String name;
    private int age;
    private String address;

    public User(String name, int age, String address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }
    public User(){
    }
}

接口代码

/**
 * @description: IUserService
 * @author: dyy
 * @date: 2020/7/7 23:05
 * @Version: 1.0
 */
public interface IUserService {
    /**
     * 根据ID查询User
     * @param id
     * @return
     */
    User findUserById(int id);
    /**
     * 修改客户信息
     * @param name
     * @return
     */
    User updateUserById(String name);
}

2.模块rpcServer通过网络对外提供服务
maven 只依赖了接口
在这里插入图片描述
mapper.properties文件
在这里插入图片描述
这里应该是访问数据库进行增删改查,为了简化问题只做简单实现

/**
 * @description: Server端实现接口
 * @author: dyy
 * @date: 2020/7/7 23:10
 * @Version: 1.0
 */
public class UserServiceImpl implements IUserService {
    @Override
    public User findUserById(int id) {
        return new User("小明",id,"");
    }

    @Override
    public User updateUserById(String name) {
        return new User(name,101,"");
    }
}

对外提供网络服务类

/**
 * @description:
 * @author: dyy
 * @date: 2020/7/7 23:14
 * @Version: 1.0
 */
public class NetService {
    /**
     * mapper维护了服务端提供接口访问的实现类映射
     * 例如 com.dyy.api.IUserService=com.dyy.service.UserServiceImpl
     * 当我们从网络中获取需要访问的接口时,根据映射关系找到对应实现类
     */
    private static Properties mapper = new Properties();;
    static{
        try (InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream("mapper.properties")){
            if( null != in && in.available() > 0){
                mapper.load(in);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static void startService()throws Exception{
        System.out.println("启动服务");
        try(
            //对外提供8888端口服务
            ServerSocket serverSocket = new ServerSocket(8888);
            Socket socket = serverSocket.accept();
            //获取网络输入流
            InputStream in = socket.getInputStream();
            //ObjectOutputStream ObjectInputStream 序列化和反序列化的效率不高,后期可优化
            ObjectInputStream ois = new ObjectInputStream(in);
            //获取网络输入流
            OutputStream out = socket.getOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(out);
        ){
            System.out.println("服务接收请求");
            //获取类名
            String clazzName = ois.readUTF();
            /**
             * 因为重载的存在,
             * 所以使用反射时,需要知道方法名称和参数类型
             */
            //获取方法名
            String methodName = ois.readUTF();
            //获取方法参数类型
            Class<?>[] parameterTypes = (Class<?>[]) ois.readObject();
            //获取方法实参
            Object[] args = (Object[]) ois.readObject();

            //获取接口与实现类的映射关系
            clazzName = mapper.getProperty(clazzName);
            Class clazz = Class.forName(clazzName);

            Method method = clazz.getMethod(methodName, parameterTypes);
            //方法调用
            Object object = method.invoke(clazz.newInstance(), args);
            //写出返回值
            oos.writeObject(object);
            oos.flush();
            System.out.println("服务完成");
        }
    }
    public static void main(String[] args) throws Exception {
    		//启动服务
            startService();
    }
}

3.模块rpcClient通过调用接口完成远程访问请求服务
maven 只依赖了接口
模块结构如下:在这里插入图片描述
Stub类封装了网络通信和调用的接口细节

/**
 * @description:
 * @author: dyy
 * @date: 2020/7/7 23:30
 * @Version: 1.0
 */
public class Stub {
    public static <T> T getStub(Class<?>... interfaces) {
        return (T)Proxy.newProxyInstance(
                Stub.class.getClassLoader(),interfaces
                , new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) {
                        String clazzName = method.getDeclaringClass().getName();
                        String methodName = method.getName();
                        Class<?>[] parameterTypes = method.getParameterTypes();
                        try(
                            //向远程请求连接    
                            Socket socket = new Socket("127.0.0.1", 8888);
                            OutputStream os = socket.getOutputStream();
                            ObjectOutputStream oos = new ObjectOutputStream(os);
                            InputStream in = socket.getInputStream();
                            ObjectInputStream ois = new ObjectInputStream(in);
                        ){
                            //发送接口名称
                            oos.writeUTF(clazzName);
                            //发送方法名称
                            oos.writeUTF(methodName);
                            //发送方法形参类型列表
                            oos.writeObject(parameterTypes);
                            //发送方法实参
                            oos.writeObject(args);
                            oos.flush();
                            //读取远程返回的数据
                            return ois.readObject();
                        }catch(Exception e){
                            e.printStackTrace();
                        }
                        return null;
                    }
                }
        );
    }
}

Client调用接口抽象方法,具体如何做交给Stub中间件完成

/**
 * @description:
 * @author: dyy
 * @date: 2020/7/8 9:55
 * @Version: 1.0
 */
public class Client {
    public static void main(String[] args) {
        IUserService service = Stub.getStub(IUserService.class);
        User user = service.updateUserById("大牛");
        System.out.println(user);
    }
}

程序演示:

在这里插入图片描述
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值