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

问题场景:

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

设想的解决方案:

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

现有解决方案:

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

基于文件的队列处理设计

主要用的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. FFmpeg and x264 Encoding Guide

    https://trac.ffmpeg.org/wiki/Encode/H.264 FFmpeg and H.264 Encoding Guide Contents Constant Rate Fac ...

  2. mobile angualar ui的简单使用

    最近做一个微信App形式的业务平台,之前从别人的推荐文中知道了mobile angualar ui这个东西,这次纯做mobile Web就试用了一下,之前PCWeb中用过AngularJS,Hybri ...

  3. php添加日志文件

    记录一下. 有时候写测试代码的时候,不习惯直接在屏幕上输出反馈,那么可以配置日志文件,把需要输出的内容追加到日志文件里面,就很方便. Php自带日志系统,可以参考网上的博客配置. 我要说的是,如果你的 ...

  4. Mysql分页查询性能分析

    [PS:原文手打,转载说明出处,博客园] 前言 看过一堆的百度,最终还是自己做了一次实验,本文基于Mysql5.7.17版本,Mysql引擎为InnoDB,编码为utf8,排序规则为utf8_gene ...

  5. sql server 多行数据合并成一列

    首先是源数据: ),cip.CheckIn_StartTime, )),cip.CheckIn_EndTime, )),cip.Rental_Price)) as content from Check ...

  6. JS windows对象的top属性

    原博文:http://www.jb51.net/article/44078.htm   本文为大家介绍下JS window对象的top.parent.opener含义,不了解的朋友可以参考下,希望对大 ...

  7. c#语言中的Process进程类型的使用示例

    下面我们用一个简单的例子来说明如何使用 我们用vs2015新建一个解决方案,这个解决方案包含两个WINFORM窗体项目,一个是SoftWare.Test,一个是SoftWare.Update,如下图所 ...

  8. JSON-RPC远程调用协议

    1. JSON-RPC简介 2. 请求 3. 响应 4. 错误 4.1. 错误对象 4.2. 错误码 5. 批量调用 6. 示例 6.1. 列表形式参数 6.2. key-value形式参数 6.3. ...

  9. hadoop HA 详解

    NameNode 高可用整体架构概述 在 Hadoop 1.0 时代,Hadoop 的两大核心组件 HDFS NameNode 和 JobTracker 都存在着单点问题,这其中以 NameNode ...

  10. 洛谷 P1129 解题报告

    P1129 [ZJOI2007]矩阵游戏 题目描述 小\(Q\)是一个非常聪明的孩子,除了国际象棋,他还很喜欢玩一个电脑益智游戏――矩阵游戏.矩阵游戏在一个\(N*N\)黑白方阵进行(如同国际象棋一般 ...