Java RMI & GSON

Java RMI & GSON

起因是分布式与云计算课程需要做这样一个项目… 虽然 RMI 已经是十多年前的东西了,不过课程需要,还是得硬上。第一次作业是基于RMI写的,本次作业则是自己重写一个RMI。助教提供的代码中已经完成了 registry 部分,因此本文章的内容主要注重于 Communication Module 部分。

RMI 架构&通信流程

先说说 RMI 的架构和通信流程,便于接下来的理解。

RMI 架构

RMI 大致上可以分为三个部分:registry, server, client

具体上:

  • registry 负责存储服务的信息,并提供 server 注册服务和 client 发现服务的相关方法
  • server 是具体的服务器业务类,开启的时候要向 registry 注册自己提供的服务,并维护 rorTable (RemoteObjectReferenceTable),以便 client 在进行远程过程调用的时候能找到对应的远程对象。
  • clinet 是具体的客户端业务类,开启后先向 registry 查询服务,得到服务端留下的 ROR (RemoteObjectReference),然后就可以用其中储存的 IP, 端口等信息直接和服务器交流了(之后就没有 registry 什么事情了…)

需要注意的两个表/mapping:

  • registry 上:Registry: ServiceName[String] -> ror[RemoteObjectReference]
  • server 上:ror[RemoteObjectReference] -> realObject[Object]

一个可以参考的架构图(随手画的,还请见谅):
随手画的糟糕架构图

RMI 通信流程

大概可以看这个说明:
在这里插入图片描述

1 s, create rorTable
2 s, create object
3 s, generate ror with object
3.1 s, put object in self.rorTable
3.2 s, start server-side cm listening
4 s->r, register in registry, servicename -> ror
5 c->r, using servicename to fetch ror
6 c, get stub from ror.localize()
7 c, stub being used, remote method invoked
8 c, invocation caught in RORIn…Handler
9 c, read ip:port in ror
10 c->s, init TCP conn with ip:port
11 c->s, send ror, method, args
12 s, read ror, method args
13 s, using ror find object from self.rorTable
14 s, do the real invocation
15 s->c, send back result
16 c, show result. rmi finished.

基于 GSON 的 JSON 化传递

坑点:

  1. Class<?>[] 不支持直接json化,需要特殊处理(实际上就是把method的参数类型和参数本身都转换成string,然后再在远端恢复),具体操作如下:
    远程消息传递类(RemoteInvokeInfo):

    public String methodName;
    public String[] argClassNames;
    public Object[] args;
    

    服务端通信模块 (ServerCM):

    Class[] argClasses = new Class[rii.argClassNames.length];
    for (int i = 0; i < argClasses.length; i++) {
        argClasses[i] = RemoteInvokeInfo.parseType(rii.argClassNames[i]);
        System.out.println(args[i].toString());
        args[i] = gson.fromJson(args[i].toString(),argClasses[i]);
    }
    method = instance.getClass().getMethod(rii.methodName,argClasses);
    result = method.invoke(instance,args);
    

    类型转换特殊处理 (RemoteInvokeInfo.parseType):

    // convert from className str to class
    // https://stackoverflow.com/questions/5032898/how-to-instantiate-class-class-for-a-primitive-type
    public static Class<?> parseType(final String className) {
        switch (className) {
            case "boolean":
                return boolean.class;
            case "byte":
                return byte.class;
            case "short":
                return short.class;
            case "int":
                return int.class;
            case "long":
                return long.class;
            case "float":
                return float.class;
            case "double":
                return double.class;
            case "char":
                return char.class;
            case "void":
                return void.class;
            default:
                try {
                    return Class.forName(className);
                } catch (ClassNotFoundException ex) {
                    throw new IllegalArgumentException("Class not found: " + className);
                }
        }
    }
    
  2. 如果要无痛用gson,最好设置好完备的getter/setter,否则会遇到一些奇怪的错误,以java.lang.UnsupportedOperationException: Attempted to serialize java.lang.Class :***. Forgot to register a type adapter?为主。

    如果你是 IDEA 用户,直接 Alt+Insert 即可。

还没有解决的问题

  1. 这里实际上还是单例模式,client 每次只要向 registry 请求同一个 service,拿回来的总是同一个 ror,对应的在 server 上也总是同一个对象,并没有实现每一个 client 都能拿到 service 上的一个新对象。

    可能的解法大概有以下三种,然而我都没有尝试过:

    1. 在 registry 上做手脚:允许多维护一层,可以多个 server 用同一个 ServiceName 注册服务,然后在 client 查询的时候随机分配
    2. 在 server 上做手脚:把 rorTable 从一对一改成一对多,每次根据不同的客户端调用取不同的对象
    3. 在 client 和 server 上做手脚:server 在注册服务的时候提供一个 newInstance 方法和对应的 deleteInstance 方法,client 开启的时候新建一个新的对象,结束的时候销毁。

就这样吧。祝各位编程愉快。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值