Winse Blog

走走停停都是风景, 熙熙攘攘都向最好, 忙忙碌碌都为明朝, 何畏之.

Windows下部署/配置/调试hadoop2

Windows作为开发屌丝必备,在windows上如何跑集群方便开发调试,以及怎么把eclipse写好的任务mapreduce提交到测试的集群(linux)上面去跑?这些都是需要直面并解决的问题。

本文主要记录在windows上hadoop集群的环境准备,以及eclipse调试功能等。

  1. windows伪分布式部署
    • cmd
    • cygwin shell
  2. windows-eclipse提交任务到linux集群
  3. 导入源码到eclipse

这篇文章并非按照操作的时间顺序来进行编写。而是,如果再安装第二遍的话,自己应该如何去操作来组织下文。

一、Windows伪分布式部署

尽管一直用windows,但是对windows自带的cmd命令很是不屑!想在cygwin下部署,现在想来,最终用的是windows的java!在cygwin下不就是把路径转换后再传给java执行吗!

所以,如果把cygwin环境搭建好了的话,其实已经把windows的环境也搭建好了!同样hadoop的windows环境配置好了,cygwin环境也同样配置好了。但是,在cygwin下面提交mapreduce任务会有各种”凌乱”的问题!

先说说在windows环境搭建的步骤,然后再讲cygwin下运行。

  1. 需要用到的软件环境
  2. 编译windows环境变量配置
  3. 编译hadoop-common源代码生成本地依赖库
  4. 伪分布式配置
  5. windows下运行
  6. cygwin下运行

1.1 需要用到的软件环境

  • Win7-x86
  • hadoop-2.2.0.tar.gz
  • git
  • cygwin (源码编译时需要执行sh命令)
  • visual studio 2010(如果与.net framework4有关的问题请查阅: [*] [*] [*]
  • protoc(protoc-2.5.0-win32.zip)(解压,然后把路径加入到PATH)

搭建环境之前,建议您看看wiki-Hadoop2OnWindows。最终有用的步骤都在上面了!不过在自己瞎折腾的过程中也弄了不少东西,记录下来!

1.2 编译windows环境变量配置

变量 windows
Platform Win32
ANT_HOME D:\local\usr\apache-ant-1.9.0
MAVEN_HOME D:\local\usr\apache-maven-3.0.4
JAVA_HOME D:\Java\jdk1.7.0_02
PATH C:\cygwin\bin;C:\protoc;D:\local\usr\apache-maven-3.0.4\bin;D:\local\usr\apache-ant-1.9.0/bin;D:\Java\jdk1.7.0_02\bin;%PATH%

编译时,在打开的命令行加入cygwin的路径即可。 在maven编译最后需要用到sh的shell命令,需要把c:\cygwin\bin目录加入到path环境变量。 这里先不配置hadoop的环境变量,因为我只需要用到编译后的本地库而已!!

1.3 编译源代码生成本地依赖库(dll, exe)

hadoop2.2.0操作本地文件针对平台的进行了处理。也就是只要在windows运行集群,不管怎么样,你都得先把winutils.exe、hadoop.dll编译出来,用来处理对本地文件赋权、软链接等(类似Linux-Shell的功能)。否则会看到下面的错误:

  • 命令执行出错,少了winutils.exe
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
14/04/14 20:07:58 ERROR util.Shell: Failed to locate the winutils binary in the hadoop binary path
java.io.IOException: Could not locate executable null\bin\winutils.exe in the Hadoop binaries.
  at org.apache.hadoop.util.Shell.getQualifiedBinPath(Shell.java:278)
  at org.apache.hadoop.util.Shell.getWinUtilsPath(Shell.java:300)
  at org.apache.hadoop.util.Shell.<clinit>(Shell.java:293)

14/04/17 21:22:32 INFO service.AbstractService: Service org.apache.hadoop.yarn.server.nodemanager.LocalDirsHandlerServic
e failed in state INITED; cause: java.lang.NullPointerException
java.lang.NullPointerException
      at java.lang.ProcessBuilder.start(ProcessBuilder.java:1010)
      at org.apache.hadoop.util.Shell.runCommand(Shell.java:404)
      at org.apache.hadoop.util.Shell.run(Shell.java:379)
      at org.apache.hadoop.util.Shell$ShellCommandExecutor.execute(Shell.java:589)
      at org.apache.hadoop.util.Shell.execCommand(Shell.java:678)
      at org.apache.hadoop.util.Shell.execCommand(Shell.java:661)
      at org.apache.hadoop.fs.RawLocalFileSystem.setPermission(RawLocalFileSystem.java:639)
      at org.apache.hadoop.fs.RawLocalFileSystem.mkdirs(RawLocalFileSystem.java:435)
  • 少了hadoop.dll的本地库文件
1
2
3
4
5
6
7
8
9
10
11
14/04/17 21:30:27 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-ja
va classes where applicable
14/04/17 21:30:29 FATAL datanode.DataNode: Exception in secureMain
java.lang.UnsatisfiedLinkError: org.apache.hadoop.io.nativeio.NativeIO$Windows.access0(Ljava/lang/String;I)Z
      at org.apache.hadoop.io.nativeio.NativeIO$Windows.access0(Native Method)
      at org.apache.hadoop.io.nativeio.NativeIO$Windows.access(NativeIO.java:435)
      at org.apache.hadoop.fs.FileUtil.canRead(FileUtil.java:977)
      at org.apache.hadoop.util.DiskChecker.checkAccessByFileMethods(DiskChecker.java:177)
      at org.apache.hadoop.util.DiskChecker.checkDirAccess(DiskChecker.java:164)
      at org.apache.hadoop.util.DiskChecker.checkDir(DiskChecker.java:147)
      at org.apache.hadoop.hdfs.server.datanode.DataNode$DataNodeDiskChecker.checkDir(DataNode.java:1698)

下载源码进行编译

下面需要用到visual studio修改项目配置信息(或者直接修改sln文件也行),然后再使用maven进行编译。

这里仅编译hadoop-common项目,最后把生成winutils.exe/hadoop.dll放到hadoop程序bin目录下。

第一步 下载源码

1
2
3
4
5
6
7
/*  https://github.com/apache/hadoop-common.git  */

Administrator@WINSELIU /e/git/hadoop-common (master)
$ git checkout branch-2.2.0
Checking out files: 100% (5536/5536), done.
Branch branch-2.2.0 set up to track remote branch branch-2.2.0 from origin.
Switched to a new branch 'branch-2.2.0'

第二步 应用补丁patch-native-win32

jira: https://issues.apache.org/jira/browse/HADOOP-9922
patch: https://issues.apache.org/jira/secure/attachment/12600760/HADOOP-9922.patch

native.sln-patch有点问题,下面通过vs修改,使用Visual Studio修改native的活动平台

第三步 在Visual Studio 命令提示(2010)命令行进行Maven编译(仅需编译hadoop-common)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
E:\git\hadoop-common\hadoop-common-project\hadoop-common>mvn package -Pdist,native-win -DskipTests -Dtar -Dmaven.javadoc.skip=true

/*  native files  */

Administrator@winseliu /cygdrive/e/git/hadoop-common/hadoop-common-project/hadoop-common
$ ls -1 target/bin/
hadoop.dll
hadoop.exp
hadoop.lib
hadoop.pdb
libwinutils.lib
winutils.exe
winutils.pdb

Administrator@winseliu /cygdrive/e/git/hadoop-common/hadoop-common-project/hadoop-common
$ cp target/bin/* ~/hadoop/bin/

windows的本地库的路径就是PATH环境变量。所以windows下最好还是把dll放到bin目录下,同时把HADOOP_HOME/bin加入到环境变量中!! 修改PATH环境变量。

可以把dll放到自定义的位置,但是同样最好把该路径加入到PATH环境变量。java默认会到PATH路径下找动态链接库dll。

1.4 修改hadoop配置,部署伪分布式环境

可以直接把linux伪分布式的配置cp过来用。然后修改namenode/datanode/yarn文件的存储路径就可以了。 这里有个坑,hdfs-default.xml中的路径前面都加了file://前缀!所以hdfs配置中涉及到路径的,这里都得进行了修改。

Notepad++的Ctrl+D是一个好功能啊

属性
slaves
localhost
core-site.xml
fs.defaultFS hdfs://localhost:9000
io.file.buffer.size 10240
hadoop.tmp.dir file:///e:/tmp/hadoop
hdfs-site.xml
dfs.replication 1
dfs.namenode.secondary.http-address localhost:9001 #设置为空可以禁用
dfs.namenode.name.dir ${hadoop.tmp.dir}/dfs/name
dfs.datanode.data.dir ${hadoop.tmp.dir}/dfs/data
dfs.namenode.checkpoint.dir ${hadoop.tmp.dir}/dfs/namesecondary
dfs.namenode.shared.edits.dir ${hadoop.tmp.dir}/dfs/shared/edits
mapred-site.xml
mapreduce.framework.name yarn
mapreduce.jobhistory.address localhost:10020
mapreduce.jobhistory.webapp.address localhost:19888
yarn-site.xml
yarn.nodemanager.aux-services mapreduce_shuffle
yarn.nodemanager.aux-services.mapreduce_shuffle.class org.apache.hadoop.mapred.ShuffleHandler
yarn.resourcemanager.address localhost:8032
yarn.resourcemanager.scheduler.address localhost:8030
yarn.resourcemanager.resource-tracker.address localhost:8031
yarn.resourcemanager.admin.address localhost:8033
yarn.resourcemanager.webapp.address localhost:8088
yarn.application.classpath %HADOOP_CONF_DIR%, %HADOOP_COMMON_HOME%/share/hadoop/common/, %HADOOP_COMMON_HOME%/share/hadoop/common/lib/, %HADOOP_HDFS_HOME%/share/hadoop/hdfs/, %HADOOP_HDFS_HOME%/share/hadoop/hdfs/lib/, %HADOOP_YARN_HOME%/share/hadoop/yarn/, %HADOOP_YARN_HOME%/share/hadoop/yarn/lib/

注意点:

  • yarn.application.classpath必须定义!尽管程序中有判断不同平台的默认值不同,但是在yarn-default.xml中已经有值了!
    • yarn.application.classpath对启动程序没影响,但是在运行mapreduce时影响巨大破坏力极强!
  • 自定library的路径是个坑!!
    • 在windows下,执行java程序java.library.path默认到PATH路径找。这也是需要定义环境变量HADOOP_HOME,以及把bin加入到PATH的原因吧!

1.5 Windows直接运行cmd启动

如果是用windows的cmd的话,到这里已经基本ok了!格式化namenodehadoop namenode -format),启动就ok了! 发现自己其实很傻×,固执的要用cygwin启动运行!用windows的cmd启动,然后用cygwin的终端查看数据不就行了!两不耽误!

cmd命令默认是去bin目录下找hadoop.dll的,同时hadoop命令会把bin加入到java.library.path路径下。再次强调/推荐:直接把hadoop.dll放到bin路径。 设置环境变量,启动文件系统:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* **设置环境变量** */
HADOOP_HOME=E:\local\libs\big\hadoop-2.2.0 
PATH=%HADOOP_HOME%\bin;%PATH%

/* 格式化namenode */
hadoop namenode -format

/* 操作HDFS */
set HADOOP_ROOT_LOGGER=DEBUG,console

E:\local\libs\big\hadoop-2.2.0>sbin\start-dfs.cmd

E:\local\libs\big\hadoop-2.2.0>hdfs dfs -put README.txt /   # 很弱,fs简化操作都不兼容!

E:\local\libs\big\hadoop-2.2.0>hdfs dfs -ls /
Found 1 items
-rw-r--r--   1 Administrator supergroup       1366 2014-04-22 22:20 /README.txt

JAVA_HOME的路径中最好不要有空格!否则测试下面的方式进行处理:

instead e.g. c:\Progra~1\Java… instead of c:\Program Files\Java.…

好处也是明显的,直接是windows执行,可以使用jdk自带的工具查看运行情况。

?疑问: log日志都写在hadoop.log文件中了?反正我是没看到hadoop.log的文件!

HDFS操作文件OK,如果按照上面步骤或者官网的wiki操作,则运行mapreduce也是不会出问题的!!

1
2
3
4
5
6
7
8
E:\local\libs\big\hadoop-2.2.0>sbin\start-yarn.cmd

E:\local\libs\big\hadoop-2.2.0>hadoop org.apache.hadoop.examples.WordCount /README.txt /out

E:\local\libs\big\hadoop-2.2.0>hdfs dfs -ls /out
Found 2 items
-rw-r--r--   1 Administrator supergroup          0 2014-04-22 22:22 /out/_SUCCESS
-rw-r--r--   1 Administrator supergroup       1306 2014-04-22 22:22 /out/part-r-00000

如果你使用上面的hadoop命令执行不了命令,请把hadoop.cmd的换行(下载下来后是unix的)转成windows的换行!

问题原因分析

如果你运行mapreduce失败,不外乎三种情况:没有定义HADOOP_HOME系统环境变量,hadoop.dll没有放在PATH路径下,以及yarn.application.classpath没有设置。这三个问题导致。如果你不幸碰到了,那我们如何来确认问题呢?

下面一步步的来解读这个处理过程。在运行mapreduce时报错,可以使用远程调试方式来确认发生的具体位置。(如果你还没有弄好本地开发环境,请先看[三、导入源码到eclipse])

第一步 调试NodeManager,从根源下手

由于windows的hadoop的程序都是直接运行的,不像linux还要ssh再登陆然后在启动。所以这里直接设置HADOOP_NODEMANAGER_OPTS就可以了。

1
2
3
4
5
6
set HADOOP_NODEMANAGER_OPTS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8092"

E:\local\libs\big\hadoop-2.2.0\sbin\start-yarn.cmd
starting yarn daemons

E:\>hadoop org.apache.hadoop.examples.WordCount /in /out

运行任务之前,在ContainerLaunch#call#171行打个断点(可以查看执行的java命令脚本内容,#254writeLaunchEnv写入cmd文件)。同时可以去到nm-local-dir/nmPrivate目录下查看任务的本地临时文件。application_XXX/containter_XXX/launch_container.cmd文件是MRAppMaster/YarnChild/YarnChild的启动脚本。

  • 调试。备份生成的脚本文件,开启死循环拷贝模式,把缓存留下来慢慢看

      while true ; do cp -rf nm-local-dir/ backup/ ; sleep 0.5; done
    

  • 查看缓存文件

    • 真正启动Mapreduce(yarnchild)的脚本文件launch_container.cmd
    • 查看系统日志,确定错误

    • classpath路径

    • Job任务类型。第三个参数!

这里可以查看脚本,确认HADOOP的相关目录是否正确!以及查看classpath的MANIFEST.MF查看依赖的jar是否完整!也可以通过任务的名称了解相关信息。

  • 路径问题,不影响大局(可以不关注/不修改)

  • 调试map/reduce

调试程序mapreduce比较好办了,毕竟代码都是自己写的好弄。可以使用mrunit。

map和reduce的进程都是动态的,既不能通过命令行的OPTS参数指定。如果要调试map/reduce需要在opts中传递给它们。

1
hadoop org.apache.hadoop.examples.WordCount  "-Dmapreduce.map.java.opts=-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=8090" /in /out
  • library问题

如果因为library的问题报access$0的错,提交任务都不成功,可以把自定义的dll路径加入java.library.path尝试一下。

1
hadoop org.apache.hadoop.examples.WordCount "-Dmapreduce.map.java.opts= -Djava.library.path=E:\local\libs\big\hadoop-2.2.0\lib\native" "-Dmapreduce.reduce.java.opts=-Djava.library.path=E:\local\libs\big\hadoop-2.2.0\lib\native"  /in /out

1.6 cygwin下运行

要在cygwin下面把hadoop弄起来,你要把cygwin与java的路径区分,理清楚路径,配置工作就成功一半咯!既然用的还是windows的java程序。配置文件也是最终提供给java执行的,所以配置都不需要修改。

要在cygwin中运行hadoop,仅仅搞定脚本就ok了!在执行java命令之前,把cygwin的路径转换为windows。

  • 修改了hadoop-env.sh的内容:
1
2
3
export JAVA_HOME=/cygdrive/d/Java/jdk1.7.0_02 #本来已经在环境变量中定义了,但是执行后台批处理的时刻不会调用环境变量的配置!
export HADOOP_HEAPSIZE=512
export HADOOP_PID_DIR=${HADOOP_PID_DIR:-${HADOOP_LOG_DIR}}

cygwin也就是linux的默认加载native的路径是libs/native!!拷一份过去把!!或者配置JAVA_LIBRARY_PATH,参见下面的修改Shell脚本部分。

cygwin自带的工具有个优势:运行脚本和java命令都不出现乱码。(或许把SecureCRT改成GBK编码也行)

  • 修改shell脚本命令

由于java在windows和linux在识别文件路径上也有差异。如/data传给java,在windows会加上当前路径的盘符(e.g. E),那写入数据目录就为e:/data

同时,不同操作系统的classpath的组织方式也不同。(1)需要对classpath已经文件夹的路径进行转换,才能在cygwin下正常的运行java程序。 所以,只要在执行java命令之前对路径和classpath进行转换即可。(2)还需要对getconf返回值的换行符进行处理。涉及到下列的文件:

1
2
3
4
5
6
7
libexec/hadoop-config.sh
bin/hadoop
bin/hdfs
bin/mapred
bin/yarn
sbin/start-dfs.sh
sbin/stop-dfs.sh

重点修改两个问题如下:

  • 配置
1
2
3
4
/* hadoop-config.sh */

# 定义时注意,处理cygwin路径时只处理了以/cygdrive开头的路径! 
export JAVA_LIBRARY_PATH=/cygdrive/e/local/libs/big/hadoop-2.2.0/bin

由于windows配置时,把hadoop.dll的动态链接库放到bin目录下,而linux(cygwin)的sh脚本默认是去lib/native下面,所以需要定义一下链接库的查找路径。

  • 脚本
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
/* hadoop-config.sh */

/* 在调用java命令前,调用该方法 */
function Cygwin_Patch_PathConvert() {

  cygwin=false
  case "`uname`" in
  CYGWIN*) cygwin=true;;
  esac

  # cygwin path translation
  if $cygwin; then
      CLASSPATH=`cygpath -p -w "$CLASSPATH"`
      # ssh过来执行命令是不从.bash_profile获取参数!
      if [ "X$HADOOP_HOME" != "X" ]; then
          HADOOP_HOME=`cygpath -w "$HADOOP_HOME"`
      fi
      HADOOP_LOG_DIR=`cygpath -w "$HADOOP_LOG_DIR"`
      if [ "X$TOOL_PATH" != "X" ]; then
          TOOL_PATH=`cygpath -p -w "$TOOL_PATH"`
      fi
      
HADOOP_COMMON_HOME=`cygpath -w "$HADOOP_COMMON_HOME"`
JAVA_HOME=`cygpath -w "$JAVA_HOME"`
HADOOP_YARN_HOME=`cygpath -w "$HADOOP_YARN_HOME"`
HADOOP_HDFS_HOME=`cygpath -w "$HADOOP_HDFS_HOME"`
HADOOP_CONF_DIR=`cygpath -w "$HADOOP_CONF_DIR"`

# HOME
      
      # 把带/cygdrive/[abc]形式的路径转换为windows路径
      HADOOP_OPTS=`echo $HADOOP_OPTS | awk -F" " '{for(i=1;i<=NF;i++)print $i}' | awk -F"=" ' {if($2~/^\/cygdrive\/[a|b|c|d|e]/){print $1;system("cygpath -w -p " $2 )}else{ print $0 }; print ""}' | awk 'BEGIN{opt="";last=""}{if($0~/^$/){ opt=opt " "; last="" }else{ if(last!=""){ opt=opt "="} opt=opt $0; last=$0; }; }END{ print opt }' `
      
      YARN_OPTS=`echo $YARN_OPTS | awk -F" " '{for(i=1;i<=NF;i++)print $i}' | awk -F"=" ' {if($2~/^\/cygdrive\/[a|b|c|d|e]/){print $1;system("cygpath -w -p " $2 )}else{ print $0 }; print ""}' | awk 'BEGIN{opt="";last=""}{if($0~/^$/){ opt=opt " "; last="" }else{ if(last!=""){ opt=opt "="} opt=opt $0; last=$0; }; }END{ print opt }' `

      JAVA_LIBRARY_PATH=`cygpath -p -w "$JAVA_LIBRARY_PATH"`
      
  fi
}

/* 系统的换行符不同,需要转换 */
SECONDARY_NAMENODES=$($HADOOP_PREFIX/bin/hdfs getconf -secondarynamenodes 2>/dev/null | sed 's/^M//g' )

在解析OPTS时执行cygpath转换的时刻,也需要加上-p的参数!OPTS中有java.library.path的环境变量!

  • HDFS文件系统测试
1
2
3
bin/hadoop namenode -format
sbin/start-dfs.sh
ps

jps没有作用了;或者也可以通过任务管理器/ProcessExplorer查看java.exe,命令行列还可以查看具体的执行命令,对应的什么服务。

映像名称 用户名 命令行
java.exe Administrator D:\Java\jdk1.7.0_02\bin\java.exe -Dproc_namenode -Xmx512m … org.apache.hadoop.hdfs.server.namenode.NameNode
java.exe Administrator D:\Java\jdk1.7.0_02\bin\java.exe -Dproc_datanode -Xmx512m … org.apache.hadoop.hdfs.server.datanode.DataNode
java.exe Administrator D:\Java\jdk1.7.0_02\bin\java.exe -Dproc_secondarynamenode … org.apache.hadoop.hdfs.server.namenode.SecondaryNameNode

修改了hadoop的脚本,启动环境(cygwin下启动和windows启动都可以),就可以操作HDFS了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Administrator@winseliu ~
$ hadoop/bin/hadoop fs -put job.xml /
14/04/22 23:53:31 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable

Administrator@winseliu ~
$ export JAVA_LIBRARY_PATH=/cygdrive/e/local/libs/big/hadoop-2.2.0/bin

Administrator@winseliu ~
$ hadoop/bin/hadoop fs -ls /
Found 4 items
-rw-r--r--   1 Administrator supergroup       1366 2014-04-22 22:20 /README.txt
-rw-r--r--   1 Administrator supergroup      66539 2014-04-22 23:53 /job.xml
drwxr-xr-x   - Administrator supergroup          0 2014-04-22 23:34 /out
drwx------   - Administrator supergroup          0 2014-04-22 22:21 /tmp

如果执行权限问题,可以使用设置HADOOP_USER_NAME的方式处理:

1
2
3
4
5
Administrator@winseliu ~/hadoop
$ export HADOOP_USER_NAME=Administrator

Administrator@winseliu ~/hadoop
$ bin/hadoop fs -rmr /out

MapReduce任务测试

1
2
sbin/start-yarn.sh
ps

yarn资源框架启动后,任务管理又会添加两个java的程序:

映像名称 用户名 命令行
java.exe Administrator D:\Java\jdk1.7.0_02\bin\java.exe -Dproc_resourcemanager … org.apache.hadoop.yarn.server.resourcemanager.ResourceManager
java.exe Administrator D:\Java\jdk1.7.0_02\bin\java.exe -Dproc_nodemanager … org.apache.hadoop.yarn.server.nodemanager.NodeManager

提交任务,执行任务处理

在cygwin环境下,hdfs和yarn都启动成功了,并且能传文件到HDFS中。但是由于cygwin环境最终还是使用windows的java程序集群执行任务!

(可考虑[2.2 Eclipse提交MapReduce])

  • 已处理问题一: cygwin下启动nodemanager,路径没转换

由于在cygwin下面启动,大部分的环境变量都是从cygwin带过来的!解析conf中的变量时会使用nodemanager中对应变量的值,如HADOOP_MAPRED_HOME等。

在cygwin使用start-yarn.sh调用java启动程序之前需要转换路径为windows下的路径。在上面的操作已经进行了处理。

1
2
3
4
5
6
7
8
9
# 在临时目录下生成了launch_container.cmd文件,用于执行命令,而里面环境变量的值有些cygwin环境下的!

# 设置端口调试nodemanager
Administrator@winseliu ~/hadoop
$ grep "8092" etc/hadoop/*
etc/hadoop/yarn-env.sh:export YARN_NODEMANAGER_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8092"

## windows下这个方法大有文章,会把客户端传递的CLASSPATH写入jar的MANIFEST.MF中!
org.apache.hadoop.yarn.server.nodemanager.containermanager.launcher.ContainerLaunch.sanitizeEnv()
  • 已处理问题二:执行mapreduce任务时,缺少环境变量(使用Process Explorer工具查看)
1
2
3
4
5
6
7
8
9
10
11
12
# 设置远程调试map
hadoop org.apache.hadoop.examples.WordCount  "-Dmapreduce.map.java.opts=-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=8090" /job.xml /out

# mapred-site.xml设置超时时间
  <property>
      <name>mapred.task.timeout</name>
      <value>1800000</value>
  </property>

# 结束任务
Administrator@winseliu ~/hadoop
$ bin/hadoop job -kill job_1398407971082_0003
  • 取不到HADOOP_HOME环境变量,查找winutils.exe时报错!
    • 在hadoop-env.sh中增加定义HADOOP_HOME!
  • library路径问题,解析动态链接库hadoop.dll失败!
    • 增加-D参数吧!
1
hadoop org.apache.hadoop.examples.WordCount "-Dmapreduce.map.java.opts= -Djava.library.path=E:\local\libs\big\hadoop-2.2.0\bin" "-Dmapreduce.reduce.java.opts=-Djava.library.path=E:\local\libs\big\hadoop-2.2.0\bin"  /job.xml /out

windows泽腾啊。

  • 问题二:直接提交任务到linux集群,环境变量不匹配
1
2
3
4
5
6
7
8
Administrator@winseliu ~/hadoop
$ bin/hadoop  fs -ls hdfs://192.168.1.104:9000/

Administrator@winseliu ~/hadoop
$ export HADOOP_USER_NAME=hadoop

Administrator@winseliu ~/hadoop
$  bin/hadoop  org.apache.hadoop.examples.WordCount   -fs hdfs://192.168.1.104:9000 -jt 192.168.1.104 /in /out

由于本地是windows的java执行任务提交到集群,所以使用了%JAVA_HOME%,以及windows下的CLASSPATH!执行任务时,同时把nodemanager节点的临时目录备份下来再慢慢查看:

1
2
[hadoop@slave temp]$ while true ; do cp -rf nm-local-dir/ backup/ ; sleep 0.1; done
[hadoop@slave temp]$ find . -name "*.sh"

修复该问题,可以参考[2.2 Eclipse提交MapReduce]。

参考


二、Windows下使用eclipse连接linux集群

2.1 java代码操作HDFS

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
public class HelloHdfs {

  public static boolean FINISH_CLEAN = true;

  public static void main(String[] args) throws IOException {
      System.setProperty("HADOOP_USER_NAME", "hadoop"); // 设置用户,否则会有读取权限的问题
      
      FileSystem fs = FileSystem.get(new Configuration());

      fs.mkdirs(new Path("/java/folder"));
      OutputStream os = fs.create(new Path("/java/folder/hello.txt"));
      Writer w = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
      w.write("hello hadoop!");
      w.flush();
      w.close();
      os.close();

      FSDataInputStream is = fs.open(new Path("/java/folder/hello.txt"));
      BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
      System.out.println(br.readLine());
      br.close();
      is.close();

      // IOUtils.copyBytes(in, out, 4096, true);

      if (FINISH_CLEAN)
          fs.delete(new Path("/java"), true);
  }

}

对于访问linux集群的hdfs,只要编译通过,对集群HDFS文件系统的CRUD基本没有不会遇到什么问题。写代码过程中遇到过下面两个问题:

  • 如果你也引入了hive的包,可能会抛不能重写final方法的错误!由于hive中也就了proto的代码(final),调整下顺序先加载proto的包就可以了!

      log4j:WARN Please initialize the log4j system properly.
      Exception in thread "main" java.lang.VerifyError: class org.apache.hadoop.security.proto.SecurityProtos$CancelDelegationTokenRequestProto overrides final method getUnknownFields.()Lcom/google/protobuf/UnknownFieldSet;
          at java.lang.ClassLoader.defineClass1(Native Method)
          at java.lang.ClassLoader.defineClass(ClassLoader.java:791)
    
  • Permission denied: user=Administrator, access=WRITE, inode=“/”:hadoop:supergroup:drwxr-xr-x 这个问题的处理方式有很多。

    • hadoop fs -chmod 777 /
    • 在hdfs的配置文件中,将dfs.permissions修改为False
    • System.setProperty(“user.name”, “hduser”)/System.setProperty(“HADOOP_USER_NAME”, “hduser”)/configuration.set(“hadoop.job.ugi”, “hduser”);

2.2 Eclipse提交MapReduce

  • 需要设置HADOOP_HOME/hadoop.home.dir的环境变量,即在该目录下面有bin\winutils.exe的文件。否则会报错:

      14/04/14 20:07:57 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
      14/04/14 20:07:58 ERROR util.Shell: Failed to locate the winutils binary in the hadoop binary path
      java.io.IOException: Could not locate executable null\bin\winutils.exe in the Hadoop binaries.
          at org.apache.hadoop.util.Shell.getQualifiedBinPath(Shell.java:278)
          at org.apache.hadoop.util.Shell.getWinUtilsPath(Shell.java:300)
          at org.apache.hadoop.util.Shell.<clinit>(Shell.java:293)
    
  • 任务端(map/reduce)执行命令的classpath变量在客户端Client拼装的!

    浏览官网的jira,然后下载并应用MRApps.patchYARNRunner.patch两个补丁。

    其实就是修改Apps#addToEnvironment(Map<String, String>, String, String)来拼装特定操作系统的classpath。以及JAVA_HOME等一些环境变量的值($JAVA_HOME or %JAVA_HOME%

    使用patch -p1 < PATCH进行修复。如果patch文件不在项目根路径,可以删除补丁内容前面文件夹路径,直接与源文件放一起然后应用patch就行了。当然你根据修改的内容手动修改也是OK的。

如果仅仅是作为客户端client提交任务时使用。如仅在eclipse中运行main提交任务,那么就没有必要打包!直接放到需要项目源码中即可。

* 把应用了补丁的YARNRunner和MRApps加入到项目中
* 然后再configuration中加入`config.set("mapred.remote.os", "Linux")`
* 把mapreduce的任务打包为jar,然后`job.setJar("helloyarn.jar")`
* 最后`Run As -> Java Application`运行提交

如果很多项目使用,可以打包出来,然后把它添加到classpath中,同时添加加入自定义的xml配置。

1
2
3
4
5
6
7
8
lib-ext>jar tvf window-client-mapreduce-patch.jar
  25 Wed Apr 16 11:21:26 CST 2014 META-INF/MANIFEST.MF
 26684 Wed Apr 16 10:10:16 CST 2014 org/apache/hadoop/mapred/YARNRunner.class
 24397 Tue Apr 15 10:32:28 CST 2014 org/apache/hadoop/mapred/YARNRunner.java
  1406 Wed Apr 16 10:10:16 CST 2014 org/apache/hadoop/mapreduce/v2/util/MRApps$1.class
  2450 Wed Apr 16 10:10:16 CST 2014 org/apache/hadoop/mapreduce/v2/util/MRApps$TaskAttemptStateUI.class
 19887 Wed Apr 16 10:10:16 CST 2014 org/apache/hadoop/mapreduce/v2/util/MRApps.class
 18879 Tue Apr 15 11:42:42 CST 2014 org/apache/hadoop/mapreduce/v2/util/MRApps.java

参考:


三、导入源码到eclipse

环境

参考前面的【window伪分布式部署】

打开Visual Studio的命令行工具

1
启动\所有程序\Microsoft Visual Studio 2010\Visual Studio Tools\Visual Studio 命令提示(2010)

获取源码,检查2.2.0的分支

1
2
git clone git@github.com:apache/hadoop-common.git
git checkout branch-2.2.0

也可以下载src的源码包,但是如果想修改点东西的话,clone源码应该是最佳的选择了。

编译生成打包

1
2
set PATH=c:\cygwin\bin;%PATH%
mvn package -Pdist,native-win -DskipTests -Dmaven.javadoc.skip=true

最好加上skipTests条件,不然编译等待时间不是一般的长!!

导入eclipse

1
mvn eclipse:eclipse

然后使用eclipse导入已经存在的工程(existing projects into workspace),导入后存在两个问题:

  1. stream工程的conf源码包找不到。修改为在.project文件中引用,然后把conf引用加入到.classpath。
  2. common下的test代码报错。把target/generated-test-sources/java文件夹的也作为源码包即可。

eclipse的maven插件你得安装了(要用到M2_REPO路径),同时引用正确conf\settings.xml的Maven配置路径。

注意: 不要使用eclipse导入已经存在的maven方式!eclipse的m2e有些属性和插件还不支持,导入后会报很多错!而使用mvn eclipse:eclipse的方式是把依赖的jar加入到.classpath

参考


四、胡乱噗噗

查看Debug日志

1
2
[hadoop@umcc97-44 ~]$ export HADOOP_ROOT_LOGGER=DEBUG,console
[hadoop@umcc97-44 ~]$ hadoop fs -ls /

java加载动态链接库的环境变量java.library.path

1
2
3
4
5
6
7
8
9
10
D:\local\cygwin\Administrator\test>java LoadLib

D:\local\cygwin\Administrator\test>java -Djava.library.path=. LoadLib
Exception in thread "main" java.lang.UnsatisfiedLinkError: no hadoop in java.library.path
        at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1860)
        at java.lang.Runtime.loadLibrary0(Runtime.java:845)
        at java.lang.System.loadLibrary(System.java:1084)
        at LoadLib.main(LoadLib.java:3)

D:\local\cygwin\Administrator\test>java -Djava.library.path=".;%PATH%" LoadLib

没有定义的时刻,会去PATH路径下找。一旦定义了java.library.path只会在给定的路径下查找!

hadoop的本地native-library的位置

文件具体放什么位置,随便运行一个命令,通过debug的日志就可以看到默认Library的路径。

1
2
3
4
5
6
7
8
Administrator@winseliu ~/hadoop
$ export HADOOP_ROOT_LOGGER=DEBUG,console

Administrator@winseliu ~/hadoop
$ bin/hadoop fs -ls /

14/04/18 09:48:39 DEBUG util.NativeCodeLoader: java.library.path=E:\local\libs\big\hadoop-2.2.0\lib\native
14/04/18 09:48:39 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable

cygwin下运行java程序,路径问题

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
Administrator@winseliu ~/test
$ ls
ENV.class  ENV.java  w3m-0.5.2

Administrator@winseliu ~/test
$ java ENV
Windows 7

Administrator@winseliu ~/test
$ jar cvf test.jar *.class
已添加清单
正在添加: ENV.class(输入 = 475) (输出 = 307)(压缩了 35%)

Administrator@winseliu ~/test
$ ls -l
总用量 18
-rwxr-xr-x  1 Administrator None 475 四月  4 15:00 ENV.class
-rw-r--r--  1 Administrator None 117 四月  4 15:00 ENV.java
-rwxr-xr-x  1 Administrator None 758 四月 19 12:03 test.jar
drwxr-xr-x+ 1 Administrator None   0 四月 12 20:31 w3m-0.5.2

Administrator@winseliu ~/test
$ java -cp test.jar ENV
Windows 7

Administrator@winseliu ~/test
$ java -cp /home/Administrator/test/test.jar ENV
错误: 找不到或无法加载主类 ENV

Administrator@winseliu ~/test
$ set -x

Administrator@winseliu ~/test
$ java -cp `cygpath -w /home/Administrator/test/test.jar` ENV
++ cygpath -w /home/Administrator/test/test.jar
+ java -cp 'D:\local\cygwin\Administrator\test\test.jar' ENV
Windows 7

[cygwin]ssh单独用户权限问题

1
2
3
Administrator@winseliu ~
$ hadoop/bin/hadoop  fs -put .bash_profile /bash.info
put: Permission denied: user=Administrator, access=WRITE, inode="/":cyg_server:supergroup:drwxr-xr-x
  • 设置环境变量HADOOP_USER_NAME=hadoop
  • 可以使用dfs.permissions属性设置为false。
  • 给位置chown/chmod赋权: hadoop fs -chmod 777 /
  • 也可以使用ssh-host-config的Should privilege separation be used? (yes/no) no设置为no。使用当前用户进行管理。 Administrator@winseliu /var $ chown Administrator:None empty/ Administrator@winseliu ~ $ /usr/sbin/sshd.exe # 启动,也可以弄个脚本到启动项,开机启动 Administrator@winseliu ~/hadoop $ ps | grep ssh 4384 1 4384 4384 ? 500 02:41:21 /usr/sbin/sshd

Visual Studio处理winutils工程

[cygwin]ipv6的问题,改成ipv4后不能登陆!

可能是新版本的openssh的bug!!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Administrator@winseliu /cygdrive/h/documents
$ ssh -o AddressFamily=inet localhost -v
OpenSSH_6.5, OpenSSL 1.0.1g 7 Apr 2014
debug1: Reading configuration data /etc/ssh_config
debug1: Connecting to localhost [127.0.0.1] port 22.
debug1: Connection established.
debug1: identity file /home/Administrator/.ssh/id_rsa type 1
debug1: identity file /home/Administrator/.ssh/id_rsa-cert type -1
debug1: identity file /home/Administrator/.ssh/id_dsa type -1
debug1: identity file /home/Administrator/.ssh/id_dsa-cert type -1
debug1: identity file /home/Administrator/.ssh/id_ecdsa type -1
debug1: identity file /home/Administrator/.ssh/id_ecdsa-cert type -1
debug1: identity file /home/Administrator/.ssh/id_ed25519 type -1
debug1: identity file /home/Administrator/.ssh/id_ed25519-cert type -1
debug1: Enabling compatibility mode for protocol 2.0
debug1: Local version string SSH-2.0-OpenSSH_6.5
ssh_exchange_identification: read: Connection reset by peer

Administrator@winseliu ~
$ ping localhost

正在 Ping winseliu [::1] 具有 32 字节的数据:
来自 ::1 的回复: 时间<1ms
来自 ::1 的回复: 时间<1ms

还不能在hosts文件中加!如,指定localhost为127.0.0.1后,得到结果为:

1
2
3
Administrator@winseliu ~/hadoop
$ ssh localhost
ssh_exchange_identification: read: Connection reset by peer

–END

GIT操作记录手册

Git的每次提交都有一个唯一的ID与之对应,所有的TAG/Branch/Master/HEAD等等,都是一个软链接/别名而已!这个是理解好Git的基础!

提交最佳实践

  • commit 只改一件事情。
  • 如果一个文档有多个变更,使用git add --patch只选择文档中的部分变更进入stage。具体怎么使用,键入命令后在输入?
  • 写清楚 commit message

配置

内建的图形化 git:

1
gitk

git服务器

搭建git服务器也很方便,有很多web-server的版本,我试用了下scm-manager使用挺简单的! 如果已经有了SVN的服务器,可以直接使用git-svn检出到本地!!

配置环境

1
2
git config --global user.email "XXX"
git config --global user.name "XXX"

换行(\r\n)提交检出均不转换

基本上都在windows操作系统上工作,不需要进行转换!

1
git config --global core.autocrlf false
  • true 提交时转换为LF,检出时转换为CRLF
  • input 提交时转换为LF,检出时不转换
  • false 提交检出均不转换

core.safecrlf

  • true 拒绝提交包含混合换行符的文件
  • false 允许提交包含混合换行符的文件
  • warn 提交包含混合换行符的文件时给出警告

默认分支

.git/config如下的内容:

1
2
3
[branch "master"]
    remote = origin
    merge = refs/heads/master

这等于告诉git两件事: 1. 当你处于master branch, 默认的remote就是origin。 2. 当你在master branch上使用git pull时,没有指定remote和branch,那么git就会采用默认的remote(也就是origin)来merge在master branch上所有的改变

如果不想或者不会编辑config文件的话,可以在bush上输入如下命令行:

1
2
$ git config branch.master.remote origin 
$ git config branch.master.merge refs/heads/master 

之后再重新git pull下。最后git push你的代码,到此步顺利完成时,则可以在Github上看到你新建的仓库以及你提交到仓库中文件了OK。

修改默认Git编辑器

1
2
3
4
5
6

$ git config core.editor vim

$ git config --global core.editor vi
git config --global core.editor "vim"
export GIT_EDITOR=vim

常用基本操作

操作 说明
git init
git init –bare 服务端使用bare(空架子,赤裸)的方式
git status 使用git打的最多的就是status命令,查看状态的同时会提示下一步的操作!
git diff 工作空间和index/stage进行对比
git diff –cached index/stage与本地仓库进行对比
增加到变更(index/stage)
git add . 将当前目录添加到git仓库中,常用命令!
git add -A 添加所有改动的文档
git add -u 只加修改过的文件,新增的文件不加入
git rm –cached
添加到本地库
git commit
git commit -m “msg”
git commit -a -a是把所有的修改的(tracked)文件都commit
git commit –amend -m “commit message.” 未push到远程分支的提交,快捷的回退再提交。修补提交(修补最近一次的提交而不创建新的提交),可结合git add使用!
git commit -v -v 可以看到文件哪些内容被修改
git commit -m ‘v1.2.0-final’ –allow-empty
git reset
git reset HEAD^
git reset –hard HEAD^
git checkout file
git checkout –orphan ; git rm –cached -r .
git rebase
git merge
日志
git log
git log –oneline –decorate –graph
git log –stat 查看提交信息及更新的文件
git log –stat -p -1 –format=raw
git log -3 文件的最近3次提交的历史版本记录
git log –stat -2 查看最近两次的提交描述及修改文件信息。–stat 每次提交的简略的统计信息
git log -p -2 展开显示每次提交的内容差异,类似git show功能。 -p 选项展开显示每次提交的内容差异,用 -2 则仅显示最近的两次更新
git log –name-status 仅显示文件的D/M/A的状态
git log –summary
git log –dirstat -5
git log –pretty=format:“%h %s” –graph git log –pretty=format:“%h - %an, %ar : %s”
git log –pretty=oneline
git reflog 查看本地操作历史。 ref log
git show 查看某版本文件的内容,版本库中最新提交的diff!
git show master:index.md 查看历史版本的文件内容
git show <哈希值:文件目录/文件> 查看内容
git cat-file
分支
git branch 查看本地分支
git branch 添加新分支,新分支创建后不会自动切换!!
git branch –set-upstream branch-name origin/branch-name * 建立本地分支和远程分支的关联
git branch -a
git branch –list –merged
git branch -r 查看远程分支
git checkout –orphan
git checkout 切换分支
git checkout -b [new_branch_name] 创建新分支并立即切换到新分支。git checkout -b branch-name origin/branch-name,本地和远程分支的名称最好一致
git branch -d branch_name -d选项只能删除已经参与了合并的分支,对于未有合并的分支是无法删除的。如果想强制删除一个分支,可以使用-D选项
git branch -d -r remote_name/branch_name
git merge origin/local-branch 本地分支与主分支合并
推/拉
git pull * 等价于git fetch && git merge
git fetch 先把git的东西fetch到你本地然后merge后再push
git push –rebase *
git push
git push –set-upstream origin To push the current branch and set the remote as upstream
git push origin branch-name 创建远程分支(本地分支push到远程),从本地推送分支。如果推送失败,先用git pull抓取远程的新提交
git push -u origin master 将代码从本地回传到仓库
git push origin test:master 提交本地test分支作为远程的master分支
git push -f * 强推(–force),即利用强覆盖方式用你本地的代码替代git仓库内的内容,这种方式不建议使用。
git pull [remoteName] [localBranchName] 获取远程版本库提交与本地提交进行合并
git push [remoteName] [localBranchName] 提交、推送远程仓库
git push –tags 提交时带上标签信息
git push master 把本地仓库提交到远程仓库的master分支中
git push origin :branch_name 删除远端分支,(如果:左边的分支为空,那么将删除:右边的远程的分支。)远程的test将被删除,但是本地还会保存的,不用担心。
git push origin :/refs/tags/tagname 删除远端标签
git clone http://path/to/git.git clone的内容会放在当前目录下的新目录
git clone –branch 获取指定分支,检出远程版本的分支。 git clone –branch unity /d/winsegit/hello helloclone
TAG
git tag 查看标签
git tag 添加标签
git tag -d 删除标签
git tag -r 查看远程标签
git show 查看标签的信息
git tag -a
REMOTE
git remote [show] 查看远程仓库
git remote -v 查看远程仓库
git remote add [name] [url] 添加远程仓库
git remote set-url –push[name][newUrl] 修改远程仓库
git remote show origin 远程库origin的详细信息。缺省值推送分支,有哪些远端分支还没有同步到本地,哪些已同步到本地的远端分支在远端服务器上已被删除,git pull 时将自动合并哪些分支!
git remote show 远程版本信息查看
git remote add origin 设置仓库
git remote rm [name] 删除远程仓库
文件列表
git ls-tree –name-only -rt
打包
git archive –format tar –output master 将 master以tar格式打包到指定文件 $ git archive -o load.tar master
1
2
3
4
5
6
7
8
9
10
11
$ git config core.filemode false

git config --get core.filemode

git config --global core.filemode false
git config --global core.autocrlf false

cat .git/config
.gitconfig

git ls-files

按功能点完整的操作步骤

查看指定版本文件内容

1
2
3
4
5
6
Administrator@WINSELIU /e/git/hello (master)
$ git ls-tree master
100644 blob 139b30f9054cf77bd2eeabcebaf6ca3f32cd1d50    abc

Administrator@WINSELIU /e/git/hello (master)
$ git cat-file -p 139b30f9054cf77bd2eeabcebaf6ca3f32cd1d50

回归指定的版本文件

1
2
3
# 通过上面的命令得到该文件的版本
winse@Lenovo-PC ~/esw/git
$ git checkout 4179d96 esw/DTA/ISMI_CU/docs/测试/省汇聚平台/dta/kettle/file/hive2mysql.ktr

查看提交版本的指定文件内容

1
2
3
4
git log abc  # 获取文件提交ID
git cat-file -p <commit-id>  # 获取treeID
git cat-file -p <tree-id>  # 获取当前tree的列表
git cat-file -p <file-blob-id>

根据格式输出日志

1
2
3
4
5
6
7
8
9
10
11
12
$ git log --pretty=oneline
$ git log --pretty=short
$ git log --pretty=format:'%h was %an, %ar, message: %s'
$ git log --pretty=format:'%h : %s' --graph
$ git log --pretty=format:'%h : %s' --topo-order --graph
$ git log --pretty=format:'%h : %s' --date-order --graph

# https://stackoverflow.com/questions/1441010/the-shortest-possible-output-from-git-log-containing-author-and-date
git log --pretty=format:"%h%x09%an%x09%ad%x09%s"
pretty = format:%C(auto,yellow)%h%C(auto,magenta)% G? %C(auto,blue)%>(12,trunc)%ad %C(auto,green)%<(7,trunc)%aN%C(auto,reset)%s%C(auto,red)% gD% D

git log --pretty=format:'%h %ad %s | %an' --date=short  

你也可用‘medium’,‘full’,‘fuller’,‘email’ 或‘raw’. 如果这些格式不完全符合你的相求, 你也可以用‘–pretty=format’参数(参见:git log)来创建你自己的”格式“.

本地提交后再次修改

修改注释

1
git commit --amend 

内容修改

1
2
3
 # edit file
git add file
git commit --amend

提交了不该提交的,并撤回

刚刚提交的不完整,想修改一些东西,加到刚才的提交中

commit -> modify -> add -> amend

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
git reset HEAD^
git status
cat abc
git diff
git commit -a -m "for test reset"
git log
git diff

vi abc
git add abc
git commit --amend

git status
git diff
git show master:abc
git log

没有push到远程库的提交,本地可以做的事情

  • git reset: 用于回溯,回到原来的提交节点,然后可以merge多次提交合并为一个
  • git rebase :在origin分支的基础上,合并当前分支上的提交,形成线性提交历史。 会把当前分支的提交保存为patch,然后切到origin分支应用patch,形成线性的提交,common-origin-current。

rebase冲突处理时,使用git add && git rebase –continue。如果你使用了git add && git commit,那么当前冲突使用git rebase –skip即可。

处理本地和服务器之间冲突的方式

  • 以本地为主。 git push -f
  • 归并merge。 git pull 或者 git fetch && git merge

从stash恢复出现冲突,可以先提交,然后在pop,最后处理冲突。一般提交到本地index中的数据才是自己想要的,从stash中获取的数据只是临时的,可以直接用HEAD的数据内容覆盖,省去处理冲突的时间。

1
2
3
4
5
$ git add -u
$ git commit -m 'update XXXX'
$ git stash pop

$ git status | grep 'both modified'  | grep ' ssh-config' | awk -F: '{print $2}' | while read line ; do git show HEAD:"$line" > "$line" ; done

从Github远程服务上拿其他分支:

1
2
3
4
5
6
7
8
Administrator@WINSELIU /e/git/to-markdown (master)
$ git branch -r
  origin/HEAD -> origin/master
  origin/gh-pages
  origin/jquery
  origin/master

$ git checkout -b jquery origin/jquery

把本地的git项目发布到Github

1
2
3
4
5
6
touch README.md
git init
git add README.md
git commit -m "first commit"
git remote add origin git@github.com:winse/flickr-uploader.git
git push -u origin master

Push an existing repository from the command line:

1
2
git remote add origin git@github.com:winse/flickr-uploader.git
git push -u origin master

如果已经存在remote origin,使用下面的方式修改远程的地址:

1
2
3
4
5
6
7
8
9
10
Administrator@WINSELIU /d/winsegit/flickr_uploader/chrome (master)
$ git remote set-url --add origin  git@github.com:winse/flickr-uploader.git

Administrator@WINSELIU /d/winsegit/flickr_uploader/chrome (master)
$ git remote show origin
Warning: Permanently added 'github.com,192.30.252.128' (RSA) to the list of known hosts.
* remote origin
  Fetch URL: git@github.com:winse/flickr-uploader.git
  Push  URL: git@github.com:winse/flickr-uploader.git
  HEAD branch: (unknown)

reset后撤回

可能存在已经更新的数据,先提交到临时缓存区

1
git stash

然后通过reflog得到需要撤回到的版本号

1
2
3
$ git reflog
ef9ccf7 HEAD@{0}: reset: moving to HEAD^^
4f317fe HEAD@{1}: commit (amend): 2015-03-04 d

仍然使用reset回退

1
2
3
4
5
git reset --hard 4f317fe
git stash pop
# 处理冲突
# git add 冲突文件
git reset

强制回退所有的操作:

1
2
git reset --hard
git clean -fd

强制检出新版本:

1
2
git fetch --all
git reset --hard origin/master

git查看本地领先远程的提交

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
Administrator@WINSELIU /d/winsegit/winse.github.com (master)
$ git status
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
#   (use "git push" to publish your local commits)
#
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   about.md
#       modified:   blog/_posts/2014-01-21-monitoring-mobile-networks.md
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       default.html
no changes added to commit (use "git add" and/or "git commit -a")

Administrator@WINSELIU /d/winsegit/winse.github.com (master)
$ git log --oneline --decorate -5
0425ec5 (HEAD, master) 增加日志Github修改历史功能
f3a4a58 把TAG定位到首页,并分页以及按照年分类
d5097e3 (origin/master, origin/HEAD) plugins disabled in github page!
e75d62b test
876dd42 修复根目录下的md不能通过npp-windows请求编辑的BUG

Administrator@WINSELIU /d/winsegit/winse.github.com (master)
$ git cherry
+ f3a4a58cfead3aa76e4b92de3342bee5970accb7
+ 0425ec548aa4e3dd29cd6fbfa1b656543e85058e

找回游离的提交

绑定到新分支

1
2
git reflog # 查看本地操作历史
git branch head23 HEAD@{23} # 把分支head23指向/绑定到游离的提交

git的版本都是从分支开始查找的,如果没有被分支管理的提交就游离在版本库中! 所以在reset重新修改时,最好建立分支然后再提交! 如果发现类似的提交问题,就需要尽快的修复,不然提交的ID找不到就S了!

那些老的提交会被丢弃。 如果运行垃圾收集命令(pruning garbage collection), 这些被丢弃的提交就会删除. (请查看 git gc)

重置HEAD

1
git reset --hard HEAD@{23}

删除提交

删除提交E:

1
2
3
4
5
6
7
8
9
$ git tag F
$ git tag E HEAD^
$ git tag D HEAD^^
$ git checkout D
$ git cherry-pick master # 把master-patch应用到TAG-D
# fix conflicts
$ git status # 提交
$ git checkout master # checkout到master分支
$ git reset --hard HEAD@{1} # 重置master到删除E后的提交

Git浏览特定版本的文件列表

1
git ls-tree --name-only  -rt <SHA-ID>

删除没有被git track的文件

1
2
3
4
5
6
7
8
9
10
11
12
13
# 删除 untracked files
git clean -f
# 连 untracked 的目录也一起删掉
git clean -fd # -f force branch switch/ignore unmerged entries, -d if you have new directory
# 连 gitignore 的untrack 文件/目录也一起删掉 (慎用,一般这个是用来删掉编译出来的 .o之类的文件用的)
git clean -x -fd

# 在用上述 git clean 前,建议加上 -n 参数来先看看会删掉哪些文件,防止重要文件被误删
git clean -nxfd

git reset --hard ( or git reset then back to 1. )
git checkout . ( or specify with file names )
git reset --hard ( or git reset then back to 3. )

检出SVN项目

1
2
Administrator@ZGC-20130605LYE /e/git
$ git svn clone http://chrome-hosts-manager.googlecode.com/svn/trunk/

http://www.worldhello.net/2010/02/01/339.html下面提到的有意思:

Git-svn 是 Subversion 的最佳伴侣,可以用 Git 来操作 Subversion 版本库。这带来一个非常有意思的副产品——部分检出: 可以用 git-svn 来对 Subversion 代码库的任何目录进行克隆,克隆出来的是一个git版本库 可以在部分克隆的版本库中用 Git 进行本地提交。 部分克隆版本库中的本地提交可以提交到上游 Subversion 版本库的相应目录中

如果需要密码的,使用方面的方式会报错git-svn died signal 11。可以先init,然后在fetch。

1
2
3
4
5
6
7
8
9
10
Kevin@Kevin-PC /cygdrive/d/dta-git
$ git svn init URL --username=NAME
Initialized empty Git repository in /cygdrive/d/dta-git/.git/

$ git svn fetch
Authentication realm: <https://IP:PORT> Subversion Repositories
Password for 'NAME':
# 输入密码后,ctrl+c退出后再重新下载

$ git svn fetch > fetch.log 2>&1

Github添加项目主页github page(gh-pages)

提交后就可以访问了页面了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Administrator@WINSELIU /d/winsegit/flickr_uploader/chrome (master)
$ git branch -a
* master
  remotes/origin/master

Administrator@WINSELIU /d/winsegit/flickr_uploader/chrome (master)
$ git push origin master:gh-pages
Warning: Permanently added 'github.com,192.30.252.128' (RSA) to the list of known hosts.
Total 0 (delta 0), reused 0 (delta 0)
To git@github.com:winse/flickr-uploader.git
 * [new branch]      master -> gh-pages

Administrator@WINSELIU /d/winsegit/flickr_uploader/chrome (master)
$ git branch -a
* master
  remotes/origin/gh-pages
  remotes/origin/master

Creating Project Pages manually

  cd repository

  git checkout --orphan gh-pages
  # Creates our branch, without any parents (it's an orphan!)
  # Switched to a new branch 'gh-pages'

  git rm -rf .
  # Remove all files from the old working tree
  # rm '.gitignore'

  echo "My GitHub Page" > index.html
  git add index.html
  git commit -a -m "First pages commit"
  git push origin gh-pages

子模块操作

git-submodule教程!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Administrator@WINSELIU /d/winsegit/jae_winse (master)
$ git submodule add git@github.com:winse/flickr-uploader.git src/main/webapp/flickr

Administrator@WINSELIU /d/winsegit/jae_winse (master)
$ git submodule status
 635090c5a754eebf5ce6566b7f8c65446b764f51 src/main/webapp/flickr (heads/master)

Administrator@WINSELIU /d/winsegit/jae_winse (master)
$ git commit -m "add submodule"
[master c7dc8c7] add submodule
warning: LF will be replaced by CRLF in .gitmodules.
The file will have its original line endings in your working directory.
 2 files changed, 4 insertions(+)
 create mode 100644 .gitmodules
 create mode 160000 src/main/webapp/flickr

如:$ git submodule add git://github.com/soberh/ui-libs.git src/main/webapp/ui-libs

初始化子模块:$ git submodule init —-只在首次检出仓库时运行一次就行

更新子模块:$ git submodule update —-每次更新或切换分支后都需要运行一下

删除子模块:(分4步走哦)

  1. $ git rm –cached [path]
  2. 编辑“.gitmodules”文件,将子模块的相关配置节点删除掉
  3. 编辑“.git/config”文件,将子模块的相关配置节点删除掉
  4. 手动删除子模块残留的目录

检出(Checkout)仓库的一部分内容

;; ADD 2018-5-27

类似SVN检出子目录的功能。在git里面叫做 Sparse checkout (稀疏的检出)。

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
: 先初始化一个git空仓库
$ mkdir hello-scala-mapreduce
$ cd hello-scala-mapreduce/
$ git init
Initialized empty Git repository in /mnt/f/temp/hello-scala-mapreduce/.git/

: 关联远程仓库
$ git remote add -f origin https://github.com/winse/helloworld.git
Updating origin
remote: Counting objects: 290, done.
remote: Total 290 (delta 0), reused 0 (delta 0), pack-reused 290
Receiving objects: 100% (290/290), 761.75 KiB | 326.00 KiB/s, done.
Resolving deltas: 100% (49/49), done.
From https://github.com/winse/helloworld
 * [new branch]      hello      -> origin/hello
 * [new branch]      javaweb    -> origin/javaweb
 * [new branch]      master     -> origin/master

: 开启Sparse Checkout模式
$ git config core.sparsecheckout true
: 指定需要检出的子目录/文件
$ echo HelloScalaMapReduce > .git/info/sparse-checkout

$ git pull origin hello
From https://github.com/winse/helloworld
 * branch            hello      -> FETCH_HEAD
$ ls
HelloScalaMapReduce

其他偶尔使用命令

1
2
3
4
5
6
git diff --check # 检查行尾有没有多余的空白
git remote prune <remotename>
git ls-remote --heads origin
git gc --prune=now
git ls-remote --heads <remote-name>
git rm -r --cached *

参考

–END

Jekyll按照tag分页面

单页实现

从jekll-bootstrap检出的代码中,tags.html实现了标签的显示。但是所有的标签和日志列表都码在一个文件里面,总感觉有点太拥挤。

<div class="page-header">
    <h1>{{ page.title }} {% if page.tagline %} <small>{{ page.tagline }}</small>{% endif %}</h1>
</div>

<ul class="tag_box inline">
    {% assign tags_list = site.tags %}  

    {% if tags_list.first[0] == null %}
        {% for tag in tags_list %} 
        <li><a href="#{{ tag }}-ref">{{ tag }} <span>{{ site.tags[tag].size }}</span></a></li>
        {% endfor %}
    {% else %}
        {% for tag in tags_list %} 
        <li><a href="#{{ tag[0] }}-ref">{{ tag[0] }} <span>{{ tag[1].size }}</span></a></li>
        {% endfor %}
    {% endif %}

    {% assign tags_list = nil %}
</ul>

{% for tag in site.tags %} 
<h2 id="{{ tag[0] }}-ref">{{ tag[0] }}</h2>
<ul class="index">
    {% assign pages_list = tag[1] %}  

    {% if site.JB.pages_list.provider == "custom" %}
        {% include custom/pages_list %}
    {% else %}
        {% for node in pages_list %}
            {% if node.title != null %}
                {% if group == null or group == node.group %}
                    {% if page.url == node.url %}
                    <li class="active">
                        <a href="{{ BASE_PATH }}{{ node.url }}" class="active">{{ node.title | xml_escape }}</a>
                        <span><time datetime="{{ node.date | date: "%Y-%m-%d" }}">{{ node.date | date: "%Y/%m/%d" }}</time></span>
                    </li>
                    {% else %}
                    <li>
                        <a href="{{ BASE_PATH }}{{ node.url }}" class="active">{{ node.title | xml_escape }}</a>
                        <span><time datetime="{{ node.date | date: "%Y-%m-%d" }}">{{ node.date | date: "%Y/%m/%d" }}</time></span>
                    </li>
                    {% endif %}
                {% endif %}
            {% endif %}
        {% endfor %}
    {% endif %}

    {% assign pages_list = nil %}
    {% assign group = nil %}   
</ul>
{% endfor %}

插件实现分页面

找了一些资料,使用plugin的方式可以生成文件,以及页面的自定义标签。在_plugins目录下新增jekyll-tag-page.rb :

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
module Jekyll

  class TagPage < Page
      def initialize(site, base, dir, tag)
          @site = site
          @base = base
          @dir = dir
          @name = 'index.html'

          self.process(@name)
          self.read_yaml(File.join(base, '_layouts'), 'tag_index.html')

          self.data['tags'] = tag
      end
  end

  class TagPageGenerator < Generator
      safe true

      def generate(site)
          if site.layouts.key? 'tag_index'
              dir = site.config['tag_dir'] || 'tags'
              site.tags.keys.each do |tag|
                  site.pages << TagPage.new(site, site.source, File.join(dir, tag), tag)
              end
          end
      end
  end
end

生成插件为每个TAG生成了一个页面,_layout模板设置为tag_index.html,在模板中可以根据当前页面的tags过滤并只显示该tag的日志列表。文件默认保存到tags/TAG目录下。

{% for tag in site.tags %} 
    {% if page.tags == tag[0] %}
    <h2>{{ tag[0] }}</h2>
    <ul class="index">
        {% assign pages_list = tag[1] %}  

        {% for node in pages_list %}
            {% if node.title != null %}
            <li>
                <a href="{{ BASE_PATH }}{{ node.url }}">{{ node.title | xml_escape }}</a>
                <span><time datetime="{{ node.date | date: "%Y-%m-%d" }}">{{ node.date | date: "%Y/%m/%d" }}</time></span>
            </li>
            {% endif %}
        {% endfor %}

        {% assign pages_list = nil %}
    </ul>
    {% endif %}
{% endfor %}

使用脚本生成目录和md文件来实现

但是由于github不支持自定义插件功能,也就是说,就算我提交了_plugin的代码也是无效的。最终最后的实现,使用Shell脚本在tags目录下生成文件夹和内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cd $tools/../tags

find * -type d -exec rm -rf {} +

for tag in `cat tags`; do 
mkdir $tag
cat > $tag/index.md <<EOF
---
layout: tag
categories: [$tag]
---

EOF
done;

脚本列表tags文件内容生成目录和index.md文件。

layout模板tag.html页面代码如下:

<h3>Tag: {{ page.categories[-1] }}</h3>
<ul class="archive-list">

{% for tag in site.tags %}
{% if page.categories[-1] == tag[0] %}

{% assign pages_list = tag[1] %} 
{% for node in pages_list %}
    {% if node.title != null %}
        <li class="archive">
            <span>
                <time datetime="{{ node.date | date: "%Y-%m-%d" }}">
                    {{ node.date | date: "%Y/%m/%d" }}
                </time>
            </span>
            <a href="{{ BASE_PATH }}{{ node.url }}" class="archive-link">{{ node.title | xml_escape }}</a>
        </li>
    {% endif %}
{% endfor %}
{% assign pages_list = nil %} 

{% endif %}
{% endfor %}

</ul>

–END

参考

–END

Jekyll页面添加编辑按钮

近期把其他博客的日志导出成Markdown后导入到自己的blog。想在本地预览后,看到不好的地方就进行修改,想法是美好的,现实是残酷的!找文件太麻烦了,定位到了文件夹,还需要找文件。反正就是觉得很不爽!最终通过自定义URL协议的方式完美的解决

尝试ActiveXObject

IE下可以通过ActiveXObject调用本地的程序打开文件。尽管局限于IE,但是如果能实现的话将就下也OK的。 理所当然认为IE下是没有问题,就找chrome下能否执行ActiveXObject的兼容对象

在chrome下,没啥很好的替代实现。同时在IE11下,ActiveXObject调用shell对象报automation服务器不能创建对象,需要额外开启功能、降低IE的安全性等等,也很麻烦!!

1
2
3
4
function callShellApplication(){
  var objShell = new ActiveXObject("WScript.shell");
  objShell.run('"C:\Program Files (x86)\wkhtmltopdf\wkhtmltopdf.exe" "c:\PDFTestPage.html" "c:\TEST.pdf"');
}

尝试Flash

有想过使用flash解决,毕竟剪贴板复制功能的ZeroClipboard.swf是那么的完美^_^。但是swf不能执行exe程序,没办法只能再寻求其他办法。

自定义URL协议

最后看到了URL Protocol的实现方式,是的哦,Github-Windows、以及QQ等这些网页的工具不都是调用系统的程序吗?!

然后试着按照网页的教程,试着弄弄!又是一个新鲜事物,有挑战的事情做起来就更有激情!

添加注册表项reg-npp-windows.bat:(可以先导出github-windows的注册表项参考。这里使用用bat来实现,注册脚本中可以通过相对路径指向执行的程序)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
set NPP_APP=%~dp0npp-windows.bat
set NPP_ARG=%%1

set "NPP_CMD=\"%NPP_APP%\" \"%NPP_ARG%\""

reg add "HKEY_CLASSES_ROOT\npp-windows" /f
reg add "HKEY_CLASSES_ROOT\npp-windows" /ve /t REG_SZ /d "URL:npp-windows Protocol" /f
reg add "HKEY_CLASSES_ROOT\npp-windows" /v "URL Protocol" /t REG_SZ /d "" /f

reg add "HKEY_CLASSES_ROOT\npp-windows\DefaultIcon"  /f
reg add "HKEY_CLASSES_ROOT\npp-windows\DefaultIcon"  /ve /t REG_SZ /d "" /f

reg add "HKEY_CLASSES_ROOT\npp-windows\shell" /f
reg add "HKEY_CLASSES_ROOT\npp-windows\shell\open" /f
reg add "HKEY_CLASSES_ROOT\npp-windows\shell\open\command" /f
reg add "HKEY_CLASSES_ROOT\npp-windows\shell\open\command" /ve /t REG_SZ /d "%NPP_CMD%" /f

pause

点击URL真正调用的是npp-windows.bat命令(即上面的注册脚本的NPP_APP):

1
2
3
4
5
6
7
@echo off

set fileRelativePath=%1
set filepath="%~dp0..\..\..\%fileRelativePath:~17,-1%"

start D:\local\usr\npp\notepad++.exe %filepath%
exit

start命令新起一个子进程打开程序,这样可以启动后面的程序,同时关闭/退出掉黑窗口! 请求参数有整个url加上双引号,如"npp-windows://e/about.md"。数字17,-1获取真正的路径。由于根下的文件会被自动加反斜杠"npp-windows://about.md/",所以加上e/进行修复。

在网页使用链接,然后加上npp-windows的协议就可以打开文件进行修改编辑了。

<a class="shellExecuteLink" href="npp-windows://e/{{ page.path }}" title="本地编辑"><i class="icon-edit"> </i></a>

对BAT的命令相当的迷惑:

  • "D:\local\usr\npp\notepad++.exe" "%~dp0..\..\..\%fileRelativePath:~17,-1%" OK
  • start D:\local\usr\npp\notepad++.exe "%~dp0..\..\..\%fileRelativePath:~17,-1%" OK
  • start "D:\local\usr\npp\notepad++.exe" "%~dp0..\..\..\%fileRelativePath:~17,-1%" 弹出Windows无法打开此文件的框

闪黑窗口优化尝试

功能已经实现了,但是仍然有一个黑窗口一闪而过!

爱折腾的程序员啊!!

找啊找,发现vbs可以后台执行命令。直接在命令行是可以执行npp-windows.vbs的,同时那种不弹窗的效果也是自己想要的!!但把vbs作为url-protocol协议的执行程序时,点击页面链接却没一点反应!但是运行bat和exe程序是没有问题的。

其实windows下的bat和vbs程序都是可以转成exe的。 下载了ScriptCryptor Compiler破解版,把vbs转成exe后就可以被url-protocol调用了,且没有闪窗的现象了!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Dim CurrPath
CurrPath=WScript.Createobject("Scripting.FileSystemObject").GetFile(Wscript.ScriptFullName).ParentFolder.Path

Dim fileRelativePath
fileRelativePath=WScript.Arguments(0)

Dim filepath
filepath=Mid(fileRelativePath,17)

Dim Wsh
Set Wsh = WScript.CreateObject("WScript.Shell")

Wsh.Run "D:\local\usr\npp\notepad++.exe " + CurrPath + "\..\..\..\" + filepath,0,True

Set Wsh=NoThing
WScript.quit

同时上面的vbs程序有一个与bat同样的问题:在文件没有关闭的前,vbs程序是一直等待的!如果使用这种方式,编辑很多文件就会存在很多进程。

而直接在Wsh.Run后面加上start前缀就报系统找不到指定的文件的错误!整来整去最后还是调用bat文件,解决主进程一直等待的问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
Dim CurrPath
CurrPath=WScript.Createobject("Scripting.FileSystemObject").GetFile(Wscript.ScriptFullName).ParentFolder.Path

Dim fileRelativePath
fileRelativePath=WScript.Arguments(0)

Dim Wsh
Set Wsh = WScript.CreateObject("WScript.Shell")

Wsh.Run CurrPath + "\npp-windows.bat """ + fileRelativePath + """",0,True

Set Wsh=NoThing
WScript.quit

然后修改添加注册表项的命令reg-npp-windows.bat,重新覆盖注册下就可以了。

1
set NPP_APP=%~dp0npp-windows.vbs.exe

尽管没有黑窗口了闪现,但是速度慢了很多!! 两种方式换着用呗,修改也简单: 改下程序的名字,重新注册下就行了。

其他

URL-protocol的用户选择设置会被chrome缓冲,可以通过C:\Users\Administrator\AppData\Local\Google\Chrome\User Data\Local State文件的protocol_handler节点来修改。

参考

–END

Tomcat源码阅读-批处理解读

常用Tomcat批处理文件

在windows系统下,双击bin/startup.bat即可启动tomcat。但核心的批处理文件是catalina.bat,最终调用的就是catalina.bat文件。

version.bat、shutdown.bat、startup.bat、configtest.bat都只是增加version/stop/start/configtest命令参数,然后调用catalina.bat批处理文件。

catalina.bat

批处理文件中最重要的非catalina.bat莫属了!包含非常多的操作!

1 关闭优化

Suppress(止住) Terminate batch job on CTRL+C

在第一个参数%1为run操作时,同时拥有%TEMP%目录写权限,执行call "%~f0" %* <"%TEMP%\%~nx0.Y",好像也就是调用自身,如果检测到"%TEMP%\%~nx0.run"存在就跳到脚本主体去执行。完成后退出。Tomcat7才加上的!

在按了CTRL+C后,去掉提示用户确认操作!!!对比下Tomcat6和Tomcat7一切都明白了!在命令行里面调用call的重定向输入%TEMP%\%~nx0.Y的就是Y!!就是提示行的输入啊!

Suppress anoying Terminate batch job prompt when hitting CTRL+C. Note however that it leaves the file named yes in the bin directory

git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@922223 13f79535-47bb-0310-9956-ffa450edef68

2 验证主目录

  • 获取CATALINA_HOME

    • CATALINA_HOME环境变量存在
    • 当前目录%cd%,如果%CATALINA_HOME%\bin\catalina.bat存在,跳到验证EXECUTABLE操作
    • 当前目录父目录。
  • 验证CATALINA_HOME

    如果%CATALINA_HOME%\bin\catalina.bat不存在,打印错误信息,退出

  • 验证EXECUTABLE

    其实就是验证%CATALINA_HOME%\bin\catalina.bat是否存在(个人觉得相当多余,到达这一部,已经保证了catalina.bat的存在)。

    不存在则报错。每个文件都要获取并设置CATALINA_HOME,其实shell的sh文件也一样,每个sh文件都需要获取一遍当前dirname。

3 设置环境变量/参数

  • 设置CATALINA_BASE为CATALINA_HOME(如果没定义)
  • 调用setenv.bat脚本设置环境变量(如果存在)。先检测调用CATALINA_BASE目录下的,没有才调用CATALINA_HOME下的。
  • 调用CATALINA_HOME下面的setclasspath.bat脚本(必须存在,否则会报错退出)
  • 获取CLASSPATH,并添加bootstrap.jar
  • 设置CATALINA_TMPDIR为%CATALINA_BASE%\temp(如果没定义)
  • 如果tomcat-juli.jar存在则添加到CLASSPATH
  • %LOGGING_CONFIG%加入JAVA_OPTS
    1. %LOGGING_CONFIG%
    2. -Dnop
    3. -Djava.util.logging.config.file="%CATALINA_BASE%\conf\logging.properties"
  • %LOGGING_MANAGER%加入JAVA_OPTS
    1. %LOGGING_MANAGER%
    2. -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
  • 输出环境变量信息

4 组装执行命令

接下来的变量直接牵涉到怎么去运行tomcat! 这些变量包括: MAINCLASS、ACTION、JDPA、_EXECJAVA、SECURITY_POLICY_FILE

  • MAINCLASS设置了Java调用的主类org.apache.catalina.startup.Bootstrap

    除了version直接调用org.apache.catalina.util.ServerInfo外,其他命令的入口都是这个类。

  • ACTION默认的操作为start

    另外命令对应是stop/configtest。

  • JDPA(Java Platform Debugger Architecture)

    使用jpda [start|run|stop|configtest]即可启用远程调试功能。默认为-agentlib:jdwp=transport=dt_socket,address=8000,server=y,suspend=n。也可以通过JPDA_TRANSPORT,JPDA_ADDRESS,JPDA_SUSPEND环境变量来指定部分参数,或者直接指定JPDA_OPTS完整的JPDA选项。

  • _EXECJAVA: 默认为%_RUNJAVA%,debug时设置为%_RUNJDB%,start时指定TITLE设置为start "%TITLE%" %_RUNJAVA%

  • SECURITY_POLICY_FILE: debug/run/start后跟-security时,设置为%CATALINA_BASE%\conf\catalina.policy [TODO]

最后就是_EXECJAVA、Jpda、Security进行组合来执行调用Java类,ACTION作为最后一个参数传给java的main方法。

其他一些参数可以通过JAVA_OPTS,CATALINA_OPTS环境变量指定。


其他注意点

  • 如果第一个参数为debug命令,一定要用JDK!

    在打印输出信息的时刻,会输出JAVA_HOME,而不是JRE_HOME

    看到的一些不足: 在打印信息的时刻,%1判断是否为debug。接下来检测取出jpda,再判读其他执行操作(debug/run/start/configtest/stop/version)。 但是呢,jdpa后不能跟debug的!执行了catalina.bat debug会调用jdb,而 -agentlib:jdwp不在jdb允许的选项之列!

  • 网上很多教程都是,安装tomcat都会去设置环境变量!

    JAVA_HOME和TOMCAT_HOME的环境变量(全局),其实可以在startup.bat中设置即可(本地)。这样,可以很好的运行多个不同版本的tomcat。

  • run和start的区别

    start命令会start %_RUNJAVA%另起一个进程,而run是直接调用%_RUNJAVA%。 在windows系统start命令会弹出一个新窗口,但是在linux下面start会在调用的时刻添加&后台运行,打印的信息都得去日志文件中查看。