这个问题记录下来有一段时间, 一直没有发出来, 今天补上.
为了使存储在 HBase 中的数据支持 SQL 查询, 我们采用 Phoenix
入库. 入库数据的 client 代码用到的 jar 版本是: phoenix-4.5.0-HBase-0.98-client.jar
.
我们的入库程序运行一个 streaming 的平台上, 每个 worker 会分配一个 container 类似的东西, 也就是会限制 worker 使用的相关资源, 如内存, CPU 等.
程序运行一段时间后, 报错:
java.lang.OutOfMemoryError: unable to create new native thread
at java.lang.Thread.start0(Native Method)
at java.lang.Thread.start(Thread.java:714)
at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:949)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1360)
at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:132)
at org.apache.hadoop.hbase.client.HTable.coprocessorService(HTable.java:1625)
at org.apache.hadoop.hbase.client.HTable.coprocessorService(HTable.java:1598)
at org.apache.phoenix.cache.ServerCacheClient.removeServerCache(ServerCacheClient.java:308)
at org.apache.phoenix.cache.ServerCacheClient.access$000(ServerCacheClient.java:82)
at org.apache.phoenix.cache.ServerCacheClient$ServerCache.close(ServerCacheClient.java:142)
at org.apache.phoenix.execute.MutationState.commit(MutationState.java:486)
at org.apache.phoenix.jdbc.PhoenixConnection$3.call(PhoenixConnection.java:465)
at org.apache.phoenix.jdbc.PhoenixConnection$3.call(PhoenixConnection.java:462)
at org.apache.phoenix.call.CallRunner.run(CallRunner.java:53)
at org.apache.phoenix.jdbc.PhoenixConnection.commit(PhoenixConnection.java:462)
at scloud.data.db.PhoenixClient.batch(PhoenixClient.java:48)
at scloud.data.parser.Parser.insert(Parser.java:78)
at scloud.data.parser.AccessParser.parse(AccessParser.java:85)
at scloud.data.parser.Parser.run(Parser.java:47)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
unable to create new native thread
并不是 jvm 的 memory 不足导致,
这里有一篇较好的分析文章:
解决 - java.lang.OutOfMemoryError: unable to create new native thread .
排查后发现, 入库程序本身没有线程失控的状况, 只创建很少的线程.
但是 Phoenix 的 jdbc 库中和 HBase 连接和创建大量的线程, 可以用 ps -eLf
看到数量很多.
通过查看 phoenix 和 hbase 的源码,
发现可以通过参数 hbase.htable.threads.max
限制线程数, 默认值是:
int maxThreads = conf.getInt("hbase.htable.threads.max", Integer.MAX_VALUE);
Phoenix 的文档中对这个参数也有提及: Secondary Indexing
但是我在 hbase-site.xml
中设置该参数并不生效,
(该文件中其他参数已经生效, 因此确认该文件被正确加载).
在 Phoenix jdbc 建立连接使用的 Properites 中设置也不生效.
在 Phoenix 邮件组中询问 Phoenix Client create too many threads, 没有得到解答.
因此, 个人在 Phoenix-4.5.0-HBase-0.98
代码中更改
phoenix-core/src/main/java/org/apache/phoenix/query/HConnectionFactory.java
:
static class HConnectionFactoryImpl implements HConnectionFactory {
@Override
public HConnection createConnection(Configuration conf) throws IOException {
- //return HConnectionManager.createConnection(conf);
+ ThreadPoolExecutor pool = new ThreadPoolExecutor(
+ 1,
+ conf.getInt("hbase.htable.threads.max", 100),
+ conf.getLong("hbase.htable.threads.keepalivetime", 60),
+ TimeUnit.SECONDS,
+ new SynchronousQueue<Runnable>(),
+ Threads.newDaemonThreadFactory("htable"));
+ ((ThreadPoolExecutor) pool).allowCoreThreadTimeOut(true);
+
+ return HConnectionManager.createConnection(conf, pool);
}
}
pool 的创建方式和 hbase 代码中默认的 pool 创建是一致的.
通过上面的代码, 可以在 Phoenix jdbc 建立连接使用的 Properites
设置 hbase.htable.threads.max
生效.
这样就可以控制线程的数量, 让程序在 streaming 的平台上正常运行而不被 kill.