自己写了一个flume小文件合并脚本,解决hdfs小文件过多问题

flume数据采集之后,本打算按照五分钟滚动一个文件的,但是由于实时性要求提高,改为一分钟一个文件。但是开启了三个flume实例,导致hdfs小文件过多。

1.影响NameNode,

2.影响task数量


决定写一个flume的合并脚本,写着写着,功能越来越多,越来越完善。

合并方案我写过两个:

1.使用spark进行读取合并,优点是scala代码书写,高级语言编程,易于理解维护,但是不方便,每次要打包。

2.使用shell脚本进行getmerger,然后split成128MB大小的文件。

前面一种方式很简单,这里就不列出如何实现了。


#后面代码

shell合并脚本思路:

1. getmerge into tmpfile 

2. split into block file(128MB)

3.put to hdfs

4. check file

5. rm hdfs small file

6. rm local tmpfile 



这段shell,基本上通过变量传值,有一定的shell编程经验才能看懂,通用性蛮强的,具体的有空贴出来,不过使用的时候还是要先做好测试。

#! /bin/sh
# vim /data/executeTask/file_compact/compact_flumedata.sh
# ref: sendmessage.sh
#       1. PROGRAMID    : add id and program desc into programelist.log
#       2. errortimes   : error times
#       3. errorcode    : error code
#       4. DesNo1       : destination phone number

#################### check result function #################### 
checksuccess(){
	project=$1
	projectId=10
	errorCode=$2
	processname=$3
	yesterday=$4
	logfile=$5
	table_name=$6
	phoneNumber=15202125865
	errorTime=1 
	echo " project=$project errorCode=$errorCode processname=$processname yesterday=$yesterday logfile=$logfile table_name=$table_name"

	if [ $errorCode -ne 0 ]; then
		echo "`date +%F\ %T` ${project} errorCode=${errorCode} error process $table_name ${processname} of ${yesterday} data " >> ${logfile}
		/data/executeTask/file_compact/sendmessage.sh $projectId $yesterday $errorCode $phoneNumber
		exit ${errorCode}
	else
		echo "`date +%F\ %T` ${project} errorCode=${errorCode} success process $table_name ${processname} of ${yesterday} data " >> ${logfile}
	fi
}
#################### check result function end #################### 

#################### processCompact function start #################### 
processCompact(){
	table_name=$1
	date=$2
	compact_file=$3
	logfile=$4
	project=$5

	# 0. rm tmp copy data eg. compact.1510051113._COPYING_ created while copy but job failed 
	# TODO rm all compact file 
	hdfs dfs -rm /data/$table_name/$date/compact*_COPYING_ 
	# asume size is measured by GB maybe is mb
	begainsize=`hdfs dfs -du -s -h /data/$table_name/$date/ | awk '{ print $1}' ` 
	
	#errorCode=$?
	#processname=rmCopytmp
	#checksuccess $project $errorCode $processname $date $logfile $table_name
	
	# 1. get to local
	hdfs dfs -getmerge /data/$table_name/$date/*FlumeData* /data/executeTask/file_compact/$compact_file
	errorCode=$?
	processname=getmerge
	checksuccess $project $errorCode $processname $date $logfile $table_name
	
	# split into 128m
	sizeunit=`hdfs dfs -du -s -h /data/$table_name/$date/ | awk '{ print $2}' ` 
	# test
	#begainsize=`hdfs dfs -du -s -h /data/Gps_Log/20171106/ | awk '{ print $1}' `
	#sizeunit=`hdfs dfs -du -s -h /data/Gps_Log/20171106/ | awk '{ print $2}' `
	#if [ $sizeunit = "G" ];then
	#		res=$(printf "%.f" `echo "scale=5;$begainsize*8 "|bc`)
	#else
	#		res=$(printf "%.f" `echo "scale=5;$begainsize/128 "|bc`)  # celling 取整数 http://blog.youkuaiyun.com/naiveloafer/article/details/8783518
	#fi
	#echo $res

	if [ $sizeunit = "G" ];then 
		res=$(printf "%.f" `echo "scale=5;$begainsize*8 "|bc`)
	else
		res=$(printf "%.f" `echo "scale=5;$begainsize/128 "|bc`)  # celling 取整数 http://blog.youkuaiyun.com/naiveloafer/article/details/8783518
	fi

	cd /data/executeTask/file_compact/
	# split into $res files with number suffix.  reg http://blog.youkuaiyun.com/microzone/article/details/52839598
	compact_file_name=$compact_file"_"
	echo "compact_file_name :"$compact_file_name
	split -n l/$res /data/executeTask/file_compact/$compact_file -d -a 3 /data/executeTask/file_compact/${compact_file_name}
	
	#for test
	#res=34
	#compact_file=compact.151018888
	#split -n l/$res /data/executeTask/file_compact/tmp/$compact_file -d -a 3 ${compact_file}"_"
	
	
	# 2 copyFromLocal. 
	hdfs dfs -copyFromLocal /data/executeTask/file_compact/$compact_file_name* /data/$table_name/$date/
	errorCode=$?
	processname=copyFromLocal
	checksuccess $project $errorCode $processname $date $logfile $table_name

	
	# 3. chown this step can be skip 
	hdfs dfs -chown flume /data/$table_name/$date/$compact_file_name*
	errorCode=$?
	processname=chown
	checksuccess $project $errorCode $processname $date $logfile $table_name

	
	# 4
	# deprecated endsize=`hdfs dfs -du -s -h /data/$table_name/$date/ | awk '{ print $1}' `
	endsize=`hdfs dfs -du -s -h /data/$table_name/$date/compact* |  awk '{sum+=$1/1024};END{print sum} ' `
	echo $begainsize  >> $logfile
	echo $endsize >> $logfile

	percent=`echo "scale=2;$begainsize/$endsize" | bc`
	upper=1.2
	lower=0.8
	c1=$(echo "$percent < $upper" | bc)
	c2=$(echo "$percent > $lower" | bc)

	if [ $c1 -eq 1 ] && [ $c2 -eq 1 ];then
		echo " accessable rate"  
	else 
		echo " warn rate" 
		processname=sizerate
		checksuccess $project 1 $processname $date $logfile $table_name
	fi

	
	# 5. rm data after check option
	hdfs dfs -rm /data/$table_name/$date/*FlumeData*
	errorCode=$?
	processname=rmFlumeData
	checksuccess $project $errorCode $processname $date $logfile $table_name

	
	# 6. rm local compact file #  remove local file before getmerge
	rm -rf /data/executeTask/file_compact/$compact_file*
	errorCode=$?
	processname=rmLocalFile
	checksuccess $project $errorCode $processname $date $logfile $table_name
}

#################### processCompact function END #################### 

#################### main start #################### 
#################### config begin #################### 
project=obdAndipda

#the begin day to process 
date=20170216

logfile=/data/job/log/compact_flumedata.log
#the end day to process  
end=`date -d' -1 day' +"%Y%m%d"` 
end=20170216

echo "start: ${date} end: $end"
#################### config end #################### 
while(( $date <= $end ))
do
#################### prepare #################### 
# prepare 
	dealtime=`date +%s`
	compact_file="compact."${dealtime}
	echo "deal date:"${date}
	#################### prepare END #################### 
	table_name="table_name"
	echo "/data/$table_name/$date/"
	processCompact $table_name $date $compact_file $logfile $project $table_name

	date=`date -d"-1 day ago ${date}" +%Y%m%d`

done
echo "complete";
exit 0
#################### main end #################### 



### 如何将本地目录中新创建的文件同步或上传到HDFS 为了实现将本地目录中的新文件自动同步或上传到HDFS,可以采用以下方法: #### 方法一:通过脚本监控本地目录变化并触发上传操作 可以通过编脚本来定期检查本地目录的变化,并将新增加的文件上传到HDFS。以下是具体实现方式。 1. **使用`inotify-tools`工具监听本地目录** `inotify-tools`是一个Linux下的工具包,能够实时监测文件系统的变动情况(如新建、修改、删除等)。安装命令如下: ```bash sudo apt-get install inotify-tools ``` 2. **编Shell脚本完成文件上传** 下面是一段示例脚本,用于检测指定目录的新建文件并将它们上传到HDFS。 ```bash #!/bin/bash MONITOR_DIR="/tmp/test" # 需要监控的本地目录 HDFS_TARGET_DIR="/user/hadoop/data" # 对应的目标HDFS路径 while true; do # 使用inotifywait等待文件被创建 created_file=$(inotifywait -e create --format '%w%f' "$MONITOR_DIR") if [[ ! -z $created_file ]]; then echo "Detected new file: $created_file" # 延迟几秒确保文件完全入完毕再上传 sleep 5 # 将文件上传到HDFS hdfs dfs -put "$created_file" "$HDFS_TARGET_DIR/" # 如果需要可以在上传成功后删除原文件 rm "$created_file" fi done ``` 上述脚本实现了对`/tmp/test`目录的持续监控,一旦发现有新的文件生成,则延迟5秒钟后再将其上传到目标HDFS路径下[^4]。 3. **注意事项** - 文件名建议带上时间戳或者日期前缀以便区分不同批次的数据。 - 可能还需要考虑异常处理逻辑,比如网络中断等情况可能导致上传失败等问题。 #### 方法二:利用Flume采集日志类数据流式传输至HDFS 如果业务场景涉及频繁产生的小规模增量更新型的日志记录之类的非结构化文本数据源的话,那么推荐借助Apache Flume这样的分布式高可用可靠的服务框架来进行收集汇总工作之后统一存放到远端集群节点上的HDFS之中去保存起来供后续分析挖掘用途所用。 配置好source channel sink三个组件之间的关系链路图即可轻松达成目的;其中Source负责接收来自外部环境输入过来的消息事件流Channel充当缓冲区角色Sink则指向最终目的地即我们的HDFS系统内部特定位置区域范围内[^5]。 --- #### 性能优化提示 由于HDFS本身并非专为管理大量小型独立对象而设计构建出来的存储解决方案因此针对这种情况我们往往采取预先合并打包成较大尺寸批量单元形式然后再执行实际物理层面真正意义上的PUT动作从而有效减少元数据开销提升整体效率表现水平同时兼顾降低NameNode内存压力风险隐患等方面因素综合考量得出结论[^1]。 ```python import os from datetime import datetime def merge_files(source_dir, target_file): with open(target_file, 'wb') as tf: for root, dirs, files in os.walk(source_dir): for fname in sorted(files): # 按照一定顺序排列组合防止乱序现象发生 path = os.path.join(root, fname) try: with open(path, 'rb') as sf: tf.write(sf.read()) print(f"Merged {fname} into {target_file}") except Exception as e: print(f"Error merging {fname}: {str(e)}") if __name__ == "__main__": now = datetime.now().strftime("%Y%m%d%H%M%S") source_directory = "/path/to/local/files" merged_output_path = f"/path/to/output/{now}_merged.dat" merge_files(source_directory, merged_output_path) # 接下来可继续调用hdfs命令或者其他API接口实施进一步转移过程... ``` 上述Python程序片段展示了如何遍历某个给定路径下面的所有子项进而逐一拼接组装形成单一巨型文档实体最后一步骤便是按照既定计划安排部署提交上去完成整个流程闭环运作机制建设任务目标达成效果验证评估总结反思改进措施制定落实行动方案推进落地实践检验成果展示分享交流学习成长共同进步共赢未来愿景展望规划布局谋篇布局构思创意创新突破自我超越极限追求卓越永不止步勇往直前无惧挑战迎接机遇把握机会成就辉煌人生价值意义非凡无比珍贵值得珍惜爱护珍藏铭刻于心铭记不忘时刻牢记始终践行初心使命担当责任奉献社会造福人类共创美好明天! ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mtj66

看心情

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值