月度归档:2012年11月

通过 jconsole查看tomcat运行情况的配置方法

来源:互联网

——基于JDK1.5、Linux(Redhat5.5)、Tomcat6

由于项目的原因,需要使用jconsole对tomcat进行远程监控,结合网上的资料对配置方法进行了总结。

第一步、配置tomcat

打开%TOMCAT_HOME%/bin下的文件catalina.sh,搜索“JAVA_OPTS”找到下面这行:

wps_clip_image-13083[4][1]

在每个“JAVA_OPTS”后边都添加以下标黄代码段,且在一行显示:

if [ -z "$LOGGING_MANAGER" ]; then

JAVA_OPTS="$JAVA_OPTS -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.rmi.server.hostname=192.9.100.48  -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port="9004" -Dcom.sun.management.jmxremote.authenticate="false" -Dcom.sun.management.jmxremote.ssl="false""

else

JAVA_OPTS="$JAVA_OPTS $LOGGING_MANAGER -Djava.rmi.server.hostname=192.9.100.48  -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port="9004" -Dcom.sun.management.jmxremote.authenticate="false" -Dcom.sun.management.jmxremote.ssl="false""

fi

其中-Djava.rmi.server.hostname项必须设置,否则远程连接会因为解析到127.0.0.1失败,该项的值就是你在windows客户端连接linux时的ip地址

-Dcom.sun.management.jmxremote.port="9004"项设置远程连接端口,不要与其他应用冲突

ssl和authenticate设置为false,如果需要安全,请不要false

第二步、重启tomcat

使用root身份登录系统,进入%TOMCAT_HOME%/bin目录下:

[root@test ~]#ps –ef |grep tomcat –-输入命令查看是否存在tomcat进程

[root@test ~]#./shutdown.sh--停止tomcat服务,如果无效使用kill命令杀掉进程

[root@test ~]#./startup.sh  --启动tomcat服务

第三步、运行jconsole

进入JDK安装目录%JDK_HOME%/bin下,找到“jconsole.exe”,点击运行并选择【远程】选项卡:

wps_clip_image-19601[3][1]

在【主机名或ip】输入要远程监控的tomcat服务器地址

在【端口】输入上文设置的端口号:9004

【用户名、口令】为空,点击【连接】进入监控界面:

wps_clip_image-19967[3][1]

JConsole 远程监控Tomcat服务

JConsole是一个基于JMX的GUI工具,用于连接正在运行的JVM,不过此JVM需要使用可管理的模式启动。如果要把一个应用以可管理的形式启动,可以在启动是设置com.sun.management.jmxremote。JConsole能够提供被监控虚拟机的内存、线程、类的加载以及MBean等信息,从而能够对服务器的运行情况进行实时监控。

其实在 JDK 5 中已经新加入了这个功能了. 现在的 JDK 已经内置了对 VM 的监控功能. JDK 6 中这个工具变的更加好用了. 关于 JDK 5 中如何使用这个工具可以参考这里: http://java.sun.com/developer/technicalArticles/J2SE/jconsole.html

http://java.sun.com/j2se/1.5.0/docs/guide/management/jconsole.html

2、环境配置

1、环境

服务器:CentOS 5 + JDK6 + Tomcat6

ip地址:192.168.1.101

客户机:Windows + JDK 6

2、 配置

l 修改java虚拟机启动参数

在%TOMCAT_HOME%\bin\catalina.sh文件中将

JAVA_OPTS=”-Xms256m -Xmx512m -XX:MaxPermSize=128m”

修改为:

JAVA_OPTS=”-Xms256m -Xmx512m -XX:MaxPermSize=128m -Dcom.sun.management.jmxremote.port=1090 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false”

注意:1090表示监控的端口号,确保指定的端口不被占用;可以采用netstat -an来查看已经占用的端口;配合lsof -i:portnum 来查看占用端口的具体应用程序;另外如果开启了防火墙服务,请确保端口能够透过防火墙访问;

l 启动Tomcat服务器

./catalina.sh  run&

l 连接远程虚拟机

启动%JAVA_HOME%\bin目录下的jconsole,弹出对话框

wps_clip_image-10056[3][1]

在“远程进程”中输入192.168.1.101:1090,点击“连接”,就能查看到远程Tomcat服务器的运行情况了。

wps_clip_image-17300[6][1]

当JConsole成功建立连接,它从连接上的JMX代理处获取信息,并且以下面几个标签页呈现信息。

· 概述:监控JVM和一些监控变量的信息。

· 内在:内存使用信息

· 线程:线程使用信息

· 类: 类调用信息

· VM 概要: JVM的信息

· MBean:所有MBeans的信息

MBean 标签页展示了所有以一般形式注册到JVM上的MBeans。MBeans tab允许你获取所有的平台信息,包括那些不能从其他标签页获取到的信息。注意,其他标签页上的一些信息也在MBeans这里显示。另外,你可以使用 MBeans标签管理你自己的应用的MBeans

3、使用MBean标签监控和管理MBean

注册到JMX代理的平台或者应用的MBeans,可以通过MBeans标签获取。例如,内存的MBeans如下面定义

public interface MemoryMXBean {

public MemoryUsage getHeapMemoryUsage();

public MemoryUsage getNonHeapMemoryUsage();

public int         getObjectPendingFinalizationCount();

public boolean     isVerbose();

public void        setVerbose(boolean value);

public void        gc();

}

内存的MBean包括四个属性:

· HeapMemoryUsage. 用于描述当前堆内存使用情况的只读属性

· NonHeapMemoryUsage. 用于描述当前的非堆内存的使用情况的只读属性

· ObjectPendingFinalizationCount.用于描述有多少对象被挂起以便回收。

· Verbose.用于动态设置GC是否跟着详细的堆栈信息,为一个布尔变量

内存的MBean支持一个操作——GC,此操作可以发送进行实时的垃圾回收请求

wps_clip_image-7720[4][1]

图3: MBean 标签

左边的树形结构以名字的方式展示了所有MBeans的列表。一个MBean对象的名字由一个域的名字和一串关键字属性组成。例如,JVM的平台的MBeans是在“java.lang”域下的一组,而日志的MBeans则在"java.util.logging"域下。MBean对象的名字在javax.management.ObjectName 规范中定义。

当你在树中选中一个MBean,属性,操作,或者通知等一些信息会再右边显示出来。如果属性是可写的(属性被标志为蓝色),你可以进行设置。

wps_clip_image-4531[4][1]

图4: MBean 操作

你可以操作在”操作”节点中列出的操作。

wps_clip_image-14385[4][1]

图5: MBean 通知

你也可以看到由MBean发送出来的通知:默认情况,如果你不订阅通知的话,JConsole不会收到MBean发生过来的通知。你可以点击"订阅"按钮来堆通知进行定义,而使用"末订阅"按钮来取消订阅

4、监控内存

内存标签页通过读取内存系统、内存池、垃圾回收的MBean来获取对内存消耗、内存池、垃圾回收的情况的统计。

wps_clip_image-24717[4][1]

图 6:

上图展示了内存随时间变化的使用情况。有对堆的、非堆的以及特殊内存池的统计。内存池信息是否能被获取,取决与使用的Java虚拟机。下面列表展示了HotSpot虚拟机的内存池情况。

内存池“Eden Space ”(heap): 内存最初从这个线程池分配给大部分对象。

内存池“Survivor Space” (heap):用于保存在eden space内存池中经过垃圾回收后没有被回收的对象。

内存池“Tenured Generation” (heap):用于保持已经在 survivor space内存池中存在了一段时间的对象。

内存池“Perm Generation” (non-heap): 保存虚拟机自己的静态(refective)数据,例如类(class)和方法(method)对象。Java虚拟机共享这些类数据。这个区域被分割为只读的和只写的

内存池“Code Cache”(non-heap):HotSpot Java虚拟机包括一个用于编译和保存本地代码(native code)的内存,叫做“代码缓存区”(code cache)

详细信息区域给出一些当前线程的信息:

已使用:当前的内存使用量。使用的内存包括所有对象(能被获取和不能被获取的)所占用的内存。

分配:Java虚拟机保证能够获取到的内存量。分配内存(committed memory)的量可能随时间改变。Java虚拟机可能释放部分这里的内存给系统,相应的分配的内存这时可能少于初始化时分配的给它的量。分配量总数大于或等于已使用的内存量。

最大值:内存管理系统可以使用的最大内存量。这个值可以被改变或者不做设定。如果JVM试图增加使用的内存到大于分配量(committed memory)的情况,内存分配可能失败,即便想使用的内存量小于或者等于最大值(如:系统虚拟内存比较低时)

GC 时间 :垃圾回收使用的总时间和调用垃圾回收的次数。它可能有好几行,每行代表JVM使用的垃圾回收算法。

Usage Threshold The usage threshold of a memory pool. This field will only beshown if the memory pool supports usage threshold.

右下角的棒状图表显示了被JVM的内存池消耗的内存。如果内存使用超过 usage threshold,则棒会变红。usagethreshold是用于支持内存检查的Memory Pool MBean的一个属性。MemoryPoolMXBean定义了一系列方法用于检查内存。

public interface MemoryPoolMXBean {

// Usage threshold
public long    getUsageThreshold();

public void    setUsageThreshold(long threshold);

public boolean isUsageThresholdExceeded();

public boolean isUsageThresholdSupported();

// Collection usage threshold
public long    getCollectionUsageThreshold();

public void    setCollectionUsageThreshold(long threshold);

public boolean isCollectionUsageThresholdSupported();

public boolean isCollectionUsageThresholdExceeded();

}

每种内存池可能有两种内存初始话支持: usage threshold和collection usage threshold特殊的内存池可能两种都不支持。

Usage Threshold

usage threshold是内存池中一个可管理的属性。它使用低负荷的内存监控。设置usage threshold为正值则usage threshold检查内存池。设置usage threshold为零,则关闭检查。默认值由JVM设置。JVM一般让usage threshold在最合适的时候检查内存,典型的在GC的过程中和某些分配内存的时候。如果JVM发现当前的内存使用超过了usage threshold,它将会把UsageThresholdExceeded属性设置为true

有些内存池可能不支持usage threshold。你可以使用UsageThresholdSupported属性来判断一个内存池是否支持usage threshold。例 如,一个比较完善(generational garbage collector)的垃圾回收器(如HotSpot的虚拟机),most of the objects are allocated in the young generation,从eden内存池中产生。eden pool被设计成可以被装满;再eden pool中执行垃圾回收将会释放他

Collection Usage Threshold

Collection usage threshold是可进行垃圾回收的内存池的一个可配置属性。JVM堆一个内存池进行 垃圾回收以后,此内存池中的一些内存仍然被那些没有被回收的对象占用。collection usage threshold仅允许你在垃圾回收后对内存进行检查。如果JVM发现可用内存超出collection usage threshold,它将会设置CollectionUsageThresholdExceeded属性为true。

你可以使用CollectionUsageThresholdSupported属性来控制内存池释放支持 collection usage threshold.

usage threshold 和collection usage threshold是MBean标签中的一组。例如,在左边的树形结构中选择TenuredGen,设置tenured generation memory pool的usage threshold为6m。如下图所示

wps_clip_image-30311[4][1]

图7 设置 Usage Threshold

当 TenuredGen memory pool的内存使用超过6MBytes时,代表 TenuredGen memory pool的柱状图将会呈现红色来代表使用的内存超过了usage threshold。代表堆内存的柱状图也将变为红色。你可以选择柱状图或者在图表中指定内存池来查看某个指定内存池的信息。如果把鼠标房子柱状图上,将会显示出内存池的名字

wps_clip_image-8382[4][1]

图8 Low Memory

5、开启/关闭虚拟机的详细跟踪

如上所述,内存系统的MBean定义了一个叫做Verbose布尔变量,让你能动态的打开或关闭详细的GC跟踪。详细的GC跟踪,将会在JVM启动时显示。默认的HotSpot的GC详细输出为stdout.

wps_clip_image-21498[4][1]

图 9: 设置 Verbose GC

6、死锁检查

线程标签页提供关于应用的线程运行信息

wps_clip_image-432[4][1]

图 10: Thread 标签

左下角列出了所以正在运行的线程。如果你在过滤器中输入一个字符,线程列表将仅显示线程名字包含你输入字符的线程。通过点击某个线程,你可以获取这个线程的相关信息。

线程的MBean标签提供了一些Thread标签没有提供有用的操作。

· findMonitorDeadlockedThreads. 如果发生线程死锁,可以通过这个检查出来。操作返回一组死锁的线程ID

· getThreadInfo. 返回线程的信息。包括线程的名称、堆栈信息,导致当前线程阻塞的锁,如果有的话,还返回哪儿线程持有这个锁,和这个线程信息的统计。

· getThreadCpuTime.返回指定线程消耗的CPU时间。

为使用上面这些属性,可以到MBeans标签下,在MBeans树上选择Threading MBean。它列出了当前监控的JVM所有属性的操作。

wps_clip_image-18366[4][1]

图 11: MBean 标签 Threading

为检查你的应用是否进入死锁(例如,你的应用挂起),你可以使用findMonitorDeadlockedThreads操作。

wps_clip_image-22450[4][1]

图 12: Find Deadlocked Threads.

一旦你选择了findMonitorDeadlockedThreads按钮,将会有一个弹出窗口显示结果。在上面例子中,JConsole连接了一个存在3个死锁线程的示例应用。如上所示,检查出ID为12,10和11的线程死锁。想查询更多的线程信息,可以使用getThreadInfo操作。线程的MBean支持getThreadInfo操作的四种形式,

· 对一个给定的线程ID,给出最深的堆栈情况

· 堆一系列的线程ID,给出最深的堆栈情况

· Of a given thread ID with no stack trace.

· Of an array of thread IDs with no stack trace.

对应死锁情况,你一般会比较关系堆栈情况。你可以在getThreadInfo操作的第一个参数中输入死锁的线程ID和你想跟踪的堆栈深度。

wps_clip_image-21097[4][1]

图 13: ThreadInfo for Thread ID = 12.

双击stackTrace属性的值域将会显示一个复合对话框,你可以在堆栈中来回查看。图14,15显示了死锁线程-1的复合对话框中的第一层堆栈和第二层堆栈。

wps_clip_image-12223[3][1]

图 14: Top Frame of the Stack Trace of DeadlockedThread-1.

wps_clip_image-1979[3][1]

图 15: Second Frame of the Stack Trace of DeadlockedThread-1.

线程标签页提供了一个友好的界面供查看线程的堆栈。你可以找到死锁线程的名字,使用getThreadInfo 查找线程信息。然后又可以使用线程标签页来分析死锁。

7、控制日志等级

Logging MBean定义了LoggerNames属性,用于描述日志名称。为找到你的应用的日志,可以选择在MBeans树中java.util.logging 下的Logging MBean,双击LoggerNames属性

wps_clip_image-26239[4][1]

图 16: List of All Logger Names

Logging MBean也支持三种操作:

· getParentLoggerName. 返回指定logger的父logger

· getLoggerLevel. 返回指定logger的日志等级

· setLoggerLevel.设置指定logger到一个新的等级

所有三个操作都把日志名称作为第一个参数。

wps_clip_image-12800[4][1]

图 17: Setting Log Level.

8、获取操作系统资源信息-Sun平台下的扩展

JDK6扩展了操作系统的MBean,以此可以获取一下系统资源的信息,如:

· 处理的CPU

· 总共的和空闲的物理内存

· 可获得的虚拟内存。(即保证可以分配给运行的进程的虚拟内存)

· 总共的和空闲的交换区

· 打开的文件总数(只能在Unix下使用)

当打开MBeans标签下的Operating System MBean,你可以看到平台可以执行的所有属性和操作。你可以监控任何一个属性随时间的变化——如,CPU时间-双击属性的值域部分。

wps_clip_image-1093[4][1]

图 18: MBean 标签 OS.

除此之外,VM概要标签和概述标签提供了操作系统资源的一些信息

9、管理应用的MBean

被监控的SampleTest应用有它自己的Hello MBean:

Catalina:type=Cache

如果maxAllocateIterations属性发生改变,Cache MBean将会发送一个通知。你可以和管理平台的MBean一样使用MBean标签页来管理你的应用的MBean。例如,当maxAllocateIterations属性变化的时候你想监控。你首先可以在
通知节点中订阅。如果你改变maxAllocateIterations,你可以看到一个通知被发送。

wps_clip_image-27277[4][1]

图 19: Notifications.

Jconsole与JMX监控

Jconsole是随着SUN JDK 1.5而推出的。这是一个Java监测和管理控制台-JMX兼容的图形工具来监测Java虚拟机。它能够同时监测本地和远程的JVM。

注:JMX(Java Management Extensions)是一个为应用程序植入管理功能的框架。JMX是一套标准的代理和服务,实际上,用户可以在任何Java应用程序中使用这些代理和服务实现管理。

监控本地主机上的JVM

中间件:weblogic9.2

weblogic服务是"startWebLogic.cmd" 程序来启动的 , 要想连接到jconsole必须设置com.sun.management.jmxremote 参数.

步骤一、运行,进入命令行,浏览到startWebLogic.cmd所在目录,运行startWebLogic.cmd -Dcom.sun.management.jmxremote来启动服务;或者在startWebLogic.cmd的快捷方式加上参数也可以:

wps_clip_image-8779[3][1]

步骤二、运行jdk bin目录下的jconsole.exe,并选择本地(local)监控的服务。

wps_clip_image-8977[3][1]

步骤三、点击连接,连接成功后即可监控jvm的运行情况。

wps_clip_image-2413[3][1]

监控远程主机上的JVM

服务端:Aix5.3 + jdk1.5.+ WebLogic9.2

客户端:winXP+jdk1.5

步骤一、设置环境变量

$JAVA_HOME=/home/branchuw/software/java5/sdk

$CLASSPATH=.:/home/branchuw/software/java5/sdk/lib/tools.jar:/lib

$BEA_HOME=/home/branchuw/bea

$WL_HOME=/home/branchuw/bea/weblogic92

$Domain_HOME=/home/branchuw/bea/user_projects/domains/testdomain (可不用设置)

步骤二、新建目录如jconsole(我建的目录位置/home/branchuw/software/java5/sdk/jconsole)

cp  /home/branchuw/software/java5/sdk jre/lib/management/jmxremote.pwd.template

/home/branchuw/software/java5/sdk/jconsole /jmxremote.pwd

步骤三、把拷贝过来的jmxremote.pwd.template更名为jmxremote.pwd后,编辑该文件,去掉#monitorRole  QED前的注释并将QED修改为你要设置的密码。

如:monitorRole  1234

#controlRole  R&D

修改jmxremote.password文件的权限chmod 600 jmxremote.password。

网上资料说也可以将这两个角色及其对应的密码存储到一个xxx.properties文件里,在调用时指定其绝对路径。

步骤四、修改$Domain_HOME /bin/setDomainEnv.sh,

JAVA_OPTIONS="${JAVA_OPTIONS}

-Djava.rmi.server.hostname=192.168.60.41 #添加的参数

-Dcom.sun.management.jmxremote.port=22801 #添加的参数

-Dcom.sun.management.jmxremote.pwd.file=/home/branchuw/software/java5/sdk/jconsole/jmxremote.pwd  #添加的参数

-Dcom.sun.management.jmxremote.ssl=false 添加的参数

-Dcom.sun.management.jmxremote.authenticate=false #添加的参数

${JAVA_PROPERTIES}

-Dwlw.iterativeDev=${iterativeDevFlag}

-Dwlw.testConsole=${testConsoleFlag} -Dwlw.logErrorsToConsole=${logErrorsToConsoleFlag}"

注:运行netstat -na|grep 22801,可查看设置的端口是否在监听状态,如没有,则需检查您的配置。

步骤五、在以上都设置完成,启动服务后,即可本地监控远程服务。

wps_clip_image-191[3][1]

也可进入高级以JMX URL方式连接:

JMX URL为service:jmx:rmi:///jndi/rmi://192.168.60.41:22801/jmxrmi

wps_clip_image-11850[3][1]

wps_clip_image-29461[3][1]

注:Jdk1.6与jdk1.5,在jconsole上的使用相似。

附录:Jconsole介绍

英文介绍:http://java.sun.com/j2se/1.5.0/docs/guide/management/jconsole.html

JConsole是一个基于JMX的GUI工具,用于连接正在运行的JVM,不过此JVM需要使用可管理的模式启动。如果要把一个应用以可管理的形式启动,可以在启动时设置com.sun.management.jmxremote。例如,启动一个可以在本地监控的J2SE的应用Java2Demo ,需输入以下命令:

JDK_HOME/bin/java -Dcom.sun.management.jmxremote -jar JDK_HOME/demo/jfc/Java2D/Java2Demo.jar
JDK_HOME需要是一个含有JDK5.0的目录。
要启动JConsole,运行
JDK_HOME/bin/jconsole
一个用于连接的对话框将会打开。对话框的Local标签列出了所有本地正在运行的JVM,还包含进程的ID等信息。

wps_clip_image-25758[3][1]

Figure 2: Local Tab.

JConsole可以以三种方式连接正在运行的JVM:

· Local:使用JConsole连接一个正在本地系统运行的JVM,并且执行程序的和运行JConsole的需要是同一个用户。JConsole使用文件系统的授权通过RMI连接器连接到平台的MBean服务器上。这种从本地连接的监控能力只有Sun的JDK具有

· Remote:使用下面的URL通过RMI连接器连接到一个JMX代理:

service:jmx:rmi:///jndi/rmi://hostName:portNum/jmxrmi
hostName填入主机名称,portNum为JMX代理启动时指定的端口。JConsole为建立连接,需要在环境变量中设置mx.remote.credentials来指定用户名和密码从而进行授权。

· Advanced:使用一个特殊的URL连接JMX代理。一般情况使用自己定制的连接器而不是RMI提供的连接器来连接JMX代理,或者是一个使用JDK1.4的实现了JMX和JMX Rmote的应用。

当JConsole成功建立连接,它从连接上的JMX代理处获取信息,并且以下面几个标签页呈现信息。

· Summary tab. 监控JVM和一些监控变量的信息。

· Memory tab. 内存使用信息

· Threads tab. 线程使用信息

· Classes tab. 类调用信息

· VM tab. JVM的信息

· MBeans tab.所有MBeans的信息

MBeans tab展示了所有以一般形式注册到JVM上的MBeans。MBeans tab允许你获取所有的平台信息,包括那些不能从其他标签页获取到的信息。注意,其他标签页上的一些信息也在MBeans这里显示。另外,你可以使用 MBeans标签管理你自己的应用的MBeans

使用MBeans Tab监控和管理MBean

注册到JMX代理的平台或者应用的MBeans,可以通过MBeans标签获取。例如,内存的MBeans如下面定义

public interface MemoryMXBean {
public MemoryUsage getHeapMemoryUsage();
public MemoryUsage getNonHeapMemoryUsage();
public int         getObjectPendingFinalizationCount();
public boolean     isVerbose();
public void        setVerbose(boolean value);
public void        gc();
}

内存的MBean包括四个属性:

· HeapMemoryUsage. 用于描述当前堆内存使用情况的只读属性

· NonHeapMemoryUsage. 用于描述当前的非堆内存的使用情况的只读属性

· ObjectPendingFinalizationCount.用于描述有多少对象被挂起以便回收。

· Verbose.用于动态设置GC是否跟着详细的堆栈信息,为一个布尔变量

内存的MBean支持一个操作——GC,此操作可以发送进行实时的垃圾回收请求。

wps_clip_image-6066[3][1]

Figure 3: MBeans Tab.

左边的树形结构以名字的方式展示了所有MBeans的列表。一个MBean对象的名字由一个域的名字和一串关键字属性组成。例如,JVM的平台的MBeans是在“java.lang”域下的一组,而日志的MBeans则在"java.util.logging"域下。MBean对象的名字在javax.management.ObjectName 规范中定义。

当你在树中选中一个MBean,属性,方法,或者通知等一些信息会再右边显示出来。如果属性是可写的(属性被标志为蓝色),你可以进行设置。你可以操作在Operations tab中列出的操作。你也可以看到由MBean发送出来的通知:默认情况,如果你不订阅通知的话,JConsole不会收到MBean发生过来的通知。你可以点击"Subscribe"(订阅)按钮来堆通知进行定义,而使用"Unsubscribe"按钮来取消订阅

wps_clip_image-20762[3][1]

Figure 4: MBeans Notification.

监控内存

内存标签页通过读取内存系统、内存池、垃圾回收的MBean来获取对内存消耗、内存池、垃圾回收的情况的统计。
图:
wps_clip_image-12262[3][1]

上图展示了内存随时间变化的使用情况。有对堆的、非堆的以及特殊内存池的统计。内存池信息是否能被获取,取决与使用的Java虚拟机。下面列表展示了HotSpot虚拟机的内存池情况。

Eden Space (heap): 内存最初从这个线程池分配给大部分对象。
Survivor Space (heap):用于保存在eden space内存池中经过垃圾回收后没有被回收的对象。
Tenured Generation (heap):用于保持已经在 survivor space内存池中存在了一段时间的对象。
Permanent Generation (non-heap): 保存虚拟机自己的静态(refective)数据,例如类(class)和方法(method)对象。Java虚拟机共享这些类数据。这个区域被分割为只读的和只写的,
Code Cache (non-heap):HotSpot Java虚拟机包括一个用于编译和保存本地代码(native code)的内存,叫做“代码缓存区”(code cache)

详细信息区域给出一些当前线程的信息:
Used :已使用:当前的内存使用量。使用的内存包括所有对象(能被获取和不能被获取的)所占用的内存。

Committed :分配量:Java虚拟机保证能够获取到的内存量。分配内存(committedmemory)的量可能随时间改变。Java虚拟机可能释放部分这里的内存给系统,相应的分配的内存这时可能少于初始化时分配的给它的量。分配量总数大于或等于已使用的内存量。

Max :内存管理系统可以使用的最大内存量。这个值可以被改变或者不做设定。如果JVM试图增加使用的内存到大于分配量(committedmemory)的情况,内存分配可能失败,即便想使用的内存量小于或者等于最大值(如:系统虚拟内存比较低时)

Usage Threshold The usage threshold of a memory pool. This field will only beshown if the memory pool supports usage threshold.
GC time :垃圾回收使用的总时间和调用垃圾回收的次数。它可能有好几行,每行代表JVM使用的垃圾回收算法。(

右下角的棒状图表显示了被JVM的内存池消耗的内存。如果内存使用超过 usage threshold,则棒会变红。usagethreshold是用于支持内存检查的Memory Pool MBean的一个属性。MemoryPoolMXBean定义了一系列方法用于检查内存。
public interface MemoryPoolMXBean {
....
// Usage threshold
public long    getUsageThreshold();
public void    setUsageThreshold(long threshold);
public boolean isUsageThresholdExceeded();
public boolean isUsageThresholdSupported();
// Collection usage threshold
public long    getCollectionUsageThreshold();
public void    setCollectionUsageThreshold(long threshold);
public boolean isCollectionUsageThresholdSupported();
public boolean isCollectionUsageThresholdExceeded();
}

每种内存池可能有两种内存初始话支持: usage threshold和collection usage threshold特殊的内存池可能两种都不支持。

Usage Threshold

usage threshold是内存池中一个可管理的属性。它使用低负荷的内存监控。设置usage threshold为正值则usage threshold检查内存池。设置usage threshold为零,则关闭检查。默认值由JVM设置。JVM一般让usage threshold在最合适的时候检查内存,典型的在GC的过程中和某些分配内存的时候。如果JVM发现当前的内存使用超过了usage threshold,它将会把UsageThresholdExceeded属性设置为true
有些内存池可能不支持usage threshold。你可以使用UsageThresholdSupported属性来判断一个内存池是否支持usage threshold。例 如,一个比较完善(generational garbage collector)的垃圾回收器(如HotSpot的虚拟机),most of the objects are allocated in the young generation,从eden内存池中产生。eden pool被设计成可以被装满;再eden pool中执行垃圾回收将会释放他

Collection Usage Threshold

Collection usage threshold是可进行垃圾回收的内存池的一个可配置属性。JVM堆一个内存池进行 垃圾回收以后,此内存池中的一些内存仍然被那些没有被回收的对象占用。collection usage threshold仅允许你在垃圾回收后对内存进行检查。如果JVM发现可用内存超出collection usage threshold,它将会设置CollectionUsageThresholdExceeded属性为true。
你可以使用CollectionUsageThresholdSupported属性来控制内存池释放支持 collection usage threshold.
usage threshold 和collection usage threshold是MBean标签中的一组。例如,在左边的树形结构中选择TenuredGen,设置tenured generation memory pool的usage threshold为6m。如下图所示

wps_clip_image-10662[3][1]

Figure 6: Setting Usage Threshold.

当 TenuredGen memory pool的内存使用超过6MBytes时,代表 TenuredGen memory pool的柱状图将会呈现红色来代表使用的内存超过了usage threshold。代表堆内存的柱状图也将变为红色。你可以选择柱状图或者在图表中指定内存池来查看某个指定内存池的信息。如果把鼠标房子柱状图上,将会显示出内存池的名字

wps_clip_image-5228[3][1]

Figure 7: Low Memory.

开启/关闭虚拟机的详细跟踪

如上所述,内存系统的MBean定义了一个叫做Verbose布尔变量,让你能动态的打开或关闭详细的GC跟踪。详细的GC跟踪,将会在JVM启动时显示。默认的HotSpot的GC详细输出为stdout.

wps_clip_image-15549[3][1]

Figure 8: Setting Verbose GC.

死锁检查

线程标签页提供关于应用的线程运行信息

wps_clip_image-30674[3][1]

Figure 9: Threads Tab.

左下角列出了所以正在运行的线程。如果你在过滤器中输入一个字符,线程列表将仅显示线程名字包含你输入字符的线程。通过点击某个线程,你可以获取这个线程的相关信息。

线程的MBean标签提供了一些Thread标签没有提供有用的操作。

· findMonitorDeadlockedThreads. 如果发生线程死锁,可以通过这个检查出来。操作返回一组死锁的线程ID

· getThreadInfo. 返回线程的信息。包括线程的名称、堆栈信息,导致当前线程阻塞的锁,如果有的话,还返回哪儿线程持有这个锁,和这个线程信息的统计。

· getThreadCpuTime.返回指定线程消耗的CPU时间。

为使用上面这些属性,可以到MBeans标签下,在MBeans树上选择Threading MBean。它列出了当前监控的JVM所有属性的操作。

wps_clip_image-8363[3][1]

Figure 10: MBeans Tab Threading.

为检查你的应用是否进入死锁(例如,你的应用挂起),你可以使用findMonitorDeadlockedThreads操作。

wps_clip_image-30021[3][1]

Figure 11: Find Deadlocked Threads.

一旦你选择了findMonitorDeadlockedThreads按钮,将会有一个弹出窗口显示结果。在上面例子中,JConsole连接了一个存在3个死锁线程的示例应用SampleTest。如上所示,检查出ID为12,10和11的线程死锁。想查询更多的线程信息,可以使用getThreadInfo操作。线程的MBean支持getThreadInfo操作的四种形式,

· 对一个给定的线程ID,给出最深的堆栈情况

· 堆一系列的线程ID,给出最深的堆栈情况

· Of a given thread ID with no stack trace.

· Of an array of thread IDs with no stack trace.

对应死锁情况,你一般会比较关注堆栈情况。你可以在getThreadInfo操作的第一个参数中输入死锁的线程ID和你想跟踪的堆栈深度。

wps_clip_image-12724[3][1]

Figure 12: ThreadInfo for Thread ID = 12.

双击stackTrace属性的值域将会显示一个复合对话框,你可以在堆栈中来回查看。图13,14显示了死锁线程-1的复合对话框中的第一层堆栈和第二层堆栈。

wps_clip_image-5790[3][1]

Figure 13: Top Frame of the Stack Trace of DeadlockedThread-1.

wps_clip_image-32362[3][1]

Figure 14: Second Frame of the Stack Trace of DeadlockedThread-1.

线程标签页提供了一个友好的界面供查看线程的堆栈。你可以找到死锁线程的名字,使用getThreadInfo 查找线程信息。然后又可以使用线程标签页来分析死锁。

控制日志等级

Logging MBean定义了LoggerNames属性,用于描述日志名称。为找到你的应用的日志,可以选择在MBeans树中java.util.logging 下的Logging MBean,双击LoggerNames属性

wps_clip_image-5083[3][1]

Figure 15: List of All Logger Names.

Logging MBean也支持三种操作:

· getParentLoggerName. 返回指定logger的父logger

· getLoggerLevel. 返回指定logger的日志等级

· setLoggerLevel.设置指定logger到一个新的等级

所有三个操作都把日志名称作为第一个参数。

wps_clip_image-31675[3][1]

Figure 16: Setting Log Level.

获取操作系统资源信息-Sun平台下的扩展

JDK5.0扩展了操作系统的MBean,以此可以获取一下系统资源的信息,如:

· 处理的CPU

· 总共的和空闲的物理内存

· 可获得的虚拟内存。(即保证可以分配给运行的进程的虚拟内存)

· 总共的和空闲的交换区

· 打开的文件总数(只能在Unix下使用)

当打开MBeans标签下的Operating System MBean,你可以看到平台可以执行的所有属性和操作。你可以监控任何一个属性随时间的变化——如,CPU时间-双击属性的值域部分。

wps_clip_image-24432[3][1]

Figure 17: MBeans Tab OS.

除此之外,VM标签和Summary标签提供了操作系统资源的一些信息

管理应用的MBean
被监控的SampleTest应用有它自己的Hello MBean:

com.sun.example:type=Hello
如果CacheSize 属性发生改变,Hello MBean将会发送一个通知。你可以和管理平台的MBeans一样使用MBeans标签页来管理你的应用的MBean。例如,当CacheSize 属性变化的时候你想监控。你首先可以在
Notification标签页中订阅。如果你改变CacheSize,你可以看到一个通知被发送。

wps_clip_image-26246[3][1]

wps_clip_image-28179[3][1]

通过JavaJMX得到full GC次数?

今天有个同事问如何能通过 JMX获取到某个Java进程的full GC次数:
引用hi,问个问题,怎们在java中获取到full gc的次数呢?
我现在用jmx的那个得到了gc次数,不过不能细化出来full gc的次数

for (final GarbageCollectorMXBean garbageCollector
        : ManagementFactory.getGarbageCollectorMXBeans()) {
    gcCounts += garbageCollector.getCollectionCount();
}

你比如我现在是这样拿次数的

我回答说因为full GC概念只有在分代式GC的上下文中才存在,而JVM并不强制要求GC使用分代式实现,所以JMX提供的标准MXBean API里不提供“full GC次数”这样的方法也正常。

既然“full GC”本来就是非常平台相关的概念,那就hack一点,用平台相关的代码来解决问题好了。这些GC的MXBean都是有名字的,而主流的JVM的GC名字相对稳定,非要通过JMX得到full GC次数的话,用名字来判断一下就好了。

举个例子来看看。通过JDK 6自带的JConsole工具来查看相关的MXBean的话,可以看到,

GC的MXBean在这个位置:

 

这个例子是用server模式启动JConsole的,使用的是ParallelScavenge GC,它的年老代对应的收集器在这里:

 

该收集器的总收集次数在此,这也就是full GC的次数:

 

于是只要知道我们用的JVM提供的GC MXBean的名字与分代的关系,就可以知道full GC的次数了。

Java代码写起来冗长,这帖就不用Java来写例子了,反正API是一样的,意思能表达清楚就OK。

用一个Groovy脚本简单演示一下适用于Oracle (Sun) HotSpot与Oracle (BEA) JRockit的GC统计程序:

import java.lang.management.ManagementFactory

printGCStats = {
  def youngGenCollectorNames = [
    // Oracle (Sun) HotSpot
    // -XX:+UseSerialGC
    'Copy',
    // -XX:+UseParNewGC
    'ParNew',
    // -XX:+UseParallelGC
    'PS Scavenge',
    
    // Oracle (BEA) JRockit
    // -XgcPrio:pausetime
    'Garbage collection optimized for short pausetimes Young Collector',
    // -XgcPrio:throughput
    'Garbage collection optimized for throughput Young Collector',
    // -XgcPrio:deterministic
    'Garbage collection optimized for deterministic pausetimes Young Collector'
  ]

  def oldGenCollectorNames = [
    // Oracle (Sun) HotSpot
    // -XX:+UseSerialGC
    'MarkSweepCompact',
    // -XX:+UseParallelGC and (-XX:+UseParallelOldGC or -XX:+UseParallelOldGCCompacting)
    'PS MarkSweep',
    // -XX:+UseConcMarkSweepGC
    'ConcurrentMarkSweep',
    
    // Oracle (BEA) JRockit
    // -XgcPrio:pausetime
    'Garbage collection optimized for short pausetimes Old Collector',
    // -XgcPrio:throughput
    'Garbage collection optimized for throughput Old Collector',
    // -XgcPrio:deterministic
    'Garbage collection optimized for deterministic pausetimes Old Collector'
  ]

  R: {
    ManagementFactory.garbageCollectorMXBeans.each {
      def name  = it.name
      def count = it.collectionCount
      def gcType;
      switch (name) {
      case youngGenCollectorNames:
        gcType = 'Minor Collection'
        break
      case oldGenCollectorNames:
        gcType = 'Major Collection'
        break
      default:
        gcType = 'Unknown Collection Type'
        break
      }
      println "$count <- $gcType: $name"
    }
  }
}

printGCStats()

执行可以看到类似这样的输出:

5 <- Minor Collection: Copy
0 <- Major Collection: MarkSweepCompact

↑这是用client模式的HotSpot执行得到的;

0 <- Minor Collection: Garbage collection optimized for throughput Young Collector
0 <- Major Collection: Garbage collection optimized for throughput Old Collector

↑这是用JRockit R28在32位Windows上的默认模式得到的。

通过上述方法,要包装起来方便以后使用的话也很简单,例如下面Groovy程序:

import java.lang.management.ManagementFactory

class GCStats {
  static final List<String> YoungGenCollectorNames = [
    // Oracle (Sun) HotSpot
    // -XX:+UseSerialGC
    'Copy',
    // -XX:+UseParNewGC
    'ParNew',
    // -XX:+UseParallelGC
    'PS Scavenge',
    
    // Oracle (BEA) JRockit
    // -XgcPrio:pausetime
    'Garbage collection optimized for short pausetimes Young Collector',
    // -XgcPrio:throughput
    'Garbage collection optimized for throughput Young Collector',
    // -XgcPrio:deterministic
    'Garbage collection optimized for deterministic pausetimes Young Collector'
  ]
  
  static final List<String> OldGenCollectorNames = [
    // Oracle (Sun) HotSpot
    // -XX:+UseSerialGC
    'MarkSweepCompact',
    // -XX:+UseParallelGC and (-XX:+UseParallelOldGC or -XX:+UseParallelOldGCCompacting)
    'PS MarkSweep',
    // -XX:+UseConcMarkSweepGC
    'ConcurrentMarkSweep',
    
    // Oracle (BEA) JRockit
    // -XgcPrio:pausetime
    'Garbage collection optimized for short pausetimes Old Collector',
    // -XgcPrio:throughput
    'Garbage collection optimized for throughput Old Collector',
    // -XgcPrio:deterministic
    'Garbage collection optimized for deterministic pausetimes Old Collector'
  ]
  
  static int getYoungGCCount() {
    ManagementFactory.garbageCollectorMXBeans.inject(0) { youngGCCount, gc ->
      if (YoungGenCollectorNames.contains(gc.name))
        youngGCCount + gc.collectionCount
      else
        youngGCCount
    }
  }
  
  static int getFullGCCount() {
    ManagementFactory.garbageCollectorMXBeans.inject(0) { fullGCCount, gc ->
      if (OldGenCollectorNames.contains(gc.name))
        fullGCCount + gc.collectionCount
      else
        fullGCCount
    }
  }
}

用的时候:

D:\>\sdk\groovy-1.7.2\bin\groovysh
Groovy Shell (1.7.2, JVM: 1.6.0_20)
Type 'help' or '\h' for help.
--------------------------------------------------
groovy:000> GCStats.fullGCCount
===> 0
groovy:000> System.gc()
===> null
groovy:000> GCStats.fullGCCount
===> 1
groovy:000> System.gc()
===> null
groovy:000> System.gc()
===> null
groovy:000> GCStats.fullGCCount
===> 3
groovy:000> GCStats.youngGCCount
===> 9
groovy:000> GCStats.youngGCCount
===> 9
groovy:000> GCStats.youngGCCount
===> 9
groovy:000> System.gc()
===> null
groovy:000> GCStats.youngGCCount
===> 9
groovy:000> GCStats.fullGCCount
===> 4
groovy:000> quit

这是在Sun JDK 6 update 20上跑的。顺带一提,如果这是跑在JRockit上的话,那full GC的次数就不会增加——因为JRockit里System.gc()默认是触发young GC的;请不要因为Sun HotSpot的默认行为而认为System.gc()总是会触发full GC的。

关于JMX的MXBean的使用,也可以参考下面两篇文档:

 

源地址: http://www.53dns.com/info/html/wlbc/JAVAbc_1223_6809.html

Hadoop源代码分析 之Datanode工作原理(5)—–拷贝文件过程总结

原创文章,转载请指明出处并保留原文url地址

hadoop 相关视频下载地址: http://pan.baidu.com/share/link?shareid=223046310&uk=3073578852

前一文章中,我们重点讨论了hadoop shell脚本拷贝本地文件到hadoop文件系统(hdfs)的工作过程,我们重点针对拷贝的源代码进行了研究,主要研究了本地文件拷贝的初始化过程,文件拷贝过程。本篇文档我们通过汇总相关工作的序列图来从图形化方式分析相关工作过程。

引言,hadoop代表一种全新的编程思想,基于hadoop有很多衍生项目,充分利用他们

为我们服务是非常必要的,同时hadoop又是一个复杂系统,若能理解他的工作原理对

我们将有非常大的帮助,我们在这里以hadoop的0.1.0版本为基础逐步分析他的基本工作

原理、结构、思路等等,希望通过这个能帮助我们理解生产中的hadoop系统。 时间有限,

经验不足,疏漏难免,在这里仅仅分享一些心得,希望对大家能起到一个抛砖引玉的作用吧,

有问题请大家给我留言或者评论等,这样也能对我的工作有莫大的帮助。

感谢您阅读这篇文章!

 

一.文件拷贝总体过程

管理员在hadoop客户端机中,启动hadoop shell,拷贝本地文件到hadoop集群时,系统经历了大概下面几个阶段。

1. 系统初始化过程,这个过程中shell客户端根据本地配置文件初始化本次jvm进程的相关信息,从classpath中寻找到配置文件,获取namenode等具体信息,本地相关信息等,然后,启动main函数,调用里面功能创建一系列文件服务对象等。

2. 系统初始化完成后,读取文件系统数据,然后循环写入数据对象中,数据对象将数据先缓存在内存中,到达一定数据后将数据输出到本地临时文件中,当文件大小尺寸到达文件块尺寸时,启动dfsclient中的流输出对象,对象相同namenode沟通获取块信息(元信息)等,然后连接datanode节点通过网络将数据传输过去(datanode中相关流程在前一文章中已经研究过了),循环将全部数据传输完。

3. 最后文件结束后系统启动close功能,将内存中数据包括crc等数据刷新到网络上,同时清理本地相关临时文件等等。

二.初始化阶段

下图是hadoop shell在文件拷贝中初始化阶段的创建过程,图中内容比较多,为了绘图方便,大部分名称都采用简写,图中附上一个对照关系表供参考。

下图采用近似uml序列图方式绘制,但不是规范的uml序列图,同时也不完全拘泥于序列图的规范,核心就是让读者能看明白图中内容(uml相关可以参考http://www.iigrowing.cn/?s=uml)。

一)图示介绍

图中如下图示的含义如下:

1. wps_clip_image-29119[3][1]表示java中一个类的某个对象实例,一般用类名代表,但其实是实例对象。

2.  wps_clip_image-11684[3][1] 图中箭头表示一次调用,并且是一次阻塞式调用(被调用对象的相关函数若是没有返回,则调用者将一直等待返回)。图中箭头方向代表调用方向 ,例如A对象调用B的方法,就是 A--->B(A调用B)

3.  wps_clip_image-18733[3][1]图中虚线箭头代表调用的返回,箭头的方向表示从被调用者到调用者的返回。

4.  wps_clip_image-17496[3][1]图中蓝色竖条代表被调用函数的执行时间,从上到下表示调用开始、调用结束。

5.  wps_clip_image-30512[3][1]图中折线箭头表示对象内部函数调用, 新的蓝色竖条表示新的调用生命周期。

6.  wps_clip_image-2189[3][1] 图中调用箭头上面的文字用来描述调用的相关信息,主要表示调用的方法等信息,或者调用函数功能等,这里用法同标准序列图有些出入,但是基本相同。

7.  wps_clip_image-20985[3][1]图中<init>一般代表java中类(对象)的构造函数

8.  程序调用顺序从上 至下 进行。读者读图时需要从上至下进行,沿着箭头方向进行阅读。

wps_clip_image-23950[3][1]

上图是hadoop shell上传文件到hdfs过程,主要是初始化过程,下面我们来了解一下相关过程。

二)图形内容概括

如上图,我们用带阴影矩形标识出了3个区域

区域1:数据输出流对象创建,这个区域中各个调用主要围绕创建java流对象创建,创建出对象主要是保证上传数据文件对象的数据读取写入等各种操作,包括网络等流操作。

区域2:校验文件流对象创建区域,这里面也创建了一些流对象,但是这流对象是对上面数据文件数据进行校验的,这些校验信息形成一块独立的流对象,单独作为文件存储在hdfs中(这个版本是这样的,后续版本情况我没有去验证,稍后验证了在补充到文档中)。

区域3:数据工作区域,这里进行数据输出到远程服务器等工作,这里内容已经省略,详情见下一幅图形及解释。

三)流程内容解释

下面的解释需要读者参照上面图形进行慢慢理解,同时解释仅仅是一个处略的解释,详细情况请参考相关源代码。

1. 首先用户从命令行数输入命令, hadoop脚本调用java的dfsshell类的main行数,程序从这里开始执行。

2. main函数调用dfsshell的copyFromLocal函数

3. copyFromLocal函数调用DistributedFileSystem类(一个实例对象)的copyFromLocalFile函数(图中对象比较多采用简写办法DistributedFileSystem为dfs,其他对照参考对照)

4. dfs的copyFromLocalFile中调用doFromLocalFile函数

5. doFromLocalFile函数,调用create函数,create又调用另一个重载的函数

6. 最后在create函数中 创建FSDataOutputStream(FSDOS)对象

7. 最后在FSDOS的构造函数中创建summer对象

8. 在summer对象的构造函数中间接调用DFSClient及DFSClint内部类DFSOS对象,这个对象负责具体的同namenode及datanode进行通信等工作

9. DFSOS对象创建完成后,返回到summber构造函数中

10. summer构造函数中继续上面相似流程,创建下面校验对象(这里省略几个针对crc的FSDataOutputStream$buffer及FSDataOutputStream$PositionCache对象的创建过程,内容比较多,也比较烦了,只好省略不重要的内容了)

11. FSDataOutputStream$summer

对象创建完成后,系统会将FSDataOutputStream$summer

对象作为参数创建FSDataOutputStream$PositionCache对象,

12. FSDataOutputStream$PositionCache对象创建完成后作为参数创建FSDataOutputStream$buffer对象

13. 最后创建过程完成

14. 创建完成后,程序进行数据写入工作,详情见下节

三.数据写入过程

下图是hadoop shell程序写入数据到hdfs时序列图

wps_clip_image-21646[3][1]

一)图形内容概括

如上图,整个图形根据不同矩形阴影区域,可以分5个区域,相关情况如下:

1. 左侧垂直区域, 这个区域是程序员最容易看到也容易看清楚的一个区域, hadoop通过一个抽象的FileSystem对象将系统客户端代码同server代码进行了封装。当shell代码采用FileSystem系统分布式实现方式时,shell操作就是hdfs文件系统,当FileSystem实现的是本地文件系统操作的对象就是本地的普通文件。这样简化了shell的设计,开发人员可以专注一个方面的开发不用在关心复杂的网络通信等。

2. 右侧最上面区域1,是初始化区域,由于本图是shell进行文件拷贝的序列图,为了保持完整性我们保留大部分拷贝过程等,同时根据本图需要重点表现情况针对不同过程进行了省略或者简化。本区域是个初始化过程,较为详细过程在上面图中已经表达,读者可以去参考。

3. 右侧上面区域2, 是校验和写入区域,校验和写入过程中有个区域工作过程同写入正常数据类似(总体思路是类似,具体代码实现不同),我们省略了过程,相关过程读者感兴趣自己去看源代码。

4. 右侧上面区域3, 这个是本文重点,这里是具体数据写入过程,包括数据读取,数据写入内存缓存,然后写入网络,包括如何同namenode及datanode通信过程等,我们稍后会给出详细解释。

5. 右侧最下面区域, 这个区域是系统关闭代码区域,系统在关闭过程中清理了内存的缓存,临时文件,同namenode及datanode通信等,这个过程也包括了一个同上面数据写入区域类似的过程,这里面也省略了,读者感兴趣可以执行研究。

二)数据写入过程解释

前面main函数调用、初始化等过程等在前面一节已经有了介绍我们不在多言。我们直接从数据写入开始。

1. 读取数据 (in.read(buf)),in是输入流,在初始化过程已经创建完毕

2. 调用fsdos(FSDataOutputStream)对象的write(x,x,x)(代表有三个参数的重载函数版本,x省略了具体情况,读者去阅读源码吧)写入数据

3. 调用FSDataOutputStream$buffer对象相应写入函数(这里面其实是调用buffer对象的父对象的函数,我们为了简化记做buffer对象)

4. 调用FSDataOutputStream$PositionCache对象的write函数

5. 调用FSDataOutputStream$summer对象的writer(x,x,x)函数

6. 在FSDataOutputStream$summer对象的writer函数内计算校验和,写入校验和到对象中,写入先是写入内存对象中,当内存对象满或者达到一定条件后,写入临时文件以及根据网络配置情况传输到网络上的hdfs中,这个过程也相对较为省略了,请读者自己研究源码了,看懂了数据写入过程在看那个过程非常类似,比较容易看懂因此省略了)

7. 校验和写入完毕后,写入数据到DFSOS(DFSClient$DFSOutputStream)对象中

8. DFSOS(DFSClient$DFSOutputStream)首先将数据写入内存缓存

9. 数据条件满足时,写入临时文件中

10.当数据写满了一块数据后,调用endblock函数

11.endblock函数处理临时文件等,然后调用nextblockoutputstream函数

12.在nextblockoutputstream函数中,首先同namenode进行沟通来创建文件(若是首次通信),或者添加一块数据,最后获取块的元信息

13.根据块的元信息连接datanode节点

14.传输数据到datanode节点

15.处理传输后相关逻辑

17.最后程序依次返回调用函数处,然后程序继续读取下一部分数据重复上面过程,直到数据都发送完毕

18.程序关闭阶段,关闭阶段处理了内存中的缓存数据,处理本地临时文件等

19.程序最后返回给用户一个执行情况结果。

以上是hadoop shell上传文件到datanode过程的一个总结,请结合前面文章慢慢了解工作过程。若是有时间请下载相关虚拟机及eclipse项目调试整个过程,读者会获取更多理解。

 

参考文章
Hadoop源代码分析 之Datanode工作原理(5)—–拷贝文件过程总结

Hadoop源代码分析 之Datanode工作原理(4)—–拷贝本地文件到hadoop的过程

Hadoop源代码分析 之Datanode工作原理(3)—–datanode工作过程总结

hadoop源代码介绍

Hadoop源代码分析 之Datanode工作原理(2)—–datanode基本工作过程

Hadoop源代码分析 之Datanode工作原理(1)—–datanode启动过程代码分析

Hadoop源代码分析 之hadoop配置及启动(4)—–启动过程汇总

Hadoop源代码分析 之hadoop配置及启动(3)—–classpath与hadoop主要组件启动过程

Hadoop源代码分析 之hadoop配置及启动(2)—–classpath与启动shell脚本

Hadoop源代码分析 之hadoop配置及启动(1)—–classpath与配置文件

Hadoop源代码分析 之hadoop源代码项目(1)—–创建eclipse下java项目

Hadoop源代码分析 之环境配置(2)—–虚拟机ip配置

Hadoop源代码分析 之环境配置(1)—–hadoop虚拟机配置

Hadoop源代码分析 之概念介绍(2)—–初学者眼中的hadoop

Hadoop源代码分析 之概念介绍(1)—–服装加工,火车货物检查与hadoop

调试eclipse下hadoop的map reduce程序