浅谈storm实时数据的开发

2 篇文章 0 订阅
1 篇文章 0 订阅

部署方式

1. 根据业务分集群进行部署,三台主节点nimbus(master-slave). 防止出现单点故障(一台出现故障,topology拓扑也不会挂掉,只是不能提交新的任务到集群)。

2. worker节点32core+120G。每个6到10台。每台机器60Slots (flink也可以基于slot,yarn来管理,前者的好处是部署比较简单,只需要管理机器上的线程数即可,不需要额外的资源来管理容器,但是扩展内存,cpu数比较困难,集群slot的的使用率普遍会很低,除非进行代码的优化。)

3. 通过zk来管理节点状态, 主节点和worker节点通信,消息变更,以及ack等。

并发度

一个DAG拓扑,包含spout,bolt。 spout负责发送消息,将数据流以元组的方式发送,bolt负责转换数据流,包括计算,过滤,随机,根据字段指定发送到bolt.

worker是具体的进程, 拓扑跨一个或多个worker进程来执行,worker数是jvm进程的个数。
executor是进程中的线程,
task是线程中具体的逻辑对象(spout,bolt).

一个拓扑中worker的数据是不会变化的,但是exector可以变化。

每个spout, bolt的并发度,可以根据executor的数据来决定,一般因为多线程,共享变量等问题,我们executor和task的数据保持1:1的关系。

一般我们的策略是,一个项目有多个拓扑,一个拓扑对应一个topic(不涉及到流的join(会有数据延迟,回刷数据等问题))。 spout的并发度会<=消息报的分区数。

bolt的并发度会根据算子的复杂度(IO/计算密集型来进行划分)。计算密集型的可以少分配一点并发度,IO密集型的一般涉及到读取第三方数据(连表,读取kv等)可以适当加大并发度。

对于流量型的bolt,还会根据流量属性hash(userid, hash/10,把流量分成10片),进一步切分。

资源分配优化

在这里插入图片描述

storm资源的分配涉及到的还是并发度,和jvm的占用。并发度上面已经上过,jvm的占用是一个需要多次测试的活。

一般我们可以通过设置拓扑的worker jvm参数来实现。一般来说4G的堆内存足够用了,但是设置到需要进行去重,本地窗口聚合,本地缓存等场景,还是需要进行扩容,这个需要根据storm-ui来进行管理。
	1. 去重的场景,以订单来举例,如果消息报抖动,上游消息重发,下游如果不做幂等性考虑,统计数据就会变多,一般的做法就是本地存储一个map,或者通过布隆过滤器来实现。后者实现的代价相对小点。
	2. 本地窗口数据聚合,一般会根据分片做,这样可以大大节约内存,但是需要注意的是,如果聚合数据保存在一个公共的类中,会有并发的问题出现,因为bolt都是单线程在跑,如果多个bolt都在一个woker中,公共变量就会累计。这个时候就会有并发的问题出现。
	3. 本地缓存,以新老用户的判断为例,我们会取查询第三方存储kv,但是kv的qps也是一定的,需要本地缓存来提高计算效率。一般而言,本地缓存都是以LRUMap来做,老数据自动优化掉,这个时候需要计算每一个数据量的大小(long  1000000 * 8 /1024 /1024 8M), 如果是Integer类型的数据,数据量很大,可以直接使用bitmap来做存储,也是优化的点, 这个需要对第三方查询做限流,重试,更新LRU要进行加锁(如果不加锁,需要对useid进行分片) 。
	
	

框架的实现

1. 日志解析器,通用的json解析,扩展的解析器
		一般根据事件的时间做为窗口时间,需要额外进行解析,date, time两个字段,date作为存储和唯一键,time作为窗口函数时候使用。
2. 算子组件,需要实现各种算子(sum, count, count(distinct), topN)
3. 中间数据的存储,窗口功能
		storm没有窗口的功能,需要自己实现,实现的方式,比如5mins一个窗口进行数据,根据5mins的窗口,划分出一天24小时的数组,每个数组的值保存5mins的数据值, 创建一个commonSpout,启动一个定时器,1s发送一个5mins过期标识到下游bolt,,通过上游spout发送的标识,对数据中发生变化的数据sink掉。
		commonSpout两个功能,(发送数据过期命令,发送数据sink的命令)
		上面的做法的缺陷是数据的过期功能没有(类似于flink的水位点),很难实现flink事件执行时间的水位点控制。 我们的做法是针对数据延迟到达不做控制,只是定期去清理上一天的数据(在凌晨0点的时候数据延迟到达数据数据会不准确),在commonSpout中起一个线程,第二天6点,把上一天全部的数据从内存中清理掉。
		基于上个问题,如果数据出现消息乱序等问题,比如凌晨前的数据,凌晨后才收到,目前storm只能做到延后统计,不能过滤掉,需要的话,只能依赖flink基于事件事件的水位点来控制。

3. 分片
		分片主要用在分流,存储分片来用。
		1. 在日志解析器完成之后,可以通过字段进行分片,一方面可以防止数据倾斜,另外一方面可以分流。
		2. 存储分片,主要是限于一个机器的jvm内存大小。如果数据量过于大,就需要借助于第三方存储(新老用户,userid+date).
		
4. 结果的存储
	结果数据的存储流向主要是kafka。 mysql, hbase,kv等。关键是要做好幂等型(mysql insertupdate唯一键)。尤其是窗口数据,尽量根据唯一键做分区顺序性保证。避免上一条数据比下一条数据延后执行。

	我们这边的做法是统一写到一个kafka来做解耦,做到分区顺序性保证,下游根据分区起对应的处理任务,通过页面配置化的方式写入到对应的存储中(起到限流,存储挂了之后,不影响上游任务的写)。
	
5. 数据回溯组件

		
		
		

实时任务需要解决的几个问题

1. 日志乱序问题
		比如上5mins前的数据5mins后才到,就需要把所有数据都hold到内存中。现有的storm天然支持这种,flink的话,就需要设置过期时间长一点。
	
2. 幂等型
		影响幂等型的一般是数据重发导致的。数据重发一般和设置的at-least-once,或者数据回溯有很大关系。
		数据重发,一般做法是把所有数据hold到内存中。实时判断,上文已经讲过。
		数据回溯,一般都要停服,如果数据在内存中有存储,一般都是从0点开始进行数据回溯,除非有错误的数据,数据库可以根据唯一key进行保证。(有错误的数据就要清理数据库)。 flink可以根据情况从checkpot位点进行重刷(如果数据长期存储到内存中,建议不要从位点进行回刷,会产生重复数据)
		
3. 准确性。
		一般我们只实现at-least-once, exactly-once storm很难实现,即使是flink,需要根据flink自带的kafka,以及能够实现两阶段提交的数据库,内存中的中间数据持久化。来保证。
		一般来说我们只需要at-least-once和保证幂等型就好了。如果非要做到exactly-once,而且storm并不支持exactly-once.
		需要业务需要支持exactly-once,可以解除flink实现,不过需要使用flink自带的消息中间件,kafka来做,每一条消费的记录,只有最后一步sink完成之后才能commit。否者只能保存到内存中,内存中的数据通过flink的checkpoint机制,即使应用挂掉,依然可以进行数据恢复。sink到数据库必须能保证数据的一致性,一般需要使用两阶段提交来保证。
		
		storm的准确性保证主要是通过ack机制来实现,每一个storm拓扑任务,可以创建N个Ack线程.每次处理完成之后和Spout生成的messageId,进行异或操作。最后一个bolt处理完成之后,所有的异或操作结果为0,否则根据策略重新发送给spout进行失败重试。(storm的重试机制,只能首个公操作完成,比较麻烦)

4. 限流和反压
		一般来说限流只会针对查询第三方服务,和写入存储这块。可以通过rateLimter来做
		
		反压一般的场景是下游消费不过来,一般这种情况就要考虑扩容,和增加并发读,storm在反压上面做的并不好,通过下游bolt接收队列,超过高水位时候将反压写入zk,然后通知上有。spout来降低tuple发送的速度。一般通过设置接收队列,等待队列的长度缓解反压。
		flink的话,基于netty以及流,天然的反压。
		

监控体系

1:集群cpu, 内存,jvm, gc,fgc次数。
2:端到端的数据消费rt,数据延迟队列长度,数据报错情况。
3:消息延迟
4:数据质量保证,实时数据和离线数据的对比,突发情况报警。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值