现实业务中经常遇到需要队列处理的问题。

问题场景:

客户端记录设备运行数据,传输给服务器。在传输中可能存在延迟或中断情况。
当中断时,系统传输数据可能因为无法传输或电脑重启,会导致服务器数据记录不连续。

设想的解决方案:

当客户端采集到设备数据后,将数据先插入处理队列。
另一个服务程序从队列中取出数据发送到服务器。
数据记录与数据上传做到完全独立,并且是异步操作。
数据记录在硬盘上,保障了数据的连续性和可靠性。
如果一直无法上传到服务器,会导致队列中等待处理的数据积攒过多。

现有解决方案:

使用数据库做队列(可能遇到的问题:客户端性能有限,数据库可靠性差,系统断电重启,可能导致数据库损坏等)
使用专业的消息队列(学习成本高,部署复杂,功能复杂,性能高)
自己实现简单的队列服务(基于文件系统实现,无需部署,简单可靠,性能基本满足需求)

基于文件的队列处理设计

主要用的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# 基于文件系统实现的队列处理类的更多相关文章

  1. 基于数组阻塞队列 ArrayBlockingQueue 的一个队列工具类

    java语言基于ArrayBlockingQueue 开发的一个根据特定前缀和后缀的队列.每天自动循环生成. 1.定义队列基类 Cookie package com.bytter.util.queue ...

  2. 教你如何使用Java手写一个基于数组实现的队列

    一.概述 队列,又称为伫列(queue),是先进先出(FIFO, First-In-First-Out)的线性表.在具体应用中通常用链表或者数组来实现.队列只允许在后端(称为rear)进行插入操作,在 ...

  3. 笔试算法题(57):基于堆的优先级队列实现和性能分析(Priority Queue based on Heap)

    议题:基于堆的优先级队列(最大堆实现) 分析: 堆有序(Heap-Ordered):每个节点的键值大于等于该节点的所有孩子节点中的键值(如果有的话),而堆数据结构的所有节点都按照完全有序二叉树 排.当 ...

  4. 9 DelayQueueEntry 延时队列节点类——Live555源码阅读(一)基本组件类

    这是Live555源码阅读的第一部分,包括了时间类,延时队列类,处理程序描述类,哈希表类这四个大类. 本文由乌合之众 lym瞎编,欢迎转载 http://www.cnblogs.com/oloroso ...

  5. 8 延时队列相关类——Live555源码阅读(一)基本组件类

    这是Live555源码阅读的第一部分,包括了时间类,延时队列类,处理程序描述类,哈希表类这四个大类. 本文由乌合之众 lym瞎编,欢迎转载 http://www.cnblogs.com/oloroso ...

  6. HQueue:基于HBase的消息队列

    HQueue:基于HBase的消息队列   凌柏   ​1. HQueue简介 HQueue是一淘搜索网页抓取离线系统团队基于HBase开发的一套分布式.持久化消息队列.它利用HTable存储消息数据 ...

  7. 基于JavaMail开发邮件发送器工具类

    基于JavaMail开发邮件发送器工具类 在开发当中肯定会碰到利用Java调用邮件服务器的服务发送邮件的情况,比如账号激活.找回密码等功能.本人之前也碰到多次这样需求,为此特意将功能封装成一个简单易用 ...

  8. Python 基于urllib.request封装http协议类

    基于urllib.request封装http协议类 by:授客QQ:1033553122 测试环境: Python版本:Python 3.3   代码实践 #!/usr/bin/env python ...

  9. 基于NSString处理文件的高级类

    基于NSString处理文件的高级类 我已经把处理文件的类简化到了变态的程度,如果你还有更简洁的方法,请告知我,谢谢! 使用详情: 源码: // // NSString+File.h // Maste ...

随机推荐

  1. ASP.NET Core Api网关Ocelot的中文文档

    架构图 入门 不支持 配置 路由 请求聚合 GraphQL 服务发现 微服务ServiceFabric 认证 授权 Websockets 管理 流量控制 缓存 QoS服务质量 转换Headers 转换 ...

  2. 解决Select标签的Option在IE浏览中display:none不生效的问题

    页面的Select标签,需要控制Select的Option不需要显示,根据条件来隐藏某些Option选项. 正常情况下使用hide()就能实现,hide()方法实际是给Option加上display属 ...

  3. ThinkPHP5零基础搭建CMS系统(一)

    了解学习thinkphp5应该是2016年年底的事情,当时还没有接触过thinkphp3版本,觉得通过手册直接上手学习tp5蛮轻松的,现在从零记录下,搭建可扩展的CMS. 1.ThinkPHP环境搭建 ...

  4. 关于react router 4 的小实践

    详细代码栗子:https://github.com/wayaha/react-dom-CY clone然后 npm install npm start 分割线 1.这个项目使用create-react ...

  5. C# SqlBulkCopy数据批量入库

    准备条件:20万+数据 界面设计使用的WPF. 没有对比就没有伤害,以下是我两种方式导入数据案例. 运行 结果对比: 首先使用一般sql语句导入,因为时间原因,我就没有等待程序执行完,但是我记录了大约 ...

  6. 强大的代码编辑器 phpstorm version 2016.2 License Server激活

    "磨刀不误砍柴工","工欲善其事必先利其器",找个一个好的代码开发编辑工具可以让我们事半功倍,并且代码质量得到保障,在这里就推荐一款强大的代码编辑器,不对其实可 ...

  7. CentOS 7.4 MySQL 5.7.20主从环境搭建(M-S)

    MySQL主从原理: 一,master记录二进制日志,在每个事务更新数据完成之前,master在二进制日志中记录这些改变.mysql将事务写入二进制日志,即使事务中的语句都是交叉执行的.在事件写入二进 ...

  8. 网络-tcp

    1.TCP:面向连接可靠的传输协议,全拼:Transmission Control Protocol   2.UDP:用户数据报协议 全拼:User Datagram protocol 不是面向连接的 ...

  9. 免费Git客户端:sourcetree详细介绍

    一.简介:一个用于Windows和Mac的免费Git客户端.Sourcetree简化了如何与Git存储库进行交互,这样您就可以集中精力编写代码.通过Sourcetree的简单Git GUI可视化和管理 ...

  10. 升讯威微信营销系统开发实践:(4)源代码结构说明 与 安装部署说明( 完整开源于 Github)

    GitHub:https://github.com/iccb1013/Sheng.WeixinConstruction因为个人精力时间有限,不会再对现有代码进行更新维护,不过微信接口比较稳定,经测试至 ...