Java实现简单的RPC框架

1、RPC简介

RPC,全称为Remote Procedure Call,即远程过程调用,它是一个计算机通信协议。它允许像调用本地服务一样调用远程服务。它可以有不同的实现方式。如RMI(远程方法调用)、Hessian、Http invoker等。另外,RPC是与语言无关的。
Java实现简单的RPC框架_第1张图片

2、Java实现RPC框架

2.1、实现技术方案

 下面使用比较原始的方案实现RPC框架,采用Socket通信、动态代理与反射与Java原生的序列化。

2.2、RPC框架架构

RPC架构分为三部分:

1)服务提供者,运行在服务器端,提供服务接口定义与服务实现类。

2)服务中心,运行在服务器端,负责将本地服务发布成远程服务,管理远程服务,提供给服务消费者使用。

3)服务消费者,运行在客户端,通过远程代理对象调用远程服务。

3、 具体实现
Java实现简单的RPC框架_第2张图片
服务接口定义类
HelloProvider.java

package org.rpc.provider;

/**   
 * @ClassName:  HelloProvider   
 * @Description:TODO(对外提供接口)   
 * @author: Jimu 
 * @email:  [email protected] 
 * @date:   2019年3月25日 下午2:12:59   
 *     
 * @Copyright: 2019 www.maker-win.net Inc. All rights reserved. 
 *  
 */
public interface HelloProvider {
   String sayHello(String name);
   Person getPerson(String name);
}

公共网络通信类
NetModel.java

/**  
 * All rights Reserved, Designed By www.tydic.com
 * @Title:  NetModel.java   
 * @Package org.rpc.provider   
 * @Description:    TODO(用一句话描述该文件做什么)   
 * @author: Jimu
 * @email:  [email protected] 
 * @date:   2019年3月25日 下午2:14:05   
 * @version V1.0 
 * @Copyright: 2019 www.maker-win.net Inc. All rights reserved. 
 */
package org.rpc.provider;
import java.io.Serializable;
import java.util.Arrays;
/**   
 * @ClassName:  NetModel   
 * @Description:TODO(公共网络通信类,通过序列化该类,将客户端调用接口、方法、参数、参数类型封装,然后服务端反序列化,再通过反射,调取相应实现类的方法。)   
 * @author: Jimu 
 * @email:  [email protected] 
 * @date:   2019年3月25日 下午2:14:05   
 *     
 * @Copyright: 2019 www.maker-win.net Inc. All rights reserved. 
 *  
 */
 
public class NetModel implements Serializable{


    private static final long serialVersionUID = 1L;

    private String className; //接口名

    private String method; //方法名

    private  Object[] args ; //参数

    private String[] types; //参数类型

    public String getClassName() {
        return className;
    }

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

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method;
    }

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

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


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

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

    @Override
    public String toString() {
        return "NetModel [className=" + className + ", method=" + method + ", args=" + Arrays.toString(args)
                + ", types=" + Arrays.toString(types) + "]";
    }
}

序列化工具类
SerializeUtils.java

/**  
 * All rights Reserved, Designed By www.tydic.com
 * @Title:  SerializeUtils.java   
 * @Package org.rpc.provider   
 * @Description:    TODO(用一句话描述该文件做什么)   
 * @author: Jimu
 * @email:  [email protected] 
 * @date:   2019年3月25日 下午2:14:22   
 * @version V1.0 
 * @Copyright: 2019 www.maker-win.net Inc. All rights reserved. 
 */
package org.rpc.provider;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**   
 * @ClassName:  SerializeUtils   
 * @Description:TODO(序列化工具类)   
 * @author: Jimu 
 * @email:  [email protected] 
 * @date:   2019年3月25日 下午2:14:22   
 *     
 * @Copyright: 2019 www.maker-win.net Inc. All rights reserved. 
 *  
 */
public class SerializeUtils {

    /**
     * 序列化
     * @param object
     * @return
     * @throws IOException
     */
    public static byte[] serialize(Object object) throws IOException {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        ObjectOutputStream outputStream = new ObjectOutputStream(os);
        outputStream.writeObject(object);
        outputStream.flush();
        byte[] byteArray = os.toByteArray();    
        outputStream.close();
        os.close();
        return byteArray;
    }
    /**
     * 反序列化
     * @param buf
     * @return
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public static Object deSerialize(byte[] buf) throws IOException, ClassNotFoundException {
        ByteArrayInputStream is = new ByteArrayInputStream(buf);
        ObjectInputStream inputStream = new ObjectInputStream(is);
        Object object =  inputStream.readObject();
        inputStream.close();
        is.close();
        return object;
    }
}


测试实体类
Person.java

/**  
 * All rights Reserved, Designed By www.tydic.com
 * @Title:  Person.java   
 * @Package org.rpc.provider   
 * @Description:    TODO(用一句话描述该文件做什么)   
 * @author: Jimu
 * @email:  [email protected] 
 * @date:   2019年3月25日 下午2:13:51   
 * @version V1.0 
 * @Copyright: 2019 www.maker-win.net Inc. All rights reserved. 
 */
package org.rpc.provider;
import java.io.Serializable;

/**   
 * @ClassName:  Person   
 * @Description:TODO(测试实体类)   
 * @author: Jimu 
 * @email:  [email protected] 
 * @date:   2019年3月25日 下午2:13:51   
 *     
 * @Copyright: 2019 www.maker-win.net Inc. All rights reserved. 
 *  
 */
//普通公共bean
public class Person implements Serializable{

    private static final long serialVersionUID = 1L;
    private String name;
    private Integer age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }

}

服务中心
HelloServiceImpl.java

/**  
 * All rights Reserved, Designed By www.tydic.com
 * @Title:  HelloServiceImpl.java   
 * @Package org.rpc.server   
 * @Description:    TODO(用一句话描述该文件做什么)   
 * @author: Jimu
 * @email:  [email protected] 
 * @date:   2019年3月25日 下午2:16:02   
 * @version V1.0 
 * @Copyright: 2019 www.maker-win.net Inc. All rights reserved. 
 */
package org.rpc.server;

import org.rpc.provider.HelloProvider;
import org.rpc.provider.Person;

/**   
 * @ClassName:  HelloServiceImpl   
 * @Description:TODO(接口实现类)   
 * @author: Jimu 
 * @email:  [email protected] 
 * @date:   2019年3月25日 下午2:16:02   
 *     
 * @Copyright: 2019 www.maker-win.net Inc. All rights reserved. 
 *  
 */
public class HelloServiceImpl implements HelloProvider{

	/**   
	 * 

Title: sayHello

*

Description:

* @param name * @return * @see org.rpc.provider.HelloProvider#sayHello(java.lang.String) */ public String sayHello(String name) { return "hello,"+name; } /** *

Title: getPerson

*

Description:

* @param name * @return * @see org.rpc.provider.HelloProvider#getPerson(java.lang.String) */ public Person getPerson(String name) { Person person = new Person(); person.setName(name); person.setAge(25); return person; } }

Server服务端 rpc-server
RPCServer.java

/**  
 * All rights Reserved, Designed By www.tydic.com
 * @Title:  RPCServer.java   
 * @Package org.rpc.server   
 * @Description:    TODO(用一句话描述该文件做什么)   
 * @author: Jimu
 * @email:  [email protected] 
 * @date:   2019年3月25日 下午2:20:42   
 * @version V1.0 
 * @Copyright: 2019 www.maker-win.net Inc. All rights reserved. 
 */
package org.rpc.server;

import java.io.FileInputStream;
/**   
 * @ClassName:  RPCServer   
 * @Description:TODO(这里用一句话描述这个类的作用)   
 * @author: Jimu 
 * @email:  [email protected] 
 * @date:   2019年3月25日 下午2:20:42   
 *     
 * @Copyright: 2019 www.maker-win.net Inc. All rights reserved. 
 *  
 */
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import org.rpc.provider.NetModel;
import org.rpc.provider.SerializeUtils;

//Server服务端,这里使用JDK自带的ServerSocket来进行进行,服务端收到数据后,对数据进行处理,处理完把结构返回给客户端
public class RPCServer {

    public static void main(String[] args) 
    { 
        try {
            openServer();
        } catch (Exception e) {
            e.printStackTrace();
        }   
    }
    //这个方法用来启动服务端,然后接受数据,返回处理完的结果
    public static void openServer() throws IOException  {
        ServerSocket serverSocket =  new ServerSocket(9999); 
        try { 
            System.out.println("服务开启");
            while(true) {
                Socket socket = serverSocket.accept();
                System.out.println(socket.getInetAddress()+"-connected");  
                InputStream in = socket.getInputStream();
                byte[] buf = new byte[1024];  
                in.read(buf);  
                byte[] formatDate = formatData(buf);
                OutputStream out = socket.getOutputStream();  
                out.write(formatDate);  
                socket.close();  
            }
        } catch (Exception e) {
            e.printStackTrace();
            serverSocket.close();
        }
    }
    /*这个方法用来处理接收到的数据,通过反序列化得到传过来的通信类NetModel,然后得到接口名,方法名,参数,参数类型,
    最后通过JDK反射,来调取实现类的方法,并将调取结果,序列化为byte数组,然后返回
    */
    public static byte[] formatData(byte[] bs){
        try {
            //将收到的byte数组反序列化为NetModel类型,然后通过反射调用HelloServiceImpl实现类的方法
            NetModel netModel = (NetModel)SerializeUtils.deSerialize(bs);
            String className = netModel.getClassName();
            String[] types = netModel.getTypes();
            Object[] args = netModel.getArgs();

            /*这里简单通过Map来做接口映射到实现类,从map中取
            Map map = new HashMap();
            map.put("org.Simple.API.HelloService", "org.Simple.S.HelloServiceImpl");
            Class clazz = Class.forName(map.get(className)); 
            */ 
            //也可以把这个键值放到配置文件下,通过配置文件读取
            Class clazz = Class.forName(getPropertyValue(className));
            Class [] typeClazzs = null;
            if(types!=null) {
                typeClazzs = new Class[types.length];
                for (int i = 0; i < typeClazzs.length; i++) {
                    typeClazzs[i] = Class.forName(types[i]);
                }
            }
            Method method = clazz.getMethod(netModel.getMethod(),typeClazzs);
            Object object = method.invoke(clazz.newInstance(), args);
            byte[] byteArray = SerializeUtils.serialize(object);
            return byteArray;

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    //通过key去properties文件中取值
    public static String getPropertyValue(String key) throws IOException {
        Properties properties = new Properties();
        FileInputStream in = new FileInputStream("src/main/resources/config.properties");
        properties.load(in);
        in.close();
        return properties.getProperty(key);
    }
    }

config.properties

org.rpc.provider.HelloProvider=org.rpc.server.HelloServiceImpl

RPC客户端rpc-client
RPCClient.java

/**  
 * All rights Reserved, Designed By www.tydic.com
 * @Title:  RPCClient.java   
 * @Package org.rpc.client   
 * @Description:    TODO(用一句话描述该文件做什么)   
 * @author: Jimu
 * @email:  [email protected] 
 * @date:   2019年3月25日 下午2:22:14   
 * @version V1.0 
 * @Copyright: 2019 www.maker-win.net Inc. All rights reserved. 
 */
package org.rpc.client;

/**   
 * @ClassName:  RPCClient   
 * @Description:TODO(这里用一句话描述这个类的作用)   
 * @author: Jimu 
 * @email:  [email protected] 
 * @date:   2019年3月25日 下午2:22:14   
 *     
 * @Copyright: 2019 www.maker-win.net Inc. All rights reserved. 
 *  
 */

import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

import org.rpc.provider.HelloProvider;
import org.rpc.provider.SerializeUtils;


/*RPC客户端,也是用Socket与服务端进行通信*/
public class RPCClient {
      //Socket发送消息给服务端,并反序列化服务端返回的数据,返回给方法调用者
     public static Object send(byte[] bs)  {
        try {
            Socket socket = new Socket("127.0.0.1", 9999);
            OutputStream outputStream = socket.getOutputStream();
            outputStream.write(bs);  
            InputStream in = socket.getInputStream();  
            byte[] buf = new byte[1024];
            in.read(buf);
            Object formatDate = SerializeUtils.deSerialize(buf);
            socket.close();  
            return formatDate;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    //main方法,运行客户端
    public static void main(String[] args) {
        HelloProvider helloService = ProxyFactory.getInstance(HelloProvider.class);
        System.out.println("say:"+helloService.sayHello("zhangsan"));
        System.out.println("Person:"+helloService.getPerson("zhangsan"));

    }
}

ProxyFactory.java

/**  
 * All rights Reserved, Designed By www.tydic.com
 * @Title:  ProxyFactory.java   
 * @Package org.rpc.client   
 * @Description:    TODO(用一句话描述该文件做什么)   
 * @author: Jimu
 * @email:  [email protected] 
 * @date:   2019年3月25日 下午2:23:50   
 * @version V1.0 
 * @Copyright: 2019 www.maker-win.net Inc. All rights reserved. 
 */
package org.rpc.client;

/**   
 * @ClassName:  ProxyFactory   
 * @Description:TODO(代理工厂类)   
 * @author: Jimu 
 * @email:  [email protected] 
 * @date:   2019年3月25日 下午2:23:50   
 *     
 * @Copyright: 2019 www.maker-win.net Inc. All rights reserved. 
 *  
 */
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.rpc.provider.NetModel;
import org.rpc.provider.SerializeUtils;


public class ProxyFactory {

    private static InvocationHandler handler = new InvocationHandler() {

        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

            NetModel netModel = new NetModel();

            Class[] classes = proxy.getClass().getInterfaces();
            String className = classes[0].getName();

            netModel.setClassName(className);
            netModel.setArgs(args);
            netModel.setMethod(method.getName());

            String [] types = null; 
            if(args!=null) {
                types = new String [args.length];
                for (int i = 0; i < types.length; i++) {
                    types[i] = args[i].getClass().getName();
                }
            }
            netModel.setTypes(types);

            byte[] byteArray = SerializeUtils.serialize(netModel);
            Object send = RPCClient.send(byteArray);
            return send;
        }
    };

    @SuppressWarnings("unchecked")
    public static  T getInstance(Class clazz) {
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), 
                new Class[]{clazz}, handler );
    }
}


启动Server服务端

Java实现简单的RPC框架_第3张图片
启动RPC客户端
Java实现简单的RPC框架_第4张图片


参考链接
https://www.cnblogs.com/codingexperience/p/5930752.html
https://blog.csdn.net/u013034378/article/details/80686233

你可能感兴趣的:(RPC)