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 ...
随机推荐
- neo4j-rest-client使用摘要
1.使用它的原因,与django搭配的最好的neomodel目前只支持到v2.2,我已给官方发了issue,官方也回复了,马上修改并发布(老外对开源项目的负责态度让人感动) 2.这个库的文档中大概描述 ...
- Golang适合高并发场景的原因分析
http://blog.csdn.NET/ghj1976/article/details/27996095 典型的两个现实案例: 我们先看两个用Go做消息推送的案例实际处理能力. 360消息推送的数据 ...
- dijkstra算法:寻找到全图各点的最短路径
dijkstra算法介绍:即迪杰斯特拉算法,是从一个顶点到其余各顶点的最短路径算法,解决的是有向图中最短路径问题.迪杰斯特拉算法主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止,是一种广度优先 ...
- 【python进阶】深入理解系统进程1
前言 之前程序执⾏都是⼀条腿⾛路,甚⾄是⽤⼀杆枪来打天下. 通过系统编程的学习,会让⼤家有“多条腿”⼀起⾛路,就好⽐有了⼀把机关枪. 此篇为深入理解进程第一篇,下面开始今天的说明~~~ 进程 多任务的 ...
- Flex 将默认日期格式转化成通用格式
flex 默认日期格式如:Wed Dec 16 00:00:00 GMT+0800 2015 想要得到的通用格式如:2015-12-16 转换方法如下: var sdate:String = &quo ...
- Spring 的IOC和AOP总结
Spring 的IOC和AOP IOC 1.IOC 许多应用都是通过彼此间的相互合作来实现业务逻辑的,如类A要调用类B的方法,以前我们都是在类A中,通过自身new一个类B,然后在调用类B的方法,现在我 ...
- 十八、Hadoop学记笔记————Hbase架构
Hbase结构图: Client,Zookeeper,Hmaster和HRegionServer相互交互协调,各个组件作用如下: 这几个组件在实际使用过程中操作如下所示: Region定位,先读取zo ...
- vs2017 x64 ibatis.net 平台调用 Oracle.DataAccess, Version=2.112.1.0, Culture=neutral, PublicKeyToken=89b483f429c47342 x64
遇到的问题: 1.x86无法调用x64 2.调用ibatis.net的providers.config无法通过节点反射查找Oracle.DataAccess, Version=2.112.1.0, C ...
- spring security oauth2 jwt 认证和资源分离的配置文件(java类配置版)
最近再学习spring security oauth2.下载了官方的例子sparklr2和tonr2进行学习.但是例子里包含的东西太多,不知道最简单最主要的配置有哪些.所以决定自己尝试搭建简单版本的例 ...
- LeetCode Javascript实现 100. Same Tree 171. Excel Sheet Column Number
100. Same Tree /** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; ...