Java Socket实现简单的RPC

一、RPC

        RPC(Remote Procedure Call)——远程过程调用。两台服务器A、B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,由于不在一个内存空间,不能之间调用,需要通过网络来表达调用语义和传达调用的数据。

        实现RPC步骤:1.客户端生成动态代理类;2.代理类发送请求;3.服务端接受请求;4.服务端处理请求;5.服务器发送处理结果给客户端。

二、项目结构图

有三个包:api、clien、server

三、api包下的IHell、NetModel、Person

IHello是接口,有方法sayHello(),getPerson()。IHello的实现类在服务端。

package api;

public interface IHello {

    String sayHello(String name);

    Person getPerson(String name);

}

NetModel类用于记录请求的类名,方法名,参数。需要实现序列化,把客户端请求的信息包装到NetModel中。

package api;

import java.io.Serializable;

public class NetModel implements Serializable {

    private static final long serialVersionUID = -4712967162106730325L;     //序列化UID

    private String className;           //类名

    private String methodName;          //方法名

    private Object[] args;              //参数

//    无参构造器
    public NetModel() {

    }

    public NetModel(String className, String methodName, Object[] args) {
        this.className = className;
        this.methodName = methodName;
        this.args = args;
    }

    //    set、get方法
    public static long getSerialVersionUID() {
        return serialVersionUID;
    }

    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;
    }
}

Person需要序列化,用于网络传输。

package api;

import java.io.Serializable;

public class Person implements Serializable {

    private static final long serialVersionUID = -9057543404870272869L;     //生成序列化UID

    private int age;        //年龄

    private String name;    //姓名

//    无参构造器
    public Person(){

    }

//    有参构造器
    public Person(int age, String name){
        this.age = age;
        this.name = name;
    }

//    set、get方法
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

//    toString方法
    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

四、client包下的ProxyFactory、RPCClient

ProxyFactory生成动态代理类。动态代理类每次调用方法,都会走invoke方法,此时把动态代理类父接口名称(IHello)、方法名、参数,传递给服务端。

package client;

import api.NetModel;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyFactory {

    private static InvocationHandler handler = new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//          传入代理类对象接口名。动态代理类会动态生成Proxy0类,此类是实现IHello接口的,需要把IHello接口名传到服务端
            NetModel netModel = new NetModel(proxy.getClass().getInterfaces()[0].getName(), method.getName(), args);
            Object resObj = RPCClient.send(netModel);
            return resObj;
        }
    };

//    生成动态代理对象
    public static <T> T getInstance (Class<T> clazz){
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, handler);
    }

}

PRCClient

package client;

import api.IHello;
import api.NetModel;
import api.Person;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

public class RPCClient {

    public static Object send(NetModel netModel) throws IOException, ClassNotFoundException {
        Socket socket = new Socket("127.0.0.1", 8888);
//       对象发送给服务端
        ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
        out.writeObject(netModel);
        out.flush();
//        接受服务端处理后的结果
        ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
        Object obj = in.readObject();
//        关闭流及Socket
        out.close();
        in.close();
        socket.close();
        return obj;
    }

    public static void main(String[] args){
//        hello指向动态代理对象
        IHello hello = ProxyFactory.getInstance(IHello.class);
        String str = hello.sayHello("zz");
        Person person = hello.getPerson("zz");
        System.out.println(str);
        System.out.println(person.toString());
    }

}

五、server包下的Hello、RPCServer

Hello实现IHello

package server;

import api.IHello;
import api.Person;

public class Hello implements IHello {

    @Override
    public String sayHello(String name) {
        return "Hello," + name;
    }

    @Override
    public Person getPerson(String name) {
        Person person = new Person(12, name);
        return person;
    }

}

RPCServer接受请求内容,处理,发送结果给客户端。

package server;

import api.NetModel;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Properties;

public class RPCServer {

    private static void openServer() throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("服务开启...");
        while (true){
            Socket socket = serverSocket.accept();
            System.out.println("连接成功:"+socket.getLocalAddress());
//           接受对象
            ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
            NetModel netModel = (NetModel) in.readObject();
            String className = netModel.getClassName();
            String methodName = netModel.getMethodName();
            Object[] args = netModel.getArgs();             //参数值
            Class[] types = new Class[args.length];       //参数类型
            for (int i = 0; i < types.length; i++){
                types[i] = args[i].getClass();
            }
//            处理

            String classNameServer = getPropertyValue(netModel.getClassName());
            Class clazz = Class.forName(classNameServer);
            Method method = clazz.getMethod(netModel.getMethodName(), types);
//            调用方法
            Object resObj = method.invoke(clazz.newInstance(), args);
//            发送对象
            ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
            out.writeObject(resObj);
            out.flush();
//            关闭流及Socket
            out.close();
            in.close();
            socket.close();
        }
    }

    /**
     * 读取配置文件,通过key获得value,即通过IHello接口名,得到实现类Hello名称
     * @param key
     * @return
     * @throws IOException
     */
    private static String getPropertyValue(String key) throws IOException {
        Properties pro = new Properties();
        FileInputStream in = new FileInputStream("javaRPC/src/config.properties");
        pro.load(in);
        in.close();
        return pro.getProperty(key);
    }

    public static void main(String[] args){
        try {
            openServer();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

配置文件config.properties

api.IHello=server.Hello

六、测试

先运行服务端,再运行客户端。

服务端:

客户端:

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值