- 浏览: 129320 次
- 性别:
- 来自: 深圳
-
文章分类
最新评论
SpoolDirectorySource使用及源码分析
Spooling Directory Source简介
Spooling Directory Source可以获取硬盘上“spooling”目录的数据,这个Source将监视指定目录是否有新文件,如果有新文件的话,就解析这个新文件。事件的解析逻辑是可插拔的。在文件的内容所有的都读取到Channel之后,Spooling Directory Source会重名或者是删除该文件以表示文件已经读取完成。不像Exec Source,这个Source是可靠的,且不会丢失数据。即使Flume重启或者被Kill。但是需要注意如下两点:
1,如果文件在放入spooling目录之后还在写,那么Flume会打印错误日志,并且停止处理该文件。
2,如果文件之后重复使用,Flume将打印错误日志,并且停止处理。
为了避免以上问题,我们可以使用唯一的标识符来命令文件,例如:时间戳。
尽管这个Source是可靠的,但是如果下游发生故障,也会导致Event重复,这种情况就需要通过Flume的其他组件提供保障了。
channels | – |
|
type | – |
组件名:spooldir .
|
spoolDir | – | 读取文件的目录。 |
fileSuffix | .COMPLETED | Spooling读取过的文件,添加的后缀。 |
deletePolicy | never |
完成后的文件是否删除。never:不删除 或immediate:立即删除
|
fileHeader | false | 是不把路径加入到Heander |
fileHeaderKey | file | 路径加入到Header的Key是什么 |
basenameHeader | false | 是不把文件名加入到Heander |
basenameHeaderKey | basename | 文件名加入到Header的Key是什么 |
ignorePattern | ^$ | 采用正则表达是去过滤一些文件。只有符合正则表达式的文件才会被使用。 |
trackerDir | .flumespool | 被处理文件的元数据的存储目录,如果不是绝对路径,就被会解析到spoolDir目录下。 |
consumeOrder | oldest |
消费spooling目录文件的规则,分别有:oldest,youngest和random。在oldest 和 youngest的情况下, 通过文件的最后修改时间来比较文件。如果最后修改时间相同,就根据字典的序列从小开始。在随机的情况 下,就随意读取文件。如果文件列表很长,采用oldest/youngest可能会很慢,因为用oldest/youngest要 扫描文件。但是如果采用random的话,就可能造成新的文件消耗的很快,老的文件一直都没有被消费。 |
maxBackoff | 4000 | 如果Channel已经满了,那么该Source连续尝试写入该Channel的最长时间(单位:毫秒)。 |
batchSize | 100 | 批量传输到Channel的粒度。 |
inputCharset | UTF-8 | 字符集 |
decodeErrorPolicy |
FAIL
|
在文件中有不可解析的字符时的解析策略。FAIL : 抛出一个异常,并且不能解析该文件。REPLACE :
取代不可解析的字符,通常用Unicode U+FFFD. IGNORE : 丢弃不可能解析字符序列。
|
deserializer |
LINE
|
自定序列化的方式,自定的话,必须实现EventDeserializer.Builder .
|
deserializer.* |
|
|
bufferMaxLines | – | 已废弃。 |
bufferMaxLineLength | 5000 | (不推荐使用) 一行中最大的长度,可以使用deserializer.maxLineLength代替。 |
selector.type | replicating | replicating(复制) 或 multiplexing(复用) |
selector.* |
|
取决于selector.type的值 |
interceptors | – | 空格分割的interceptor列表。 |
interceptors.* |
|
|
SpoolDirectorySource示例
读取文件写入到file_roll中
a1.sources = source1 a1.sinks = sink1 a1.channels = channel1 #resources a1.sources.source1.type = spooldir a1.sources.source1.channels = channel1 a1.sources.source1.spoolDir = E:\\home\\spooling a1.sources.source1.fileHeader = true a1.sources.source1.fileHeaderKey = fishfile a1.sources.source1.basenameHeader = true a1.sources.source1.basenameHeaderKey = fishbasename a1.sinks.sink1.type = file_roll a1.sinks.sink1.sink.directory = E:\\home\\file_roll a1.sinks.sink1.sink.rollInterval = 300 a1.sinks.sink1.sink.serializer = TEXT a1.sinks.sink1.sink.batchSize = 100 a1.channels.channel1.type = memory a1.channels.channel1.capacity = 1000 a1.channels.channel1.transactionCapacity = 100 a1.sources.source1.channels = channel1 a1.sinks.sink1.channel = channel1
SpoolDirectorySource源码分析
一,调用configure(Context context)方法初始化:
@Override public synchronized void configure(Context context) { //spool目录 spoolDirectory = context.getString(SPOOL_DIRECTORY); Preconditions.checkState(spoolDirectory != null, "Configuration must specify a spooling directory"); //完成后的文件后缀 completedSuffix = context.getString(SPOOLED_FILE_SUFFIX, DEFAULT_SPOOLED_FILE_SUFFIX); //删除策略,never:不删除 或 immediate:立即删除 deletePolicy = context.getString(DELETE_POLICY, DEFAULT_DELETE_POLICY); //以下四个参数是是否在header中加入文件名和文件路径。 fileHeader = context.getBoolean(FILENAME_HEADER, DEFAULT_FILE_HEADER); fileHeaderKey = context.getString(FILENAME_HEADER_KEY, DEFAULT_FILENAME_HEADER_KEY); basenameHeader = context.getBoolean(BASENAME_HEADER, DEFAULT_BASENAME_HEADER); basenameHeaderKey = context.getString(BASENAME_HEADER_KEY, DEFAULT_BASENAME_HEADER_KEY); //批量处理的数量 batchSize = context.getInteger(BATCH_SIZE, DEFAULT_BATCH_SIZE); //字符集 inputCharset = context.getString(INPUT_CHARSET, DEFAULT_INPUT_CHARSET); //在文件中有不可解析的字符时的解析策略 decodeErrorPolicy = DecodeErrorPolicy.valueOf( context.getString(DECODE_ERROR_POLICY, DEFAULT_DECODE_ERROR_POLICY) .toUpperCase(Locale.ENGLISH)); //过滤文件的正则表达式 ignorePattern = context.getString(IGNORE_PAT, DEFAULT_IGNORE_PAT); //被处理文件的元数据的存储目录 trackerDirPath = context.getString(TRACKER_DIR, DEFAULT_TRACKER_DIR); //序列化 deserializerType = context.getString(DESERIALIZER, DEFAULT_DESERIALIZER); deserializerContext = new Context(context.getSubProperties(DESERIALIZER + ".")); //消费spooling目录文件的规则 consumeOrder = ConsumeOrder.valueOf(context.getString(CONSUME_ORDER, DEFAULT_CONSUME_ORDER.toString()).toUpperCase(Locale.ENGLISH)); // "Hack" to support backwards compatibility with previous generation of // spooling directory source, which did not support deserializers Integer bufferMaxLineLength = context.getInteger(BUFFER_MAX_LINE_LENGTH); if (bufferMaxLineLength != null && deserializerType != null && deserializerType.equalsIgnoreCase(DEFAULT_DESERIALIZER)) { deserializerContext.put(LineDeserializer.MAXLINE_KEY, bufferMaxLineLength.toString()); } maxBackoff = context.getInteger(MAX_BACKOFF, DEFAULT_MAX_BACKOFF); if (sourceCounter == null) { sourceCounter = new SourceCounter(getName()); } }
start方法:
@Override public synchronized void start() { logger.info("SpoolDirectorySource source starting with directory: {}", spoolDirectory); executor = Executors.newSingleThreadScheduledExecutor(); File directory = new File(spoolDirectory); //构建ReliableSpoolingFileEventReader对象 try { reader = new ReliableSpoolingFileEventReader.Builder() .spoolDirectory(directory) .completedSuffix(completedSuffix) .ignorePattern(ignorePattern) .trackerDirPath(trackerDirPath) .annotateFileName(fileHeader) .fileNameHeader(fileHeaderKey) .annotateBaseName(basenameHeader) .baseNameHeader(basenameHeaderKey) .deserializerType(deserializerType) .deserializerContext(deserializerContext) .deletePolicy(deletePolicy) .inputCharset(inputCharset) .decodeErrorPolicy(decodeErrorPolicy) .consumeOrder(consumeOrder) .build(); } catch (IOException ioe) { throw new FlumeException("Error instantiating spooling event parser", ioe); } //构建SpoolDirectoryRunnable线程。 Runnable runner = new SpoolDirectoryRunnable(reader, sourceCounter); //每隔POLL_DELAY_MS(500ms)执行以下SpoolDirectoryRunnable线程。 executor.scheduleWithFixedDelay( runner, 0, POLL_DELAY_MS, TimeUnit.MILLISECONDS); super.start(); logger.debug("SpoolDirectorySource source started"); sourceCounter.start(); }构建ReliableSpoolingFileEventReader类,构造方法功能:
1,spooling目录是否存在,是否是目录
2,通过创建零时文件测试spooling目录的权限。
3,创建trackerDir目录和.flumespool-main.meta文件
SpoolDirectoryRunnable线程,主要用于发送和读取Event:
private class SpoolDirectoryRunnable implements Runnable { private ReliableSpoolingFileEventReader reader; private SourceCounter sourceCounter; public SpoolDirectoryRunnable(ReliableSpoolingFileEventReader reader, SourceCounter sourceCounter) { this.reader = reader; this.sourceCounter = sourceCounter; } @Override public void run() { int backoffInterval = 250; try { while (!Thread.interrupted()) { //ReliableSpoolingFileEventReader读取batchSize大小的Event List<Event> events = reader.readEvents(batchSize); if (events.isEmpty()) { break; } //统计 sourceCounter.addToEventReceivedCount(events.size()); sourceCounter.incrementAppendBatchReceivedCount(); try { //将Event数组发送到Channel getChannelProcessor().processEventBatch(events); //commit会记录最后一次读取的行数,以便下次知道从哪里开始读 reader.commit(); } catch (ChannelException ex) { //ChannelProcessor批量提交Event出错,会抛出ChannelException异常,此时reader.commit是没有执行的 //所以在接下来的continue后,继续通过reader读取文件的话,还是从原来的位置读取,以保证数据不会丢失。 logger.warn("The channel is full, and cannot write data now. The " + "source will try again after " + String.valueOf(backoffInterval) + " milliseconds"); hitChannelException = true; if (backoff) { TimeUnit.MILLISECONDS.sleep(backoffInterval); backoffInterval = backoffInterval << 1; backoffInterval = backoffInterval >= maxBackoff ? maxBackoff : backoffInterval; } continue; } backoffInterval = 250; sourceCounter.addToEventAcceptedCount(events.size()); sourceCounter.incrementAppendBatchAcceptedCount(); } } catch (Throwable t) { logger.error("FATAL: " + SpoolDirectorySource.this.toString() + ": " + "Uncaught exception in SpoolDirectorySource thread. " + "Restart or reconfigure Flume to continue processing.", t); hasFatalError = true; Throwables.propagate(t); } } }ReliableSpoolingFileEventReader读取Event:
public List<Event> readEvents(int numEvents) throws IOException { //committed初始化为true if (!committed) { if (!currentFile.isPresent()) { throw new IllegalStateException("File should not roll when " + "commit is outstanding."); } logger.info("Last read was never committed - resetting mark position."); //正常情况下,会在SpoolDirectorySource类中记录读取的字节数之后,将commited设置为true //没有设置为true,可能是因为发送到Channel异常了,调用下面reset方法可以保证数据不丢失。 currentFile.get().getDeserializer().reset(); } else { // Check if new files have arrived since last call if (!currentFile.isPresent()) { //读取文件,读取文件过程中使用FileFilter过滤掉completedSuffix后缀的文件,然后根据消费文件的规则(consumeOrder)去消费文件。 currentFile = getNextFile(); } // Return empty list if no new files if (!currentFile.isPresent()) { return Collections.emptyList(); } } EventDeserializer des = currentFile.get().getDeserializer(); //根据序列化类读取Event List<Event> events = des.readEvents(numEvents); /* It's possible that the last read took us just up to a file boundary. * If so, try to roll to the next file, if there is one. * Loop until events is not empty or there is no next file in case of 0 byte files */ while (events.isEmpty()) { logger.info("Last read took us just up to a file boundary. Rolling to the next file, if there is one."); retireCurrentFile(); currentFile = getNextFile(); if (!currentFile.isPresent()) { return Collections.emptyList(); } events = currentFile.get().getDeserializer().readEvents(numEvents); } //添加<span style="font-family:Microsoft Yahei;">文件路径到Header</span> if (annotateFileName) { String filename = currentFile.get().getFile().getAbsolutePath(); for (Event event : events) { event.getHeaders().put(fileNameHeader, filename); } } //添加文件名到Header if (annotateBaseName) { String basename = currentFile.get().getFile().getName(); for (Event event : events) { event.getHeaders().put(baseNameHeader, basename); } } committed = false; lastFileRead = currentFile; return events; }消费文件的规则,以OLDEST为例:
for (File candidateFile: candidateFiles) { //已选择文件的最后修改时间,减去文件列表取的文件最后修改时间 long compare = selectedFile.lastModified() - candidateFile.lastModified(); if (compare == 0) { // ts is same pick smallest lexicographically. //时间一样就根据字典序列排序 selectedFile = smallerLexicographical(selectedFile, candidateFile); } else if (compare > 0) { // candidate is older (cand-ts < selec-ts). selectedFile = candidateFile; } }
在ReliableSpoolingFileEventReader类读取Events之后,调用commit方法提交,这里有个问题,batchSize为200,我们每次也就读取200行,那么SpoolingDirectorySource是如何标记我们读到文件的那个位置的呢?
其实SpoolingDirectorySource在commit时,会调用EventDeserializer的mark方法标记此时读取在文件的什么位置。源码如下:
public synchronized void storePosition(long position) throws IOException { metaCache.setOffset(position); writer.append(metaCache); writer.sync(); writer.flush(); }上面的方法会把文件的读取到什么位置记录到.flumespool\.flumespool-main.meta(默认情况下)文件中。
public void commit() throws IOException { if (!committed && currentFile.isPresent()) { currentFile.get().getDeserializer().mark(); //mark成功后,将committed设置为true committed = true; } }
相关推荐
# 使用SpoolDirectorySource,它会监控指定目录下新产生的文件 agent.sources.logSource.type = spoolDir # 指定日志文件的输入目录 agent.sources.logSource.spoolDir = /path/to/log/files # 文件被读取后自动...
脆弱水印技术在图像篡改检测中的应用与挑战,脆弱水印技术在图像篡改检测中的应用与挑战,脆弱水印的图像篡改检测 ,脆弱水印; 图像篡改; 检测; 图像处理,基于脆弱水印的图像篡改检测技术
高效Delta机械臂运动控制卡:前瞻轨迹规划,G代码编程,多维插补,激光切割与绘图,机器视觉集成,扩展坐标与旋转功能,一键脱机运行,大容量存储,基于前瞻运动轨迹规划的Delta机械臂运动控制卡:高效G代码编程,圆弧插补与激光切割功能,配合机器视觉实现精准操作。高效精准操作与管理工具的创新型机械运动控制解决方案。,delta机械臂,delta机器人,运动控制器,运动控制卡 本卡采用前瞻运动轨迹规划,运动采用G代码指令编程,具有G5三维空间的圆弧插补,空间直线插补功能,子程序编程功能,逻辑判断语句功能,示教编程功能(支持手柄),变量位置编程功能,动态PWM激光输出功能(兼容舵机控制信号),动态频率脉冲输出功能,通用输入输出功能。 可极简单的实现绘图雕刻,3维激光切割功能。 轨迹图形可xy平面整体旋转功能。 可利用变量位置,获取外部坐标要求,可轻松配合机器视觉。 支持探针功能,测平面,测外形等。 可设置4组平移工件坐标系,2组参考原点。 新增2组空间旋转工件坐标系,支持任意图形直接空间旋转。 卡上一键脱机RAM区运行功能。 2M程序容量。 断电后位置记忆,变量坐标位置记忆,计数器记忆。 伺服
毕业设计
内容概要:随着模型参数量不断扩大,如从BERT到GPT-3,传统微调方法变得不可行。文章聚焦于参数高效微调(PEFT)策略,系统探讨了几十余种方法,包括加法型、选择型、重构型及其混合方法。文中详细介绍各类PEFT的具体操作(如引入额外参数、冻结部分权重等),并通过广泛实验验证其在大型预训练模型上的适用性和性能。特别指出,PEFT在保持高性能的同时极大减少了计算与内存成本,并针对十几亿乃至几十亿参数级别的模型展开测试与讨论。 适用人群:适用于从事大规模机器学习模型研究、开发的应用科学家和技术专家,尤其是那些希望通过减少资源消耗实现高效微调的技术团队成员。 使用场景及目标:该文章适用于希望在有限资源条件下优化大模型性能的人群。帮助研究人员理解不同类型PEFT的优点和局限,为实际项目中选择合适技术路线提供建议。其目的是为了指导开发者正确理解和应用先进的PEFT技术,从而提高系统的运行效率和服务质量。 其他说明:本文不仅提供了详尽的方法介绍和性能对比,而且为未来的研究指明方向,鼓励创新思维的发展,旨在推动参数有效调优领域的进步。同时提醒注意现有的挑战和未解决问题。
磷酸铁锂体系电池COMSOL模型构建解析与实践指南,磷酸铁锂体系电池COMSOL建模分析与优化方案探讨,出一个磷酸铁锂体系电池comsol模型 ,建立磷酸铁锂体系电池; comsol模型; 电池模拟; 模型构建; 锂离子电池。,构建磷酸铁锂体系电池Comsol模型,深入探索电池性能
开关磁阻电机多维控制策略仿真研究(基于Matlab 2016b的精细化模型),开关磁阻电机多策略控制仿真模型(matlab 2016b版本,含传统与智能控制策略及离线迭代算法),开关磁阻电机控制仿真(matlab 2016b版本仿真模型 自用) 模型包涵: 开关磁阻电机传统控制:电流斩波控制、电压PWM控制、角度位置控制。 智能控制:12 8三相开关磁阻电机有限元分析本体建模、转矩分配函数控制、模糊PID控制、模糊角度控制、神经网络在线自适应迭代控制。 部分离线迭代算法:遗传算法优化PID、粒子群算法优化PID。 biye研究生自用仿真模型 . ,核心关键词: 开关磁阻电机; 控制仿真; Matlab 2016b; 传统控制; 智能控制; 有限元分析; 转矩分配函数控制; 模糊PID控制; 神经网络在线自适应迭代控制; 遗传算法优化PID; 粒子群算法优化PID; 研究生自用仿真模型。,基于Matlab 2016b的开关磁阻电机控制模型研究与仿真优化研究生自用版
McgsPro_IoT驱动_V3.1.1.8
数学建模相关主题资源2
基于改进粒子群算法的光伏储能选址定容模型分析——针对14节点配网系统的实践与出力情况探索,基于改进粒子群算法的光伏储能选址定容模型分析与出力预测研究(含配图材料参考),含光伏的储能选址定容模型 14节点 程序采用改进粒子群算法,对分析14节点配网系统中的储能选址定容方案,并得到储能的出力情况,有相关参考资料 ,核心关键词:含光伏的储能选址定容模型;14节点;改进粒子群算法;配网系统;储能选址定容方案;出力情况;参考资料。,基于改进粒子群算法的14节点配网光伏储能选址定容模型及出力分析研究
基于需求响应与阶梯式碳交易的综合能源系统优化调度模型研究(MATLAB仿真实现),基于需求响应与碳交易的综合能源系统优化调度策略:灵活调配冷热电负荷,实现低碳高效运行。,考虑需求响应和碳交易的综合能源系统日前优化调度模型 关键词:柔性负荷 需求响应 综合能源系统 参考:私我 仿真平台:MATLAB yalmip+cplex 主要内容:在冷热电综合能源系统的基础上,创新性的对用户侧资源进行了细致的划分和研究,首先按照能源类型将其分为热负荷需求响应和电负荷需求响应,在此基础上,进一步分为可削减负荷、可转移负荷以及可平移负荷三类,并将柔性负荷作为需求响应资源加入到综合能源的调度系统中,从而依据市场电价灵活调整各类负荷,实现削峰填谷,改善负荷曲线等优势,此外,为了丰富内容,还考虑了阶梯式碳交易,构建了考虑阶梯式碳交易以及综合需求响应的综合能源低碳经济调度模型,设置了多个对比场景,验证所提模型的有效性,从而体现工作量,是不可多得的代码 场景一: 这段程序主要是用来进行某微网的运行优化。它包含了多个功能和应用,涉及到了能源集线器、需求侧柔性负荷、光伏、风机、燃气轮机等内容。 首先,程序读取了
multisim
内容概要:本文详细介绍了一系列用于科学研究、工程项目和技术开发中至关重要的实验程序编写与文档报告撰写的资源和工具。从代码托管平台(GitHub/GitLab/Kaggle/CodeOcean)到云端计算环境(Colab),以及多种类型的编辑器(LaTeX/Microsoft Word/Overleaf/Typora),还有涵盖整个研究周期的各种辅助工具:如可视化工具(Tableau)、数据分析平台(R/Pandas)、项目管理工具(Trello/Jira)、数据管理和伦理审核支持(Figshare/IRB等),最后提供了典型报告的具体结构指导及其范本实例链接(arXiv/PubMed)。这为实验流程中的各个环节提供了系统的解决方案,极大地提高了工作的效率。 适合人群:高校学生、科研工作者、工程技术人员以及从事学术写作的人员,无论是新手入门还是有一定经验的人士都能从中受益。 使用场景及目标:帮助读者高效地准备并开展实验研究活动;促进团队间协作交流;规范研究报告的形式;提高对所收集资料的安全性和隐私保护意识;确保遵循国际公认的伦理准则进行实验。
基于OpenCV与深度学习的人脸表情识别系统:Python编程,实时检测与视频加载的PyQt界面应用,基于OpenCV与深度学习的人脸表情识别系统:Python编程,PyQt界面,实时视频与图片检测.exe可执行文件,基于OpenCV的人脸表情识别系统 相关技术:python,opencv,pyqt,深度学习 (请自行安装向日葵远程软件,以便提供远程帮助) 可编译为.exe文件。 软件说明:摄像头实时检测,加载照片,视频均可。 有基础的同学,可自行修改完善。 第一张和第二张为运行截图。 ,人脸表情识别; Op
基于双端口直流微电网系统模型的改进下垂控制及稳定性分析(含电压鲁棒控制器与粒子群寻优权函数),基于双端口直流微电网系统模型的优化设计与分析:改进下垂控制、电压鲁棒控制器及仿真研究,直流微网,直流微电网系统模型,有两个端口。 外环有改进下垂控制,内环双pi环,带恒功率负载。 暂态性能良好,可用于控制器设计,稳定性分析等。 另外还有电压鲁棒控制器,小信号模型,根轨迹分析,粒子群寻优权函数等内容。 仅为simulink ,直流微网; 直流微电网系统模型; 改进下垂控制; 双pi环; 恒功率负载; 暂态性能; 控制器设计; 稳定性分析; 电压鲁棒控制器; 小信号模型; 根轨迹分析; 粒子群寻优权函数,基于改进下垂控制的直流微网系统模型:双PI环与恒功率负载研究
这是萨达萨达是发生发士大夫
Labview下的通用OCR识别技术:高效文本识别与图像处理解决方案,Labview下的通用OCR识别技术:提高文字识别效率与准确度,labview.通用OCR识别技术 ,核心关键词:LabVIEW; 通用OCR识别技术; 识别技术; OCR技术; 图像识别; 文字识别。,LabVIEW平台下的通用OCR识别技术
一个任务待办记录、提醒工具 可设定提前N天开始提醒 数据本地存储