JStorm常见问题汇总

2018年5月20日 0 条评论 184 次阅读 0 人点赞

task列表“task is dead”错误

有几个原因可能导致出现这个错误:

  1. task心跳超时导致nimbus主动kill这个task所在的worker,公司的JStorm集群基于Mesos+Marathon+Docker部署,大部分情况下worker在4分钟内未部署好(含docker网络配置问题等),nimbus检测到task心跳检测其超时后主动kill worker以便重新调度。正常部署模式下需要关注导致task心跳超时的原因;
  2. task对应的 bolt/spout 中的open/prepare/execute/nextTuple等方法没有对异常做try...catch导致抛出异常进而导致task挂掉。一个worker中任意一个task如果没有做异常处理,会导致整个worker挂掉,worker中其他task也报“Task is dead”错误信息,所以在jstorm应用代码中需要在所有方法中都加上try...catch。

生产过程中常见的两种情况如下:

  • 业务存在外部依赖,比如Redis、HBase、MySQL等,初始化客户端或者外部依赖访问异常导致task挂掉;
  • 业务数据本身出现脏数据,解析过程中出现异常而没有做异常捕获导致task挂掉;

具体排查可以按照如下步骤进行:

  1. 如果task是每隔4分钟左右有规律地挂掉那么基本可以确定是task心跳超时导致的,可以直接跳到步骤3;
  2. 查看worker日志在挂掉的时间点是否有异常,但是注意要看挂掉的那个worker日志而不是重新启动的worker日志,因为worker重新起来之后可能位于不同的机器上;
  3. 如果worker日志没有异常可以看一下集群nimbus日志,搜一下:"Update taskheartbeat",然后找到挂掉的worker所对应的topology Id检查最后更新心跳的时间,对比一下task心跳超时配置nimbus.task.timeout.secs,如果worker挂掉的时间 - 最后一次更新心跳的时间 > task心跳超时,那么基本上可以确定是因为task心跳超时被kill了, 发生这样的情况有几种可能:
    1. 执行队列被阻塞了一直没有返回;
    2.  worker发生了FGC导致正常的线程都被停住,从而导致心跳超时。这时要查看一下对应的GC日志,看那个时间点附近有没有FGC;
    3.  worker/task抛出了未处理的异常如OutOfMemoryError之类的;
    4. 最后也有可能是worker一直没起来, worker心跳超时;

Connection refused错误

这个日志一般只会在task/worker挂掉的时候才会出现,因为挂掉的worker对应的端口已经被释放所以会出现连接拒绝,具体排查步骤可以参见上上文提到的“task is dead”问题排查。

task提示“xxx queue is full”

JStorm bolt/spout 中有三种类型的队列: Deserialize Queue ---> Executor Queue ---> Serialize Queue,并且每一个队列都有满的可能。 如果是 serializeQueue is full那么可能是序列化对象太大导致序列化耗时太长,针对这种情况可以精简传输对象。 如果是deserialize queue is full或是execute queue is full( 2个原因一样)都是下游bolt处理速度跟不上上游spout或bolt发送速度引起的。 解决办法:

1.判断是否为常态问题以及是不是大面积发生,如果就1个或2个task出现上述问题且没有引起worker out of memory则可以忽略;

2.如果一个component大面积发生task 队列满或因为task 满进而导致worker out of memory,那么 就需要解决处理速度更不上的问题。  最简单的办法是增加并发, 如果增加并发不能解决问题, 则需要参考性能调优寻找优化点。

task状态一直处于starting状态

到topology页面点task的worker log,看有没有日志:

  • 如果有worker log请看看里面是否有异常。确认一下所有方法中如open/prepare/execute/nextTuple中有没有做异常捕获try...catch,如果程序中抛出了异常并且没有被处理JStorm默认就会认为这个worker有问题进而导致整个worker都挂掉了。
  • 如果没有worker log则可能有以下几个原因:
    • topology请求的memory过多导致分配不出需要的内存(包括:worker.memory.size配置,JVM参数中-Xmx -Xms等的配置);
    • supervisor机器的磁盘满了或者其他机器原因;
  • 其他一些常见的错误如jvm参数设置错误(比如-Xmn > -Xmx,使用了jdk不支持的JVM参数等),jar包冲突(如日志冲突)等;

topology所在集群资源不够

日志中提示 “No supervisor resource is enough for component” 则意味着资源不够,如果是测试环境可以将supervisor的cpu 和memory slot设置大,JStorm中一个task默认会消耗一个cpu slot和一个memory slot, 而一台机器上默认的cpu slot是(cpu 核数 -1), memory slot数(物理内存大小 * 75%/1g)。如果一个worker上运行的task比较多时,需要将memory slot size设小(默认是1G), 比如512M, 对应的配置参数为memory.slot.per.size: 535298048

Frame size larger than max length

这个问题是序列化后的topology对象过大导致,通常可能是spout/bolt中创建了一个大对象(比如bitmap, 大数组等)导致序列化后对象的大小超过了thrift的max frame size(thrift中16384000这个值已经写死且只能调小不能调大)。JStorm中如果需要在spout/bolt中创建大对象,建议是在open/prepare方法中来做以延迟对象的创建时间。参见:https://github.com/alibaba/jstorm/issues/230

JStorm框架序列化问题

JStorm所有spout、bolt、configuration和发送的消息(Tuple)都必须实现Serializable接口, 否则就会出现序列化错误。如果是spout或bolt的成员变量没有实现Serializable时但又必须使用时, 可以在对该变量申明时增加transient 修饰符, 然后在open或prepare时进行实例化:

JStorm框架日志冲突问题

JStorm 0.9.x系列使用log4j作为日志系统,2.x系列则使用logback作为日志系统。 但是不管使用哪个版本的JStorm都需要注意不能使用冲突的日志依赖比,如log4j-over-slf4j和slf4j-log4j12是冲突的,两者肯定不能共存否则会出现类似这个错误:

JStorm 0.9.x依赖了log4j,slf4j-log4j12,如果使用了0.9.x版本则应用代码中必须要排除掉log4j-over-slf4j依赖。 同样JStorm 2.x依赖了logback,log4j-over-slf4j,如果使用了该版本则应用代码中需要排除掉slf4j-log4j12的依赖。

JStorm框架类冲突问题

如果应用程序使用和JStorm相同的jar但版本不一样时,建议打开classloader修改配置文件topology.enable.classloader: true或者代码中设置ConfigExtension.setEnableTopologyClassLoader(conf, true),JStorm默认是关掉classloader的因此JStorm会强制使用JStorm依赖的jar。

web ui没有显示对应task

如果提交任务等待几分钟后,web ui始终没有显示对应的task则可能由以下3种情况导致:

用户程序初始化太慢

如果有用户程序的日志输出则表明是用户的初始化太慢或者出错只需查看日志即可,另外对于MetaQ 1.x的应用程序spout会recover ~/.meta_recover/目录下文件,用户可以直接删除这些消费失败的问题以加速启动。

用户jar冲突或初始化异常

打开supervisor 日志找出启动worker命令并单独执行,然后检查是否有问题。类似下图:

Storm和JStorm本地目录相同

检查配置项storm.local.dir是不是storm和jstorm使用相同的本地目录,如果相同则将二者分开即可。

JStorm框架提示端口被绑定

通常有两种情况:

多个worker抢占一个端口

假设是6800 端口被占可以执行命令ps -ef|grep 6800检查是否有多个进程, 如果有多个进程则执行kill -9 pid手动杀死他们即可。

系统打开connection太多

Linux对外连接端口数限制,TCP client对外发起连接数达到28000左右时就开始大量抛异常,需要执行如下命令:

双网络(多网络)环境配置问题

出于管理、安全、性能等因素生产系统中经常会配置双网络(或多网络),常见的是把管理网和数据网分开部署。举例来讲一个集群中如果有10个节点,每个节点上配置了一个千兆网、一个万兆网 分别是192.168.1.xxx 和192.168.2.xxx ,如何让jstorm 按照我们希望的方式传输tuple呢? 配置文件中没有显式、单独的设置。用户可以采用如下方法配置:

1.在个机器上配置好/etc/hosts或DNS,例如/etc/hosts:

2.JStorm supervisor启动时会采集本机的hostname并注册到nimbus中,后续通信也都用hostname 去进行节点间通信, 因此hostname对应的网络是配置的关键。例如: nimbus和zookeeper在workernode01上,用户希望tuple走万兆网,zookeeper/nimbus走千兆网那么可以在storm.yaml中设置:

将每个supervisor所在节点的hostname设置为与/etc/hosts (或DNS)中对应万兆网段的主机名。 例如: 节点1上:

节点10上:

修改完配置文件后重新启动supervisor,jstorm list时就可以看到supervisor都是workernode1-10g……workernode10-10g UI中也可以一目了然。观察日志也可以发现节点间通信都是绑定的xxx-10g:xxx端口,从而实现了tuple走万兆网。

参考资料

https://github.com/alibaba/jstorm

https://groups.google.com/g/jstorm-user

勇敢,和生活的艰难无关。

文章评论(0)