文档章节

一种真正实现RMI无状态化的方法续:JVM源码修改步骤

强子1985
 强子1985
发布于 2015/08/23 10:55
字数 1364
阅读 149
收藏 1

项目背景请参考我的上一篇文章: http://my.oschina.net/qiangzigege/blog/495910

下面详细讲解如何修改JVM源码解决RMI的有状态化问题。

 

JVM源码可以看到,client通过控制链得到对象ID后,走数据链发送到RMI Server,Server的查找过程如下:

target = ObjectTable.getTarget(new ObjectEndpoint(id, transport));    

 

那么getTarget函数执行了啥?

/**

     * Returns the target associated with the object id.

     */

    static Target getTarget(ObjectEndpoint oe) {

synchronized (tableLock) {

    return objTable.get(oe);

}

    }

其中objTable

  public static final Map<ObjectEndpoint,Target> objTable =

new HashMap<ObjectEndpoint,Target>();

--------

ObjectEndpointequals函数定义如下:

 

public boolean equals(Object obj) {

if (obj instanceof ObjectEndpoint) {

    ObjectEndpoint oe = (ObjectEndpoint) obj;

 return id.equals(oe.id) && transport == oe.transport;//4

else {

    return false;

}

    }

这里重点关注 行4的内容。先看前半部分

id.equals(oe.id)代码为

 

 public boolean equals(Object obj) {

if (obj instanceof ObjID) {

    ObjID id = (ObjID) obj;

    return objNum == id.objNum && space.equals(id.space);

else {

    return false;

}

    }

space.equals(id.space)的代码为

 

public boolean equals(Object obj) {

if (obj instanceof UID) {

    UID uid = (UID) obj;

    return (unique == uid.unique &&

    count == uid.count &&

    time == uid.time);

else {

    return false;

}

    }

所以结论就是:id.equals(oe.id)只需要相关的4个值(objNum unique count time)相等就行了。

----------------------------------------

再看 transport == oe.transport;//4

先看这2

Transport transport = id.equals(dgcID) ? null : this;//dgcID代表【0:0:0 2

target = ObjectTable.getTarget(new ObjectEndpoint(id, transport));

对于业务来说,判断条件结果为false,所以结果为this.

然后这里根本不用考虑。只要保证对象绑定的port一致就可以了。

------------------------所以问题就很简单了,只要关注4个值(objNum unique count time)就行了。

而且网络中传递的也是这4ID.

 

下面看看4ID生成的规则。

 

 

 /**

     * Construct a new live reference for a server object in the local

     * address space.

     */

    public LiveRef(int port) {

this((new ObjID()), port);

    }

这里是new ObjID(),所以需要去看看new ObjID())生成的规则。

 

 

/**

     * Generates a unique object identifier.

     *

     * <p>If the system property <code>java.rmi.server.randomIDs</code>

     * is defined to equal the string <code>"true"</code> (case insensitive),

     * then this constructor will use a cryptographically

     * strong random number generator to choose the object number of the

     * returned <code>ObjID</code>.

     */

    public ObjID() {

/*

 * If generating random object numbers, create a new UID to

 * ensure uniqueness; otherwise, use a shared UID because

 * sequential object numbers already ensure uniqueness.

 */

if (useRandomIDs()) {

    space = new UID();

    objNum = secureRandom.nextLong();

else {

    space = mySpace;

    objNum = nextObjNum.getAndIncrement();

}

    }

 

可见这里根据是否使用随机ID来生成ID.useRandomIDs的函数定义如下:

 

private static boolean useRandomIDs() {

String value = AccessController.doPrivileged(

    new GetPropertyAction("java.rmi.server.randomIDs"));

return value == null ? true : Boolean.parseBoolean(value);

    }

然后我添加了调试信息

 

 if (useRandomIDs()) {

        System.out.println("use random id yes");

        space = new UID();

        objNum = secureRandom.nextLong();

    } else {

        System.out.println("use random id no");

        space = mySpace;

        objNum = nextObjNum.getAndIncrement();

    }

    System.out.println("id---"space +" "+objNum);

    }

的打印结果为:

 

 

可见,默认情况下,虚拟机采用了随机ID.

--------------------------------------那如果不采用随机规则呢?

-Djava.rmi.server.randomIDs=false

测试2个对象的生成规则为

 

use random id no
id----445c7b23:14cb0a97861:-8000 0

use random id no
id----445c7b23:14cb0a97861:-8000 1

可以看到objNum是按照规则生成的,01,2,3,4, 但是space还是没有规律。

但是上面2个对象的space是一样的,因为

 space = mySpace; mySpaceprivate static final UID mySpace = new UID();

是一个全局静态final 对象。

所以我们要保证new UID()的时候是一致的。

--------------------------------------------------

然后发现了一个奇怪的现象

就是当

-Djava.rmi.server.randomIDs=false

 

 

感觉事情貌似突然变简单了。

 

难道-Djava.rmi.server.randomIDs=false就可以解决问题了?

----------------------------

 

 

想了半天,终于找到最终解决方案了,只需要修改3个地方

修改1[java.rmi.server.ObjID]

public boolean equals(Object obj) {

             

    if (obj instanceof ObjID) {

        ObjID id = (ObjID) obj;

        return objNum == id.objNum && space.equals(id.space);

    } else {

        return false;

    }

    }

修改成

public static String newRmi = System.getProperty("java.rmi.server.randomIDs");

 

        public boolean equals(Object obj) {

        if (obj instanceof ObjID) {

            ObjID id = (ObjID) obj;

            String localSpace = space.toString();

            String objSpace = id.space.toString();

           

            if (null != newRmi && newRmi.equals("false")) {

                if (localSpace.startsWith("0:0:0") || objSpace.startsWith("0:0:0")) {

                   

                    return objNum == id.objNum && space.equals(id.space);

                } else {

                    return objNum == id.objNum;

                }

            } else {

                return objNum == id.objNum && space.equals(id.space);

            }

 

        } else {

            return false;

        }

    }

 

---

 

 

修改2:加上运行参数 

-Djava.rmi.server.randomIDs=false

 

 

 

修改3:为了防止对象被回收,修改UnicastRemoteObject

[java.rmi.server.UnicastRemoteObject]

 

private static Remote exportObject(Remote obj, UnicastServerRef sref)

   throws RemoteException

    {

   // if obj extends UnicastRemoteObject, set its ref.

   if (obj instanceof UnicastRemoteObject) {

       ((UnicastRemoteObject) obj).ref = sref;

   }

   return sref.exportObject(obj, null, false);

}

修改为

 

public static String newRmi = System.getProperty("java.rmi.server.randomIDs");

 

private static Remote exportObject(Remote obj, UnicastServerRef sref)

   throws RemoteException

    {

   // if obj extends UnicastRemoteObject, set its ref.

   if (obj instanceof UnicastRemoteObject) {

       ((UnicastRemoteObject) obj).ref = sref;

   }

   

   return sref.exportObject(obj, null,

   ( null!= newRmi && newRmi.equals(“false”) )?true:false);

    }

 

 

 

 

修改4

为了防止不存在前台reaper线程存在,导致VM退出(不依赖于任何其它业务),需要自启动一个前台线程

方法如下:

修改UnicastRemoteObject[java.rmi.server.UnicastRemoteObject]

public static String newRmi = System.getProperty("java.rmi.server.randomIDs");

 

public static Thread selfCreateThead = null;

 

    static{

        if (null!= newRmi && newRmi.equals(“false”) && null == selfCreateThead) {

            selfCreateThead = new Thread(new Runnable() {

                @Override

                public void run() {

                    // TODO Auto-generated method stub

                    // must always cycle

                    while (true) {

                        try {

                            Thread.currentThread().sleep(1 * 60 * 1000);// 1

                                                                        // minutes

                        } catch (InterruptedException e) {

                            // TODO Auto-generated catch block

                            e.printStackTrace();

                        }

                    }

                }

            });

            selfCreateThead.start();

        }

    }

 

 

修改5[暂时丢弃]

[sun.rmi.transport.ObjectEndpoint]

{注意:这个类非java官方开源,所以一定要将所在linux机器的class文件反编译后的java文件

 RPC技术文件夹下面的zip文件里的java文件

做一个仔细的比较,看是否有差异...}

    public boolean equals(Object obj) {

   if (obj instanceof ObjectEndpoint) {

       ObjectEndpoint oe = (ObjectEndpoint) obj;

       return id.equals(oe.id) && transport == oe.transport;

   } else {

       return false;

   }

    }

 

    /**

     * Returns the hash code value for this object endpoint.

     */

    public int hashCode() {

   return id.hashCode() ^ (transport != null ? transport.hashCode() : 0);

    }

 

 

 

 

 

 

 

修改为

   public static String newRmi = System.getProperty("java.rmi.server.randomIDs");

 

    public boolean equals(Object obj) {

        if (obj instanceof ObjectEndpoint) {

            ObjectEndpoint oe = (ObjectEndpoint) obj;

            if (null != newRmi && newRmi.equals("false")) {

                return id.equals(oe.id);

            } else {

                return id.equals(oe.id) && transport == oe.transport;

            }

        } else {

            return false;

        }

    }

 

    /**

     * Returns the hash code value for this object endpoint.

     */

    public int hashCode() {

        if (null != newRmi && newRmi.equals("false")) {

            return id.hashCode();

        } else {

            return id.hashCode() ^ (transport != null ? transport.hashCode() : 0);

        }

    }

 

 

 

 

  比较复杂的是对象的回收机制,此方案是否实际可行,需要经过测试的验证。

 

目前已经在2个产品中上线,运维人员表示未发现问题。

 

觉得好的请点个赞,做个技术人员也是不容易滴 :)

Email: 837500869@qq.com

© 著作权归作者所有

共有 人打赏支持
强子1985

强子1985

粉丝 875
博文 1111
码字总数 818977
作品 8
南京
架构师
私信 提问
一种基于HAProxy实现RMI负载均衡的方法

本方法提出了一种基于HAProxy实现RMI控制链和数据链都完全实现负载均衡的方法, 使得RMI Client通过HAProxy拿到任何一台服务器的RMI对象信息后,通过HAProxy发送此对象信息到任何另外一台服务...

强子哥哥
2015/08/23
0
0
用RMI实现基于Java的分布式计算(1)

向您介绍使用RMI实现Java的分布式计算。由于Java具有跨平台、代码可移植性、安全高效等广泛而强大的功能,因而在开发网络Java分布式应用的时候,可以用它自身的机制实现分布式计算。 概述 随...

山哥
2012/02/24
0
0
分布式消息通信框架RMI原理分析

什么是RPC RPC(Remote Procedure Call,远程过程调用) 一般用来实现部署在不同机器上的系统之间的方法调用, 使得程序能够像访问本地系统资源一样,通过网络传输去访问远端系统资源;(!!...

Java搬砖工程师
11/13
0
0
Java Serializable(序列化)的理解

1、序列化是干什么的? 简单说就是为了保存在内存中的各种对象的状态(也就是实例变量,不是方法),并且可以把保存的对象状态再读出来。虽然你可以用你自己的各种各样的方法来保存object st...

LCZ777
2014/08/25
0
0
【转贴】java序列化

在javaeye看到的一篇文章,讲的比较详细 http://www.javaeye.com/topic/121311 1、序列化是干什么的? 简单说就是为了保存在内存中的各种对象的状态,并且可以把保存的对象状态再读出来。虽然...

inferrrrrr
2010/05/11
159
0

没有更多内容

加载失败,请刷新页面

加载更多

[Spring4.x]基于spring4.x纯注解的Web工程搭建

在前文中已经说明了如何基于 Spring4.x+ 版本开发纯注解的非web项目,链接如下: https://my.oschina.net/morpheusWB/blog/2985600 本文则主要说明,如何在Web项目中,"基于spring纯注解方式...

morpheusWB
19分钟前
2
0
基础编程题目集-7-13 日K蜡烛图

股票价格涨跌趋势,常用蜡烛图技术中的K线图来表示,分为按日的日K线、按周的周K线、按月的月K线等。以日K线为例,每天股票价格从开盘到收盘走完一天,对应一根蜡烛小图,要表示四个价格:开...

niithub
35分钟前
3
0
Jenkins window 下的安装使用

1.下载:https://jenkins.io/download/ 双击安装完毕,将自动打开浏览器: http://localhost:8080 打开对应位置的文件,将初始密钥粘贴至输入框。 第一个是 安装默认的软件;第二个是 自定义...

狼王黄师傅
37分钟前
1
0
深入解析react关于事件绑定this的四种方式

这篇文章主要介绍了详解react关于事件绑定this的四种方式,写的十分的全面细致,具有一定的参考价值,对此有需要的朋友可以参考学习下。如有不足之处,欢迎批评指正。 在react组件中,每个方...

前端攻城小牛
45分钟前
2
0
JS正则表达式

url的正则表达式:包括IP,域名(domain),ftp,二级域名,域名中的文件,域名加上端口!用户名等等信息 `function` `IsURL(str_url) {``var` `strRegex =``"^((https|http|ftp|rtsp|mm...

前端小攻略
49分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部