今天在使用flume agent的时候,遇到了一个异常,  现把解决的过程记录如下:

问题的背景:

我使用flume agent 来接收从storm topology发送下来的accesslog , 做本地文件落盘。flume配置文件如下:

#用于syslog和accesslog的本地文件滚动。
 
a1.sources=r1
a1.sinks = sink1
a1.channels = c1
 
#thrift source;
a1.sources.r1.type= thrift
a1.sources.r1.channels = c1
a1.sources.r1.bind = 127.0.0.1
a1.sources.r1.port = 8081
 
 
 
#先用内存通道测试。
#a1.channels.c1.type = memory
#a1.channels.c1.capacity = 10000000
#a1.channels.c1.byteCapacity = 524288000
 
a1.channels.c1.type = file
a1.channels.c1.checkpointDir=/Users/dongqingswt/workdir/logs/checkpoint
a1.channels.c1.dataDirs = /Users/dongqingswt/workdir/logs/datadir
 
 
 
#使用自定义的文件滚动sink
a1.sinks.sink1.type=  com.beibei.datahamal.flume.extend.sink.roll.FileRollSink
a1.sinks.sink1.channel = c1
 
 
 
 
启动flume agent以后, thrift source在本机建立套接字监听,然后用flume-ng-sdk提供的RpcClientFactory 创建thrift rpc client , 进行消息发送的压测。
发现rpcclient 发送消息到一定量(100000) 时, FileRollSink就无法从memory channel拉取数据了, 而thrift rpc client 也得到了如下异常:
Exception in thread "main" org.apache.flume.EventDeliveryException: Failed to send event.
at org.apache.flume.api.ThriftRpcClient.append(ThriftRpcClient.java:155)
at com.beibei.datahamal.flume.extend.sink.rolling.FilerRollSinkSender.main(FilerRollSinkSender.java:42)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: java.util.concurrent.TimeoutException
at java.util.concurrent.FutureTask.get(FutureTask.java:201)
at org.apache.flume.api.ThriftRpcClient.append(ThriftRpcClient.java:134)
... 6 more

查看源码, 发现thrift client有一个20s的请求超时,这时候 , 查看thrift 错误日志,发现了如下异常:

java.lang.OutOfMemoryError: unable to create new native thread
at java.lang.Thread.start0(Native Method) ~[na:1.7.0_45]
at java.lang.Thread.start(Thread.java:713) ~[na:1.7.0_45]
at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:949) ~[na:1.7.0_45]
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1360) ~[na:1.7.0_45]
at org.apache.thrift.server.TThreadedSelectorServer.requestInvoke(TThreadedSelectorServer.java:306) ~[libthrift-0.9.3.jar:0.9.3]
at org.apache.thrift.server.AbstractNonblockingServer$AbstractSelectThread.handleRead(AbstractNonblockingServer.java:210) ~[libthrift-0.9.3.jar:0.9.3]
at org.apache.thrift.server.TThreadedSelectorServer$SelectorThread.select(TThreadedSelectorServer.java:586) ~[libthrift-0.9.3.jar:0.9.3]
at org.apache.thrift.server.TThreadedSelectorServer$SelectorThread.run(TThreadedSelectorServer.java:541) ~[libthrift-0.9.3.jar:0.9.3]

直观的看这个异常,会让人想到是堆内存不够用, 调节-Xmx1024m 参数以后,问题依旧。

接着用jconsole attach到flume agent这个进程,查看堆内存的使用量, 远没有达到预设的xmx阈值。

但是·有一个表现就是线程数猛涨到2000以后,直接掉到了20左右,为什么会创建这么多线程呢, 都是什么线程呢?

结合thrift source 的源码, 发现thrift server 采用的也是java nio   , 有SelectorThread做socket的read/write 就绪select

AcceptorThread做socket的accept的select ,而socket读就绪以后,收到的FrameBuffer会被包装成一个Runnable丢到线程池处理(查看

TThreadedSelectorServer的304 行),代码如下:
protected boolean requestInvoke(FrameBuffer frameBuffer) {
Runnable invocation = getRunnable(frameBuffer);
if (invoker != null) {
try {
invoker.execute(invocation);
return true;
} catch (RejectedExecutionException rx) {
LOGGER.warn("ExecutorService rejected execution!", rx);
return false;
}
} else {
// Invoke on the caller's thread
invocation.run();
return true;
}
} 这个任务invocation的处理逻辑是在ThriftSource的ThriftSourceHandler定义的,也就是把thrift flume event直接丢到memchannel以后返回。起初 ,我怀疑是不是flume event丢到memchannel处理太慢(比如有线程死锁),导致线程堆积, 但后面换成file channel
问题依旧,于是继续看jconsole上thrift 的Flume 线程,因为线程工厂在创建线程的时候,指定了线程名:
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat(
"Flume Thrift IPC Thread %d").build();
if (maxThreads == 0) {
sourceService = Executors.newCachedThreadPool(threadFactory);
} else {
sourceService = Executors.newFixedThreadPool(maxThreads, threadFactory);
} 所以, 查看这些线程发现这些线程:
堆栈跟踪:
sun.misc.Unsafe.park(Native Method)
java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043)
java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1068)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
java.lang.Thread.run(Thread.java:744)
 
说明线程池中的这些线程都在等任务工作, 既然大量的线程都在等任务,为什么还要创建这么多线程呢?是不是因为超过了单进程最大可建立的线程数呢,
结合ThriftSource源码,发现不指定最大线程数时, thrift server的线程池的确是不停的新建线程,而maxThreads又是一个Integer.MAX_VALUE,
if (maxThreads == 0) {
sourceService = Executors.newCachedThreadPool(threadFactory);
} else {
sourceService = Executors.newFixedThreadPool(maxThreads, threadFactory);
} 试着在flume 配置文件中指定最大线程数:
a1.channels.r1.threads=10 问题解决。
 这个问题的解决也让我们反思线程池的使用,不要设定太大的最大线程数。
 
 

一个flume agent异常的解决过程记录的更多相关文章

  1. IIS发布网站 报错500.19 错误解决过程记录

    首先先报上我的环境 WindowsServer 2012 IIS 8.5 网站是FrameWork 4.0 发布网站后浏览,报错信息如下: 解决过程记录如下: 1.看到这个问题首先想到的是权限问题,设 ...

  2. [转]线上GC故障解决过程记录

    排查了三四个小时,终于解决了这个GC问题,记录解决过程于此,希望对大家有所帮助.本文假定读者已具备基本的GC常识和JVM调优知识,关于JVM调优工具使用可以查看我在同一分类下的另一篇文章: http: ...

  3. 一次线上GC故障解决过程记录

    排查了三四个小时,终于解决了这个GC问题,记录解决过程于此,希望对大家有所帮助.本文假定读者已具备基本的GC常识和JVM调优知识,关于JVM调优工具使用可以查看我在同一分类下的另一篇文章: http: ...

  4. [转]一个CMake编译问题的解决过程

    问题的提出 公司的一个power-pc平台的产品,有个协议进行了修改,过程中出现了比较奇怪的情况.直接将修改后的动态库下载到设备上(原始设备是有文件系统和其他的依赖文件的,相当于部分更新应用),设备和 ...

  5. linux服务器报No space left on device错误的解决过程记录

    起因 今天在本地提交了点代码,但到服务器上git pull的时候提示No space left on device,第一反应是猜想可能硬盘满了(很有可能是log导致的),不过想想又觉得不太可能,这台服 ...

  6. Intellij IDEA debug模式下项目启动慢/无法启动的事件解决过程记录

    项目无法启动了 简单的介绍一下事件过程:周一的早上,收到前端同事抛过来的一个任务,说是一个接口无法正常返回数据,于是就让他把参数发过来,我想试着在本地重现一下并且将问题修复掉,这种情况肯定是要通过de ...

  7. ffmpeg Operation not permitted 报错的解决过程记录

    问题重现 由于视频的录制过程出现了一些小问题,需要重新将视频文件切割和合并,找了几个视频编辑软件来做这个事情,最终的结果都不是特别满意,当时已经挺晚的了,本来打算上床睡觉第二天再去想辙,从椅子上起身的 ...

  8. 记redis一次Could not get a resource from the pool 异常的解决过程

    最近有个项目中的redis每天都会报 "Could not get a resource from the pool"的错误,而这套代码在另一地方部署又没有问题.一直找不到错误原因 ...

  9. Content type 'application/json;charset=UTF-8' not supported异常的解决过程

    首先说一下当时的场景,其实就是一个很简单的添加操作,后台传递的值是json格式的,如下图 ,后台对应的实体类, @Data @EqualsAndHashCode(callSuper = false) ...

随机推荐

  1. CSS文本溢出处理方式

    1. 单行文本溢出省略号效果 .ellipsis { overflow:hidden; white-space:nowrap; text-overflow:ellipsis; } <div cl ...

  2. python中while循环和for循环的定义和详细的使用方法

    1. 循环的定义,反复做某事,具有明确的开始和结束.   2. 在Python中循环有while和for两种方式: While循环:1) 语法结构 >>> while 条件: ... ...

  3. 深入理解C/C++二维数组

    深入理解C/C++二维数组 前言 本来以为自己对二维数组的理解还可以,没感觉有什么,但是今天小伙伴问了一个问题感觉迷惑了好久,于是决定细致的记录一下,一步一步的探究各种关于二维数组的问题,巩固基础. ...

  4. SQL查询语句大全及其理解

    转自:https://www.cnblogs.com/1234abcd/p/5530314.html 一.基础1.说明:创建数据库CREATE DATABASE database-name2.说明:删 ...

  5. RabbitMQ入门:路由(Routing)

    在上一篇博客<RabbitMQ入门:发布/订阅(Publish/Subscribe)>中,我们认识了fanout类型的exchange,它是一种通过广播方式发送消息的路由器,所有和exch ...

  6. Java线程wait和sleep的区别

    Java中调用wait方法或者sleep方法都可以让线程进入waitint或者time-waiting状态,但是它们还是 有所不同的: wait是Object中的方法,而sleep则是Thread中的 ...

  7. php从入门到放弃系列-02.php基础语法

    php从入门到放弃系列-02.php基础语法 一.学习语法,从hello world开始 PHP(全称:PHP:Hypertext Preprocessor,即"PHP:超文本预处理器&qu ...

  8. Docker持久化存储与数据共享

    一.Docker持久化数据的方案 基于本地文件系统的Volume:可以在执行docker create或docker run时,通过-v参数将主机的目录作为容器的数据卷.这部分功能便是基于本地文件系统 ...

  9. Redis勒索事件爆发,如何避免从删库到跑路?

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由腾讯云数据库 TencentDB发表于云+社区专栏 9月10日下午,又一起规模化利用Redis未授权访问漏洞攻击数据库的事件发生,此次 ...

  10. 关于nodejs中遇到mysql默认8小时连接断开机制的终极简单解决方案

    由于mysql默认8小时连接无访问,就会断开.为此查了一下资料,有同种比较简单的解决方案: 1. 增加 MySQL 的 wait_timeout 属性的值. 修改 /etc/mysql/my.cnf文 ...