c# 基于文件系统实现的队列处理类
现实业务中经常遇到需要队列处理的问题。
问题场景:
客户端记录设备运行数据,传输给服务器。在传输中可能存在延迟或中断情况。
当中断时,系统传输数据可能因为无法传输或电脑重启,会导致服务器数据记录不连续。
设想的解决方案:
当客户端采集到设备数据后,将数据先插入处理队列。
另一个服务程序从队列中取出数据发送到服务器。
数据记录与数据上传做到完全独立,并且是异步操作。
数据记录在硬盘上,保障了数据的连续性和可靠性。
如果一直无法上传到服务器,会导致队列中等待处理的数据积攒过多。
现有解决方案:
使用数据库做队列(可能遇到的问题:客户端性能有限,数据库可靠性差,系统断电重启,可能导致数据库损坏等)
使用专业的消息队列(学习成本高,部署复杂,功能复杂,性能高)
自己实现简单的队列服务(基于文件系统实现,无需部署,简单可靠,性能基本满足需求)
基于文件的队列处理设计
主要用的C#函数:File.AppendAllText、StreamWriter.WriteLine、File.ReadAllText、File.WriteAllText、File.Delete
好处:
基于系统文件处理函数
队列大小依赖于磁盘大小
队列内容为一行文本,无格式要求
单文件队列

原理:
将业务数据追加到待处理文件中。
处理程序从文件头部读取业务数据,处理完成后,将头部处理过的业务数据删除。
遇到的问题:
当业务处理程序一直失败时,队列文件会随着时间的增长越来越大。导致文件读写缓慢。
因为业务数据写入与业务数据读取操作的是同一个文件,在大量并发时,存在文件锁定问题。
因为待处理文件过大,磁盘操作数据量过大,导致磁盘性能不足。
多文件队列

处理示意图

原理:
业务数据写入时,按日期生成文件写入。一天一个文件,避免单文件过大问题。
业务程序,处理时,将最早的待处理队列文件改名为正在处理文件。避免业务写入和业务处理同时操作一个文件问题。
业务程序处理完成后,记录已经处理到的文件行数,避免多次重写队列文件。
使用方法:
LsLib.FileQueue fileQueue = new LsLib.FileQueue(); // 插入待处理队列
fileQueue.Push(new string[] { "业务数据内容,字符串(中间不能有换行)" }); // 清空队列
fileQueue.Clear(); // 队列待处理数量
fileQueue.Count(); // 获取待处理数据
fileQueue.GetList(); // 设置最早的数据处理完成
fileQueue.Pop();
源代码:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms; namespace LsLib
{
/// <summary>
/// 文件堆栈
/// </summary>
class FileQueue
{
private string QueueName = "file";
private string FileQueuePre = "";
private string FileQueueRunPath = "";
private string FileQueueRunIndexPath = "";
private string FileQueueCountPath = ""; public FileQueue(string queueName = "file")
{
this.QueueName = queueName;
this.FileQueuePre = Application.StartupPath + "\\queue\\" + queueName;
this.FileQueueRunPath = Application.StartupPath + "\\queue\\" + queueName + "_run.dat";
this.FileQueueRunIndexPath = Application.StartupPath + "\\queue\\" + queueName + "_run_index.dat";
this.FileQueueCountPath = Application.StartupPath + "\\queue\\" + queueName + "_count.dat";
} /// <summary>
/// 插入堆栈
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public bool Push(string[] strList)
{
int tryIndex = ;
string filePath = this.FileQueuePre + "_list" + DateTime.Now.ToString("_yyyyMMdd") + ".dat"; while (tryIndex < )
{
try
{
using (StreamWriter sw = new StreamWriter(filePath, true))
{
foreach (var str in strList)
{
sw.WriteLine(str);
}
} SetCount(strList.Length); return true;
}
catch (Exception ex)
{
tryIndex++;
Thread.Sleep();
}
} return false;
} // 设置队列待处理数量
private int SetCount(int i)
{
int count = ;
if (File.Exists(this.FileQueueCountPath))
{
count = int.Parse(File.ReadAllText(this.FileQueueCountPath));
} count += i; File.WriteAllText(this.FileQueueCountPath, count.ToString()); return count;
} /// <summary>
/// 清空堆栈
/// </summary>
/// <returns></returns>
public bool Clear()
{
string[] fileList = Directory.GetFiles(Application.StartupPath + "\\queue\\", QueueName + "_*.dat"); foreach (var file in fileList)
{
File.Delete(file);
} return true;
} /// <summary>
/// 堆栈待处理数量
/// </summary>
/// <returns></returns>
public int Count()
{
int count = ;
if (File.Exists(this.FileQueueCountPath))
{
count = int.Parse(File.ReadAllText(this.FileQueueCountPath));
} return count;
} /// <summary>
/// 获取待处理列表
/// </summary>
/// <param name="count"></param>
/// <returns></returns>
public List<string> GetList(int count = )
{
List<string> list = new List<string>(); bool isFirst = false;
if (!File.Exists(this.FileQueueRunPath))
{
string[] fileList = Directory.GetFiles(Application.StartupPath + "\\queue\\", QueueName + "_list_*.dat");
if (fileList.Length == )
{
return list;
} Array.Sort(fileList); File.Move(fileList[], this.FileQueueRunPath);
isFirst = true;
} int startIndex = ;
int totalCount = ; if (File.Exists(this.FileQueueRunIndexPath))
{
string strIndex = File.ReadAllText(this.FileQueueRunIndexPath);
string[] arrIndex = strIndex.Split(','); startIndex = int.Parse(arrIndex[]);
totalCount = int.Parse(arrIndex[]);
} int index = ;
using (StreamReader sm = File.OpenText(this.FileQueueRunPath))
{
while (true)
{
string str = sm.ReadLine();
if (str == null)
{
break;
}
str = str.Trim(); if (str == "")
{
continue;
} totalCount++;
if (index < startIndex)
{
index++;
continue;
} if (list.Count < count)
{
list.Add(str);
} if (list.Count >= count && !isFirst)
{
break;
}
}
} if (isFirst)
{
File.WriteAllText(this.FileQueueRunIndexPath, "0," + totalCount);
} return list;
} /// <summary>
/// 出栈
/// </summary>
/// <param name="count"></param>
/// <returns></returns>
public bool Pop(int count = )
{
if (!File.Exists(this.FileQueueRunIndexPath))
{
return false;
} string strIndex = File.ReadAllText(this.FileQueueRunIndexPath);
string[] arrIndex = strIndex.Split(','); int startIndex = int.Parse(arrIndex[]) + count;
int totalCount = int.Parse(arrIndex[]); SetCount(- * count); if (startIndex >= totalCount)
{
File.Delete(this.FileQueueRunIndexPath);
File.Delete(this.FileQueueRunPath);
}
else
{
File.WriteAllText(this.FileQueueRunIndexPath, startIndex + "," + totalCount);
} return true;
}
}
}
c# 基于文件系统实现的队列处理类的更多相关文章
- 基于数组阻塞队列 ArrayBlockingQueue 的一个队列工具类
java语言基于ArrayBlockingQueue 开发的一个根据特定前缀和后缀的队列.每天自动循环生成. 1.定义队列基类 Cookie package com.bytter.util.queue ...
- 教你如何使用Java手写一个基于数组实现的队列
一.概述 队列,又称为伫列(queue),是先进先出(FIFO, First-In-First-Out)的线性表.在具体应用中通常用链表或者数组来实现.队列只允许在后端(称为rear)进行插入操作,在 ...
- 笔试算法题(57):基于堆的优先级队列实现和性能分析(Priority Queue based on Heap)
议题:基于堆的优先级队列(最大堆实现) 分析: 堆有序(Heap-Ordered):每个节点的键值大于等于该节点的所有孩子节点中的键值(如果有的话),而堆数据结构的所有节点都按照完全有序二叉树 排.当 ...
- 9 DelayQueueEntry 延时队列节点类——Live555源码阅读(一)基本组件类
这是Live555源码阅读的第一部分,包括了时间类,延时队列类,处理程序描述类,哈希表类这四个大类. 本文由乌合之众 lym瞎编,欢迎转载 http://www.cnblogs.com/oloroso ...
- 8 延时队列相关类——Live555源码阅读(一)基本组件类
这是Live555源码阅读的第一部分,包括了时间类,延时队列类,处理程序描述类,哈希表类这四个大类. 本文由乌合之众 lym瞎编,欢迎转载 http://www.cnblogs.com/oloroso ...
- HQueue:基于HBase的消息队列
HQueue:基于HBase的消息队列 凌柏 1. HQueue简介 HQueue是一淘搜索网页抓取离线系统团队基于HBase开发的一套分布式.持久化消息队列.它利用HTable存储消息数据 ...
- 基于JavaMail开发邮件发送器工具类
基于JavaMail开发邮件发送器工具类 在开发当中肯定会碰到利用Java调用邮件服务器的服务发送邮件的情况,比如账号激活.找回密码等功能.本人之前也碰到多次这样需求,为此特意将功能封装成一个简单易用 ...
- Python 基于urllib.request封装http协议类
基于urllib.request封装http协议类 by:授客QQ:1033553122 测试环境: Python版本:Python 3.3 代码实践 #!/usr/bin/env python ...
- 基于NSString处理文件的高级类
基于NSString处理文件的高级类 我已经把处理文件的类简化到了变态的程度,如果你还有更简洁的方法,请告知我,谢谢! 使用详情: 源码: // // NSString+File.h // Maste ...
随机推荐
- 前端CDN公共库整理
转自: 灰狼博客, 地址: http://itlobo.com/articles/2016.html 现在web应用都在使用js类库,这些类库小的几十K,大的几百K,而国内网络访问速度大家都知道不是那 ...
- Lintcode397 Longest Increasing Continuous Subsequence solution 题解
[题目描述] Give an integer array,find the longest increasing continuous subsequence in this array. An in ...
- Jmeter(二十七)_Beanshell保存响应内容到本地
利用Jmeter-BeanShell PostProcessor可以提取响应结果并保存到本地文件,这种操作在jmeter做爬虫时非常有用,可以帮助你迅速的获取想要的内容到本地文件! 1:在本地新建一个 ...
- ws-trust、域、webservice接口的总结
最近燃料公司门户做了一个待办的汇总,从三个数据源拿数据汇总到首页,这三个数据源分别是域认证的接口,域认证的webservices,证书加密的接口,下面就这些接口,做一下简单总结 1 pfx证书的探索过 ...
- Qt5.7 实现Https 认证全过程解析(亲自动手版)
#### NetworkRequestManager.h #include <QSsl>#include <QSslKey>#include <QSslSocket> ...
- svn部署项目
svn部署项目 在svn服务器上文件夹拷入项目文件~然后直接检出文件夹~即可
- Alfred效率神器
下图就是Alfred的主界面我们所有的操作都在这一个界面上进行.通过热键打开主界面(本人设置的是option+command),输入一个"a"后Alfred就会为我在候选界面上显示 ...
- PAT1083:List Grades
1083. List Grades (25) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue Given a l ...
- Asp.Net Core 2.0 项目实战(6)Redis配置、封装帮助类RedisHelper及使用实例
本文目录 1. 摘要 2. Redis配置 3. RedisHelper 4.使用实例 5. 总结 1. 摘要 由于內存存取速度远高于磁盘读取的特性,为了程序效率提高性能,通常会把常用的不常变动的数 ...
- RPC详解
RPC(Remote Procedure Call),即远程过程调用,是一个分布式系统间通信的必备技术,本文体系性地介绍了 RPC 包含的核心概念和技术,希望读者读完文章,一提到 RPC,脑中不是零碎 ...