目标

写个简单的rpc 调用

Rpc大致思路

定义一个接口HelloService 放在iface.jar中
应用P中引用iface.jar 实现接口HelloService进行处理任务 注册bean到spring中
应用C中, 通过spring获取HelloService的实例phService
调用phService.echo(xxx)方法,获得返回结果

应用P运行在1个jvm中 应用C运行在另1个jvm中

phService实质为一个代理对象
该代理对象通过把调用的参数封装,序列化后,通过网络传输给应用P
应用P根据收到参数后,调用处理接口相应的实现类,并将结果回传给应用C端
最终代理对象返回收到的运行结果

序列化-知识点补充

进行RPC调用必然要涉及对方法参数的序列化。

序列化框架有很多, 如protobuf、thrift、hession等
dubbo的rpc是支持多种序列化方式,如Java原生、Kryo、webservice等
spring cloud的http调用也算一种序列化方式
有了序列化工具,不仅仅是跨JVM的方法调用,还能跨语言调用。

本次示例使用java原生序列化

初版代码

所用到包

1
2
3
4
5
6
7
import java.io.*;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.concurrent.*;
import java.util.concurrent.locks.LockSupport;

import com.google.common.reflect.Reflection;

定义服务接口与服务实现类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
interface HelloService{
    public String echo(String msg);
}

class  HelloServiceImpl implements HelloService{
    @Override
    public String echo(String msg) {
        String result = "【" + msg + "】 is deal";
        return result;
    }
}

序列化工具类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class Util {
    public static byte[] obj2byte(Object obj) {
        byte[] bytes = null;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = null;
        try {
            oos = new ObjectOutputStream(bos);
            oos.writeObject(obj);
            oos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                oos.close();
                bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        bytes = bos.toByteArray();
        return bytes;
    }

    public static Object byte2Obj(byte[] bytes) {
        Object obj = null;
        ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
        ObjectInputStream ois = null;
        try {
            ois = new ObjectInputStream(bis);
            obj = ois.readObject();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            try {
                ois.close();
                bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return obj;
    }
}

使用反射调用服务实现类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class  DemoRpc{
    static HelloService tagert = new HelloServiceImpl();
    static HashMap ifaceMap = new HashMap();

    public static void main(String[] args) throws Exception {
        // 最初版-生产者端-直接调用
        HelloService provider0 = tagert;
        String result = provider0.echo("v0-版本");
        System.out.println(result);

        // v1版-生产者端-反射调用
        HelloService provider1 = tagert;
        String methondName = "echo";
        String[] params = new String[1];
        params[0] = "v1-版本";
        Method method = HelloService.class.getMethod(methondName, String.class);
        result = (String) method.invoke(provider1, params);
        System.out.println(result);

        // v2版本-客户端传参-生产者端接收参数-反射调用
        ifaceMap.put("helloService",HelloServiceImpl.class.getTypeName());

        RpcPojo clientSide = new RpcPojo();
        clientSide.ifaceName = "helloService";
        clientSide.methondName = "echo";
        clientSide.params = new String[1];
        clientSide.params[0]= "v2-版本";
        clientSide.paramsTypes = new Class[1];
        clientSide.paramsTypes[0]= clientSide.params[0].getClass();

        byte[] netDatas = Util.obj2byte(clientSide);
        RpcPojo providerSide = (RpcPojo) Util.byte2Obj(netDatas);
        method = HelloService.class.getMethod(providerSide.methondName, providerSide.paramsTypes);
        Object provider2 = Class.forName((String) ifaceMap.get("helloService")).newInstance();
        result = (String) method.invoke(provider2, providerSide.params);
        System.out.println(result);
    }
}
class RpcPojo implements Serializable {
    private static final long serialVersionUID = -1055524749130316395L;
    String ifaceName;
    String methondName;
    Object[] params;
    Class[] paramsTypes;
    Object result;
}

初版 -> v1版 -> v2版

这里以过渡的形式说明了rpc的大致流程,但目前还没看到动态代理的踪迹。
下个版本会通过加入动态代理的使用,说明服务是怎么注册的。