HBase表数据倾斜治理_HBaseConfiguration配置参数

HBase表数据倾斜治理_HBaseConfiguration配置参数

1.HBaseConfiguration配置文件加载过程

Configuration是hadoop作业的配置信息类,可以实现再多个mapper和多个reducer任务之间共享信息。当我们要通过java代码创建hbase client,首先要创建配置对象:

1
Configuration conf = HBaseConfiguration.create();

该步骤包括创建hdfs client和加载hbase 配置文件:

1
2
3
4
5
public static Configuration create() {
Configuration conf = new Configuration();//Hadoop包中的Configuration类
conf.setClassLoader(HBaseConfiguration.class.getClassLoader());
return addHbaseResources(conf);
}

Configuration类的静态代码块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static {
deprecationContext = new AtomicReference(new Configuration.DeprecationContext((Configuration.DeprecationContext)null, defaultDeprecations));
ClassLoader cL = Thread.currentThread().getContextClassLoader();
if (cL == null) {
cL = Configuration.class.getClassLoader();
}

if (cL.getResource("hadoop-site.xml") != null) {
LOG.warn("DEPRECATED: hadoop-site.xml found in the classpath. Usage of hadoop-site.xml is deprecated. Instead use core-site.xml, mapred-site.xml and hdfs-site.xml to override properties of core-default.xml, mapred-default.xml and hdfs-default.xml respectively");
}

addDefaultResource("core-default.xml");
addDefaultResource("core-site.xml");
}

HBaseConfiguration的addHbaseResources()方法:

1
2
3
4
5
6
7
public static Configuration addHbaseResources(Configuration conf) {
conf.addResource("hbase-default.xml");
conf.addResource("hbase-site.xml");
checkDefaultsVersion(conf);
HeapMemorySizeUtil.checkForClusterFreeMemoryLimit(conf);
return conf;
}

从源码中不难发现,在创建HBaseConfiguration对象过程中总共需要加载四个配置文件:core-default.xml、core-site.xml、hbase-default.xml、hbase-site.xml,多达数百项的默认配置信息以键值对的形式配置在这些文件当中。

配置文件中的参数配置形式如下所示,其中<final>true</final>表示在后续合并资源过程中或者谁用set方法也无法改变该配置参数。

1
2
3
4
5
6
7
8
9
10
11
<configuration>
<property>
<name>hbase.zookeeper.property.clientPort</name>
<value>2081</value>
</property>
<property>
<name>fs.defaultFS</name>
<value>file:///</value>
<final>true</final>
</property>
</configuration>

阅读源码发现,执行addResource()方法后只是将配置文件的名字加载到了Configuration对象中,并没有将配置文件当中的配置信息键值对加载进来。这里采用了延时加载的设计模式,只有调用set()或get()方法时,才会通过getProps()方法将配置文件中的属性值加载到Configuration对象的properties属性中。

getProps()方法调用loadResources()寻找配置文件存储地址并读取文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
protected synchronized Properties getProps() {
if (this.properties == null) {
this.properties = new Properties();
Map<String, String[]> backup = new ConcurrentHashMap(this.updatingResource);
this.loadResources(this.properties, this.resources, this.quietmode);
if (this.overlay != null) {
this.properties.putAll(this.overlay);
Iterator var2 = this.overlay.entrySet().iterator();

while(var2.hasNext()) {
Entry<Object, Object> item = (Entry)var2.next();
String key = (String)item.getKey();
String[] source = (String[])backup.get(key);
if (source != null) {
this.updatingResource.put(key, source);
}
}
}
}

return this.properties;
}

2.java -jar与hadoop jar的区别

2.1 Configuration加载文件内容源码

loadResources()源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
if (resource instanceof URL) {
doc = this.parse(builder, (URL)resource);
} else if (resource instanceof String) {
URL url = this.getResource((String)resource);
doc = this.parse(builder, url);
} else if (resource instanceof Path) {
File file = (new File(((Path)resource).toUri().getPath())).getAbsoluteFile();
if (file.exists()) {
if (!quiet) {
LOG.debug("parsing File " + file);
}

doc = this.parse(builder, new BufferedInputStream(new FileInputStream(file)), ((Path)resource).toString());
}
} else if (resource instanceof InputStream) {
doc = this.parse(builder, (InputStream)resource, (String)null);
returnCachedProperties = true;
} else if (resource instanceof Properties) {
this.overlay(properties, (Properties)resource);
} else if (resource instanceof Element) {
root = (Element)resource;
}

一般默认使用文件名的形式加载配置文件,所以要先根据文件名获取文件路径:

1
2
3
public URL getResource(String name) {
return this.classLoader.getResource(name);
}

注意此次调用的是类加载器当中的getResource()方法:

1
2
3
4
5
6
7
8
9
10
11
12
public URL getResource(String name) {
URL url;
if (parent != null) {
url = parent.getResource(name);
} else {
url = getBootstrapResource(name);
}
if (url == null) {
url = findResource(name);
}
return url;
}

2.2类对象和类加载器当中getResource()的区别

注意类对象和类加载器当中都有getResource(),两者作用不同。当两者的入参都是/开头的绝对路径时,两者都是从classpath下去寻找对应的文件路径;当类对象的getResource()入参为相对路径时,该方法只会在该类对象所在文件夹下寻找文件路径,而类加载器的getResource()方法还是从classpath下去寻找对应的文件路径。

现用如下实例说明:

1
2
3
4
URL classpath1 = ConfDebug.class.getResource("");
System.out.println(classpath1);
URL classpath2 = ConfDebug.class.getClassLoader().getResource("");
System.out.println(classpath2);

控制台输出:

1
2
file:/E:/files/JDIdeaProject/JDHBaseSnapshotDemo/target/classes/com/jd/hbase/conf/test/
file:/E:/files/JDIdeaProject/JDHBaseSnapshotDemo/target/classes/
  • getResource(“”)方法用于获取当前根目录。
  • 类对象getResource(“”)获取到的就是ConfDebug.class所在文件夹。
  • 类加载器getResource(“”)获取到的就是该项目的classpath。

2.3 getResource()方法可以获取jar包文件

注意getResource()方法除了可以获取classpath下的文件,实际上还可以获取到项目依赖的所有jar包中的文件。

代码实例:

1
2
3
4
5
6
URL classpath2 = ConfDebug.class.getClassLoader().getResource("");
System.out.println(classpath2);
URL is = ConfDebug.class.getClassLoader().getResource("core-site.xml");
System.out.println(is);
URL is1 = ConfDebug.class.getClassLoader().getResource("core-default.xml");
System.out.println(is1);

控制台输出:

1
2
3
file:/E:/files/JDIdeaProject/JDHBaseSnapshotDemo/target/classes/
file:/E:/files/JDIdeaProject/JDHBaseSnapshotDemo/target/classes/core-site.xml
jar:file:/E:/Mymeven/repository/org/apache/hadoop/hadoop-common/2.7.1/hadoop-common-2.7.1.jar!/core-default.xml

2.4 java项目的classpath

对于本地运行的java项目的classpath默认就是target/classes文件夹;对于jar包或war包项目classpath为jar包或war包自己。

代码实例:

1
2
3
4
5
6
URL classpath2 = ConfDebug.class.getClassLoader().getResource("");
System.out.println(classpath2);
URL is = ConfDebug.class.getClassLoader().getResource("core-site.xml");
System.out.println(is);
URL is1 = ConfDebug.class.getClassLoader().getResource("core-default.xml");
System.out.println(is1);

在本地ide上运行结果:

1
2
3
file:/E:/files/JDIdeaProject/JDHBaseSnapshotDemo/target/classes/
file:/E:/files/JDIdeaProject/JDHBaseSnapshotDemo/target/classes/core-site.xml
jar:file:/E:/Mymeven/repository/org/apache/hadoop/hadoop-common/2.7.1/hadoop-common-2.7.1.jar!/core-default.xml

打包成jar包使用java -jar命令运行结果:

1
2
3
null
jar:file:/E:/files/JDIdeaProject/JDHBaseSnapshotDemo/target/JDHBaseSnapshotDemo-1.0-SNAPSHOT.jar!/core-site.xml
jar:file:/E:/files/JDIdeaProject/JDHBaseSnapshotDemo/target/JDHBaseSnapshotDemo-1.0-SNAPSHOT.jar!/core-default.xml

2.5添加calsspath的两种方式

2.5.1设置计算机环境变量(不推荐)

在计算机系统变量中添加java classpath。比如添加两个C:\work\project1\bin;C:\shared,那么在执行getResource()方法方法时就会依次从这两目录和jar包中去寻找文件。

2.5.2给java命令传入-classpath或-cp参数(推荐)

比如java -classpath C:\work\project1\bin abc.xyz.Hello,那么在编译abc.xyz.Hello类时临时设置classpath。

如果要使用java -classpath在执行jar包时添加classpath,则不能使用java -jar,只能用java -classpath添加jar包和目标路径为classpath。

代码实例:

1
2
URL classpath2 = ConfDebug.class.getClassLoader().getResource("");
System.out.println(classpath2);

命令行运行:

1
2
3
4
5
6
7
//重要:正常使用java -classpath执行jar包中的类,就必须指定jar包名为classpath同时指定执行类。
>java -classpath JDHBaseSnapshotDemo-1.0-SNAPSHOT.jar com.jd.hbase.conf.test.ConfDebug
null

//重要:如果使用java -classpath还想添加classpath只需要使用分号添加即可。
>java -classpath E:\files;JDHBaseSnapshotDemo-1.0-SNAPSHOT.jar com.jd.hbase.conf.test.ConfDebug
file:/E:/files/

2.6 hadoop jar

hadoop jar与java -jar相比最主要的区别就是增加了运行时的classpath,主要是%hadoop_home/share目录下的jar包和%hadoop_home/etc/hadoop文件夹。

使用hadoop classpath命令可以查看hadoop环境下的classpath:

1
2
E:\HBase\hadoop\sbin>hadoop classpath
E:\HBase\hadoop\etc\hadoop;E:\HBase\hadoop\share\hadoop\common\lib\*;E:\HBase\hadoop\share\hadoop\common\*;E:\HBase\hadoop\share\hadoop\hdfs;E:\HBase\hadoop\share\hadoop\hdfs\lib\*;E:\HBase\hadoop\share\hadoop\hdfs\*;E:\HBase\hadoop\share\hadoop\yarn\lib\*;E:\HBase\hadoop\share\hadoop\yarn\*;E:\HBase\hadoop\share\hadoop\mapreduce\lib\*;E:\HBase\hadoop\share\hadoop\mapreduce\*

代码实例:

1
2
3
4
5
6
URL classpath2 = ConfDebug.class.getClassLoader().getResource("");
System.out.println(classpath2);
URL is = ConfDebug.class.getClassLoader().getResource("core-site.xml");
System.out.println(is);
URL is1 = ConfDebug.class.getClassLoader().getResource("core-default.xml");
System.out.println(is1);

命令行运行:

1
2
3
4
5
6
7
8
9
10
11
//重要:java -jar的classpath就是jar包自己。
>java -jar JDHBaseSnapshotDemo-1.0-SNAPSHOT.jar
null
jar:file:/E:/files/JDIdeaProject/JDHBaseSnapshotDemo/target/JDHBaseSnapshotDemo-1.0-SNAPSHOT.jar!/core-site.xml
jar:file:/E:/files/JDIdeaProject/JDHBaseSnapshotDemo/target/JDHBaseSnapshotDemo-1.0-SNAPSHOT.jar!/core-default.xml

//重要:hadoop jar添加了上述hadoop提供的classpath,导致同样的文件名取到的都是不同文职的同名文件。
>hadoop jar JDHBaseSnapshotDemo-1.0-SNAPSHOT.jar
file:/E:/HBase/hadoop/etc/hadoop/
file:/E:/HBase/hadoop/etc/hadoop/core-site.xml
jar:file:/E:/HBase/hadoop/share/hadoop/common/hadoop-common-2.6.5.jar!/core-default.xml

3.Configuration主要方法

3.1 addResource(String name)

使用该方法可以将指定的xml配置文件中的参数加载到Configuration。程序会到resource文件夹寻找该文件并加载配置参数。

1
conf.addResource("hdfs-site.xml");

3.2 set(String name, String value)

Configuration对象当中使用一个Hashtable<Object, Object>类型的properties属性保存配置参数,使用该方法可以设置配置参数键值对,也可以覆盖properties属性当中key相同的配置参数

4.在Mapper和Reducer当中获取Configuration对象及参数

Mapper和Reducer的抽象方法中有一个输入参数Context,使用该对象可以直接获取到对应job任务的Configuration对象。

举一个例子,在该例子中实现了,在Mapper中获取java命令行参数:

1
2
3
4
//将java命令行的第一个参数存入Configuration对象中
public static void main(String[] args) {
Configuration conf = HBaseConfiguration.create();
conf.set("first.field.length", args[0]);
1
2
3
4
5
6
7
8
9
//在Mapper中获取Configuration对象中的参数
public class GetConfMapper extends TableMapper<ImmutableBytesWritable, Put> {
private String firstFieldLength;

public void map(ImmutableBytesWritable row, Result value, Context context) {
Configuration conf = context.getConfiguration();
firstFieldLength = conf.get("first.field.length");
}
}