Xsank's Blog

java多线程案例分析

开题

设定有如下需求:
现要从hbase中导出2016年整年的,大约10w只股票行情数据,数据总量约100t
导出成如下格式:

2016-01-01/facebook.txt linkedin.txt amazon.txt google.txt...
...
2016-12-31/facebook.txt linkedin.txt amazon.txt google.txt...

汇总到hdfs中供需求方使用

分析

已知数据量规模大概是100t,那么单台机器处理肯定不是不行的,先不说大多数磁盘都没这么大,即便磁盘有这么大,单台机器处理对于内存和cpu要求也很高,所以我们将问题一般化,使用数量有限的低配机器。
那么接下来要处理的就是如何对任务进行分割,最容易想到的有两种:

  1. 按照时间分割
    2016年共365天,那么最多拆分成365组,粒度足够细。再按照小时拆分又涉及到数据合并,先不考虑。
  2. 按照股票分割
    一共10w只,粒度可以拆分的更细。

以上两种都可以,都涉及到最后汇总hdfs的情况,不过按照股票分割粒度更细,更便于控制,这里我们选用后者。

选定分割方式后又会遇到一个问题,如果将任务分割给多台机器,这里先说两种:

  1. 按照机器数平均分配
    事先将股票裁剪分配好到一台中心机器上,每台机器设定只读取其中一部分或者直接将裁剪好的股票信息分配到对应的机器
    这种做否有两个缺点:
    1) 这会导致机器状态相关,不便于横向扩展
    2) 任务平均分配可能存在不均,资源利用率不够
  2. 由机器实际处理能力决定
    事先将股票信息保存在一台中心机器上,每台机器从这里统一消费

很明显方案二比方案一要好,这里我们选用方案二

现在机器已经分配好了,剩下的就只有单机处理了,剩下的就只有并发的知识了
应到每台机器上的逻辑就是:对于获取到的每只股票,扫描整年的数据,然后写本地,写好之后copy到hdfs即可,再细化下去后大概会遇到如下几个问题:

  1. 需要添加生产者-消费者模型
    获取股票后要扫取一天的数据,生产者及是hbase数据读取方,消费者便是数据处理方
  2. 本地磁盘大小有限,数据要及时清理
    假设我们的机器磁盘都只有100g的空间,那么必须考虑本次可存的文件最大上线,一旦告警必须等待磁盘数据拷贝完成再继续处理
  3. 保证数据完整性
    这里的任何一条数据都是不能丢弃的,你可以block任务,但是不能reject
  4. 扫取范围
    对于这种类TSDB的存储,当然最好只扫描一次,但是扫出来的数据都必须根据时间判断,会浪费性能,多线程写还需要考虑文件锁,进一步降低性能,另外如果程序判断按照天截止,又容易造成数据遗漏,不按照天截止缓存整年的数据之后再拷贝到hdfs又会增加时间开销
    所以这里最好按照天的粒度再分区间scan,每个线程仅控制一天,避免多线程写,同时便于单文件写好之后立即通知上传线程将文件汇总过去

知识点

第三方系统的掌握

如已经明确相关的hbasehdfs,其他包含可能会用到的组件如:消息队列,缓存等

多线程控制

会涉及到BlockingQueue,ThreadPool,CountdownLatch,Lockconcurrent知识运用,举个具体例子:

  1. 等待ThreadPool的所有任务完成
    1) invokeAll的使用
    2) shutdown结合awaitTermination的使用

  2. ThreadPool提交任务阻塞
    1) 定制BlockingQueue

  3. Thread同步控制
    1) CountdownLatch的使用

  4. 控制生产者和消费者终止
    1) PoisonPill的使用

  5. 异步任务处理
    1) 小心阻塞操作,避免挂起

  6. Lock使用
    1) 根据并发冲突的实际情况,控制锁粒度

总结

这就是一种比较常见的,用到“大数据”处理和并发知识的场景,如果网友有更好的思路,欢迎留言讨论~