Java内容的复习-OI项目面试

OI

OI是一个告警系统。用flume收集OC client上传的数据,然后传到spark,spark再对数据进行分析,生成一些metrics键值对,然后发到graphite做显示,seyren定期去graphite查询metrics键值对,当某个metrics超过伐值的时候报警给分析人员,分析人员再对相应的日志进行分析,找出异常的原因。

拓扑框架:

Performance调优环境:

一台机器部署了log server,收集OC client上传的日志。同时部署了一个flume agent将log日志传给spark的executor

两台机器部署spark的worker,运行的时候每个worker会启动一个executor,用于启动avro flume receiver和运行driver提交的task。Flume receiver用于接收flume agent的传递过来的数据。

一台机器部署master,上面还会运行driver,graphite和seyren。Driver用于spark task,提交到executor运行。

扫描二维码关注公众号,回复: 1162017 查看本文章

软件架构:

OI系统的主要分为三层

1.Spark层:调用spark的相应函数,如果map,flatmap,reduce等,spark会按照这些函数生成相应的task,提交给spark executor

2.业务层:用于处理log日志,将log转换成相应的metrics

3.数据库层:将top10的异常日志存储到数据库,方便分析人员查看

4.Graphite交互层:用于将metrics发送给graphite

遇到的问题:

1.在运行大概5,6个小时左右,driver或者work会抛出Stackoverflow的错误

分析的步骤:

(1)查看栈信息,发现一直在做scala list的序列化,开始怀疑是不是scala list序列化会导致栈溢出的问题,网上查看相关资料发现确实有某个版本的scala list会导致堆溢出问题,但spark不是用那个版本,排除这个可能性

(2)查看日志上下文,发现是在序列化或反序列化task的时候抛出这个错误的。查看源代码,定位到相应的代码,然后修改源代码,让在出错的时候,把需要反序列化的byte数组写到文件。

(3)写测试代码,解析byte数组,查看到底反序列化了那些对象。发现RDD的依赖过长,Duration 3的RDD会依赖Duration 2的RDD,Duration 2的RDD会依赖Duration 1的RDD

(4)查看源代码,发现UpdateStateByKey方法,会生成一个StateDStream(保持状态的DStream),这个DStream在计算RDD的时候会获取前一个Duration的RDD,然后和parent RDD做一个cogroupe,这样当前计算的RDD就会依赖前一个Duration。如果这个依赖一直存在的话就会发生StackOverFlow。 

解决方案:

减少UpdateStateByKey方法的调用次数,程序中只调用一次(只调用一次的时候会将RDD的关联切断,如果调用两次,只有最后一个UpdateStateByKey生成的RDD的依赖关系会被切断)

2.Flume死锁的问题

在Flume运行一段时间后,发现flume agent不再tail log日志了。

分析的步骤:

(1)查看日志,发现某个线程在运行一段时间后没有相应的日志打出,猜测线程可能死了,或者被死锁

(2)用Jstack工具打出thread dump,发现有两个线程死锁了

(3)分析源代码发现了死锁

 线程1 

While(queue.size() == 0){

Syncronized(takeLock){

takeLock.wait()

}

}

线程2

While(queue.size() == capacity){

Syncronized(putLock){

putLock.wait()

}

}

对项目提出的优化:

1.写了专门用来监控performance相关参数的软件OI-Performance,在graphite上能够很好的展示这些属性,便于观察,监控的属性有SystemCpuLoad,ProcessCpuLoad,HeapMemoryUsage,GC的时间,disk util,network,以及一些业务属性

2.修改了spark中的storage level,减少了内存占用量

3.调整了JVM中new-ratio的比例,延长变量在新生代中存活的时间,减少full GC的时间

4.减少了collector这部环节,让agent直接发送数据到flume,让压缩过的数据直接传输到spark,减少了磁盘的读写以及内存的开销

5.减少了Avro格式转换成Seven自定义对象的环节,OI直接从generic record获取数据

6.替换flume的channel类型,让flume使用Memory Channel,减少磁盘的读写

为甚么选择flumehttp://www.kankanews.com/ICkengine/archives/89351.shtml 

1.原来有一个系统叫lsps,做log日志收集,存储到hdfs中,然后做数据挖掘的,为了和这套系统兼容,保证数据收集部分功能和代码的一致性,重用了那套机制。那套机制就是用flume做的

2.Flume有高可靠性,扩展性,能够保证日志不丢失

3.Flume的配置和部署都很简单,修改配置后,可以自动加载

4.Flume提供了丰富的sink和source,对不同格式的日志可以很方便的切换,只支持thrift client向scribe发送数据。

为什么选择spark

Spark streaming和storm的区别是(两者都是分布式流处理框架)http://www.jdon.com/46591 :

1.Spark支持保持状态

2.Spark有数据平滑窗口(sliding window),而后者需要自己去维护这个窗口

3.Spark是需要到一个duration才开始处理数据,storm是实时处理数据,有数据就处理。Storm只有秒内的延迟

4. 在Storm中,每个单独的记录当它通过系统时必须被跟踪,所以Storm能够至少保证每个记录将被处理一次,但是在从错误中恢复过来时候允许出现重复记录。这意味着可变状态可能不正确地被更新两次。另一方面,Spark Streaming只需要在批级别进行跟踪处理,因此可以有效地保证每个mini-batch将完全被处理一次,即便一个节点发生故障。简而言之,如果你需要秒内的延迟,Storm是一个不错的选择,而且没有数据丢失。如果你需要有状态的计算,而且要完全保证每个事件只被处理一次,Spark Streaming则更好。Spark Streaming编程逻辑也可能更容易,因为它类似于批处理程序(Hadoop),特别是在你使用批次(尽管是很小的)时

5.  Spark Streaming一个好的特性是其运行在Spark上. 这样你能够你编写批处理的同样代码,这就不需要编写单独的代码来处理实时流数据和历史数据

整套环境的failover机制

1.logserver那台机器的故障

(1)宕机 oc client会连接到其他log server,上传的数据可能会有部分丢失

(2)Flume 的source或者sink线程挂掉的话,不会重新启动

(3)当网络出现问题的时候,sink数据发送会失败,但会不断重试

2.Worker宕机

Receiver不会在其他地方启动

会重新在另外一个executor上启动task

3.master宕机

利用zookeeper来保证另一个master可以接着继续运行

 

猜你喜欢

转载自frankfan915.iteye.com/blog/2103245