注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

和申的个人主页

专注于java开发,1985wanggang

 
 
 

日志

 
 

DCE  

2011-09-29 15:32:43|  分类: 项目开发 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
DCE是一个允许在运行状态下无限制的修改加载类文件的Java虚拟机补丁。

当前Java虚拟机的动态加载机制只允许修改类的方法体,而使用DCE以后,可以

?增加、删除 类的属性、方法
?改变一个类的父类
DCE是基于GPL v2.0开源协议的。你可以通过Windows,Linux,Mac OS下载DCE的源代码或者可执行文件。
通过加强Hot Swap可以减少应用重启,提高开发效率。

这里说明,与startws集成、使用DCE遇到问题的解决方法,更方便地使用DCE。

Quick Start
1. 下载Linux下打过DCE补丁的JDK。
# 下载: wget ftp://10.16.28.100/dce/jdk1.6.0_25_dce.tgz 
# 下载如有问题联系我,直接传你一份  的版本,理论上可以用我repackage的jar直接安装,未测试,  
解压到$JAVA_HOME指向JDK的目录,即放在$JAVA_HOME目录的父目录。
保持目录名jdk1.6.0_25_dce不变。我机器的目录如下:
?
/usr/alibaba 
    ├── httpd -> httpd-2.0.63/ 
    ├── httpd-2.0.63
    ├── install 
    ├── java -> jdk1.6.0_22/ # JAVA_HOME在这里 # 
    ├── jboss-4.0.5.GA 
    ├── jdk1.6.0_22 
    └── jdk1.6.0_25_dce # DCEVM放在了这里 #
    注:约定目录位置及目录名 只是为了方便dce-startws脚本找到DCE JVM所在目录。
           Mac下,JDK自已照官方网站说明打一下补丁。  
2. 安装命令工具inotify-tools。 newclass2dce.sh脚本使用这个工具。
?
sudo apt-get install inotify-tools
安装报错:必须启用universe 组件。则说明你的Ubuntu的源中没有不全。
有哪些源以及如何设置参见: http://wiki.ubuntu.org.cn/Qref/Source  。推荐使用163或是Sohu的源,速度很快。
# 多谢司先锋同学报告这个问题。
3. 通过dce-startws脚本集成DCE启动应用(startws.sh脚本功能不变)
?
 path/to/deploy/bin/dce-startws
通过dce-killws脚本关闭应用。(killws.sh脚本功能不变)
?
 path/to/deploy/bin/dce-killws
 注:
? 如果Deploy工程的bin目录没有找到dce-startws.sh脚本,则startws还没有集成DCE。
如何集成参见 下面的【启动脚本的修改】一节的说明。
? dce方式启动应用只有在mode = dev(即开发环境下)才会启作用。 
DCE注意
Linux下,DCE目前只支持32位JVM,不支持64位JVM。
与JDK 1.6 update 26有兼容问题,使用JDK 1.6 update 25。
# 参见官网 http://ssw.jku.at/dcevm/binaries/  的说明。
startws集成DCE的实现结构
结构图如下:
 DCE - 和申 - 和申的个人主页
 
newclass2dce.sh脚本由dce-start脚本启动。
newclass2dce.sh脚本会扫描代码工程中的classes目录下新增的类。
以intl-myalibaba应用为例,工程目录结构如下: 
?
intl-myalibaba 
├── all 
├── biz 
│   ├── home 
│   ├── product 
│   ├── recommendservices 
│   └── saas 
├── bundle 
│   └── war 
├── deploy 
│   ├── autoconf 
│   ├── bin 
│   ├── i18n 
│   ├── jboss-conf 
│   ├── target 
│   ├── templates 
│   └── ... 
└── web 
    ├── biztrends 
    ├── buyer 
    ├── buyerprofile 
    ├── category 
    ├── common 
    ├── user 
    ├── vas 
    └── ...
代码工程目录即是指 intl-myalibaba/biz、intl-myalibaba/web下面的目录,这些目录有Java源码。
当在Eclipse中新增类时,Eclipse会代码工程的classes目录生成类文件,比如目录 intl-myalibaba/biz/product/target/classes。
注意,开发通过startws脚本启动的Java进程有Class Path并没有包含这些目录,所以需要一个脚本把新增类拷贝到JVM的Class Path中。
newclass2dce.sh脚本把新增类拷贝到的目录是 intl-myalibaba/deploy/target/intl-myalibaba.war/WEB-INF/classes。
DCE与startws集成注意
? 修改类Hot Swap的前提是Eclipse以Debug方式连接JVM。没有Debug连接JVM,Eclipse中的修改代码不会生效。
? newclass2dce.sh脚本扫描代码工程的classes目录,拷贝新增类到deploy工程的classes目录下。
前提是Eclipse编译工程目录即是newclass2dce.sh脚本扫描的工程目录。Windows下开发代码,Linux下运行的方式就不符合这个前提。
Eclipse编译目录和newclass2dce.sh扫描的目录都不在一台机器上,新增类不会Load JVM中。
可以通过提交后再在Linux机器mvn compile的方式生成新类。
已知的问题及解决
1. 动态添加的static属性,例如 private static String attrib1 = "x",调用时会显示attrib1是null。
2. 在一个正在执行的循环中,改变可能不能生效。例如:
?
 public static void main(String[] args) { 
        for (int i = 0; i < 10000; i++) { 
            test(); //sleep 1s and print something 
        } 
  }
修改为:
?
public static void main(String[] args) { 
        for (int i = 0; i < 10000; i++){ 
            test(); 
            System.out.println("xxx"); 
        } 
}
xxx是不能输出的。
但test方法体内部的修改是可以生效的。例如:
?
 public static void main(String[] args) { 
        System.out.println("xxx"); 
        for (int i = 0; i < 10000; i++) { 
            test(); 
        } 
}
3. 运行时新加了类,在新加类放到classpath下之前 访问该类,会报ClassNotFound异常。
#  新加类放到classpath下,通过newclass2dce.sh脚本完成。
之后再将新加类放到classpath下,也不会使用,只能重启VM了。
所以在新加类放到classpath下(可以通过newclass2dce.sh脚本在Deploy工程下的logs/dce.log日志文件来确认)后,再访问 使用了新类的代码。
# 理论上这个时间会很快,碰到这种情况概率较小。
4. 和asm、cglib相关的jar包冲突。
异常:
?
Caused by: java.lang.NoSuchMethodError: org.objectweb.asm.ClassWriter.<init>(Z)V 
    at net.sf.cglib.core.DebuggingClassWriter.<init>(DebuggingClassWriter.java:47) 
    at net.sf.cglib.core.DefaultGeneratorStrategy.getClassWriter(DefaultGeneratorStrategy.java:30) 
    at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:24) 
    at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216) 
    at net.sf.cglib.core.KeyFactory$Generator.create(KeyFactory.java:145) 
    at net.sf.cglib.core.KeyFactory.create(KeyFactory.java:117) 
    at net.sf.cglib.core.KeyFactory.create(KeyFactory.java:108) 
    at net.sf.cglib.core.KeyFactory.create(KeyFactory.java:104) 
    at net.sf.cglib.proxy.Enhancer.<clinit>(Enhancer.java:69) 
    at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.getProxyFactory(CGLIBLazyInitializer.java:117) 
    at org.hibernate.proxy.pojo.cglib.CGLIBProxyFactory.postInstantiate(CGLIBProxyFactory.java:43)
 参见DCE的JIRA http://kenai.com/jira/browse/DCEVM-4 
原因:dcevm.jar文件中包含了一份ASM类,版本较老,并优先加载。
解决方法1:
重命名dcevm.jar文件中asm的包名,从 org.objectweb.asm 重命名成 dce.org.objectweb.asm。
这里提供下载的DCE JDK中,已经重命名好了。下载 调整过ASM包名的dcevm.jar 。
# 重命名使用了工具 JarJar(http://code.google.com/p/jarjar/ )。 
解决方法2:
asm、cglib换成了新的版本:asm-3.3.1.jar、cglib-nodep-2.2.jar。
asm、cglib各版本匹配注意点:
    asm 1.5.3.jar 匹配 cglib-2.1.3.jar
    asm-2.X.jar asm-3.x.jar 匹配  cglib-nodep-2.1_3.jar
5. Crash when running maven test goal with jmockit
参见DCE的JIRA http://kenai.com/jira/browse/DCEVM-3 
6. DCEVM启动报错
?
Must use the serial GC in the Dynamic Code Evolution VM 
Could not create the Java virtual machine.
把JAVA启动参数中并发GC的选项删除,如:
?
-XX:+UseConcMarkSweepGC 
-XX:+CMSIncrementalMode 
-XX:+CMSIncrementalPacing 
-XX:CMSIncrementalDutyCycleMin=0
-XX:CMSIncrementalDutyCycle=10
开发模式下,修改这些选项不会有功能上的影响。
DCE环境准备
Linux下打过DCE补丁的JDK从这里下载: wget ftp://10.16.28.100/dce/jdk1.6.0_25_dce.tgz 。 
当然你也可以自己打一下补丁。简单操作如下:(详细参见参考资料中的内容)
从 http://ssw.jku.at/dcevm/binaries/  下载 DCE是JAR压缩包。
? 替换掉JDK下jre/bin/client/jvm.dll 和 jre/bin/server/jvm.dll。(Windows下)
替换掉JDK下jre/lib/i386/client/libjvm.so和jre/lib/i386/server/libjvm.so。(Linux下)
? 将dcevm.jar 加到jre/lib/ext/ 目录 
启动脚本的修改
1. 新增dce-startws
dce-startws从startws脚本拷贝过来,相对startws修改如下:
?
#!/bin/bash 
  
# check if root run the script 
  
# determine base directory; preserve where you're running from 
cd `dirname $0`/.. 
BASE=`pwd` 
  
chmod +x $BASE/bin/ -R 
  
# =================== dce section start =================== 
  
export DCE_ENABLE=true
  
target_classes=$(echo $BASE/target/*.war/WEB-INF/classes) 
if echo $target_classes|grep ' '; then 
    echo -e "\E[34;40m Too many war files under $BASE/deploy/target! \033[0m"
    exit 1
fi 
if [ ! -d "$target_classes" ]; then 
    echo -e "\E[34;40m Could not find WAR file under $BASE/deploy/target! \033[0m"
    exit 1
fi 
if [ ! -d $BASE/../web ]; then 
    echo -e "\E[34;40m Could not find code project under $BASE/..! \033[0m"
    exit 1
fi 
  
# echo -e "\E[34;40m Turn off webx module cache! \033[0m"
# find $BASE/deploy/target/ -name webx.xml -exec grep -L 'property name="module.cache" value="false"' {} \;|xargs -r sed -i  '/<service name="ModuleLoaderService">/a \\ <property name="module.cache" value="false"/>'
  
$BASE/bin/newclass2dce.sh $BASE/.. $target_classes $BASE/logs/dce.log >$BASE/logs/dce_stdout.log 2>&1 & 
  
# =================== dce section end =================== 
  
$BASE/bin/jbossctl start
2. 新增dce-killws
dce-killws从killws脚本拷贝过来,相对killws修改如下:
?
#!/bin/bash 
  
# determine base directory; preserve where you're running from 
cd `dirname $0`/.. 
BASE=`pwd` 
  
chmod +x $BASE/bin/ -R 
  
# =================== dce section start =================== 
  
ps -eo 'pgid cmd' | grep newclass2dce.sh | grep $BASE | awk '{print -$1}' | uniq | xargs kill -9
  
# =================== dce section end =================== 
  
$BASE/bin/jbossctl stop
3. 修改env脚本(env.vm)
# 修改了dev mode下的JAVA_HOME环境变量,使用DCEVM
?
 elif [ $PRODUCTION_MODE = "dev" ]; then 
    #we should reduce resource usage on developing mode 
    JAVA_MEM_OPTS=" -Xms1024m -Xmx1024m -XX:PermSize=128m -XX:MaxPermSize=192m "
    JAVA_OPTS=" $JAVA_MEM_OPTS $JAVA_DEBUG_OPT $JAVA_MONITOR_OPTS -Dtrace.timer.period=60000  "
    JAVA_OPTS="$JAVA_OPTS -Dark.trace.sql.stack=false -Dark.trace.sql=SUDI -Dcom.alibaba.ark.trace.TraceHandler=com.alibaba.ark.trace.LoggingTraceHandler -Dcom.alibaba.ark.monitor.IMonitor=com.alibaba.ark.monitor.DBMonitor"
  
    # === dce start === 
    if [ "$DCE_ENABLE" = "true" ]; then 
        if [ "$DCEVM_HOME" = "" ]; then 
            DCEVM_HOME=$(echo $(dirname $JAVA_HOME)/jdk*dce) 
        fi 
        export JAVA_HOME=$DCEVM_HOME 
    fi 
    # === dce end === 
else
4. 新增newclass2dce.sh脚本
?
#!/bin/bash 
# newclass2dce.sh prj_path target_classes_path dce_log_path 
# document: http://b2b-doc.alibaba-inc.com/display/icbu/DCE 
  
echo start newclass2dce.sh ... 
touch $3
  
echo find classes directory: 
classes_paths=`find $1 -path '*/target/classes'` 
echo "$classes_paths"
  
echo watching new class file for dce... >> $3
echo "$classes_paths" | inotifywait -mr -e modify -e create --fromfile - | grep --line-buffered '\.class$' | awk '{ path=$1$3;if($2~/^CREATE/){ print path;dict[path]=1;} else if($2~/^MODIFY/ && path in dict){ print path;}}; fflush()' | while read filename 
do
    fullname=${filename##*/target/classes/} 
    package=$(dirname $fullname) 
    target_dir=$2/$package
    mkdir -p $target_dir 
    cp $filename $target_dir 
    echo cp $filename $target_dir >> $3
done
参考资料
DCE官方网址
http://ssw.jku.at/dcevm/ 
hotswap 用户手册 - 淘宝JAVA中间件团队博客
http://rdc.taobao.com/team/jm/archives/641 
hostswap dcevm - 使用介绍
http://www.cnblogs.com/redcreen/archive/2011/06/03/2071169.html 
Dynamic Code Evolution for Java dcevm 原理
http://www.cnblogs.com/redcreen/archive/2011/06/14/2080718.html 
Java HotSpot dcevm 在debug模式下的热部署
http://sjsky.iteye.com/blog/907606 
深入 Java 调试体系
http://www.ibm.com/developerworks/cn/java/j-lo-jpda1/ 
Java Platform Debugger Architecture 
http://java.sun.com/javase/technologies/core/toolsapis/jpda/ http://wiki.ubuntu.org.cn/Qref/Source 

  评论这张
 
阅读(1337)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2016