http://www.searchtb.com/2014/03/two-hbase-bugfix.html
1
2
3
4
5
6
7
8
java.util.concurrent.ExecutionException:
java.lang.RuntimeException: java.lang.OutOfMemoryError: unable to
create new native
thread
at
java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222)
at
java.util.concurrent.FutureTask.get(FutureTask.java:83)
at
org.apache.hadoop.hbase.client.HConnectionManager$HConnectionImplementation.processBatchCallback(HConnectionManager.java:1604)
at
org.apache.hadoop.hbase.client.HConnectionManager$HConnectionImplementation.processBatch(HConnectionManager.java:1456)
at
org.apache.hadoop.hbase.client.HTable.flushCommits(HTable.java:1011)
at
org.apache.hadoop.hbase.client.HTable.put(HTable.java:834)
at
com.taobao.dump.sapclient.client.HbaseClient.set(HbaseClient.java:224)
看错误信息,不能建立新的线程,和之前集群升级时遇到的一个问题类似,分析是某个程序建了大量的线程,达到了系统的上限。看机器上跑的进程的,最大可能是这个程序自己。从出错信息分析,结合hbase源码,在每个htable建立的时候,会初始化一个线程池。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int maxThreads =
conf.getInt("hbase.htable.threads.max",
Integer.MAX_VALUE);
if (maxThreads ==
0) {
maxThreads
= 1; // is there a better
default?
}
long keepAliveTime =
conf.getLong("hbase.htable.threads.keepalivetime",
60);
// Using the "direct handoff" approach, new threads will
only be created
// if it is necessary and will grow unbounded. This
could be bad but in HCM
// we only create as many Runnables as there are region
servers. It means
// it also scales when new region servers are
added.
this.pool = new
ThreadPoolExecutor(1,
maxThreads,
keepAliveTime,
TimeUnit.SECONDS,
new
SynchronousQueue(),
Threads.newDaemonThreadFactory("hbase-table"));
((ThreadPoolExecutor)this.pool).allowCoreThreadTimeOut(true);
这个pool的作用是client在提交一个操作时,会建立一个callable线程提交到pool里,每个线程专门处理各自
regionserver上的请求,最终聚合在一起,返回给client。所以当一个批量操作的请求过来的时候,极端情况下会在每个
regionserver都会有一个线程,而每个htable都独自维护这个pool。所以当进程里htable实例多,并且集群规模较大的时候,该进程
的线程数极端情况下是很多的。
具体到这个程序,确实有大量的batch操作,并且开了29个处理线程,并且hbase集群加了一批机器后,达到了近400台的规模,那极端情况下,将有1万多个线程,还不包括其他处理线程。而当时又有多个进程被同时调度到这台机器上,这时就就会出现上述异常了。
解决思路应用层面主要是降低极端情况下的并发,有以下几个方面:
1,container数和单个container中的线程数,在并发处理能力保持不变的情况下,选择合理的container数和线程数。
2,使用HTablePool来统一管理HTable实例,最好是单例,并且设定一个合理的maxSize,如并发时htable实例不够,调用方就先等待,直到有可用的实例。
3,建立htable对象时,指定自己的线程池,这个线程池全局共用,并且维护一个有界队列,当队列满时,请求就等待。
1
2
public
HTable(Configuration conf,final
byte[] tableName,
final ExecutorService
pool)
这个case最后的解决方式是,调低了HTablePool的maxSize。
另一个解决思路,加大系统可创建的最大线程数,关于一个JVM进程最多可以创建多少个线程,请参考 http://jzhihui.iteye.com/blog/1271122 ,但在我们的环境实际测试,只有调整参数/proc/sys/vm/max_map_count才能加大可创建的线程数。如不做调整,则可创建30000+个线程。具体系统层面的因果还待进一步分析。