问题描述
|
好的,这是情况...我有一个应用程序,每秒生成大约8个文件。每个文件为19-24kb。这每分钟产生约10到11 MB。这个问题不是关于如何ftp的,因为我已经有了该解决方案...问题更多的是关于如何跟上数据流的速度(在大多数情况下,只有2mb的上传带宽,除非我前往客户站点)有大烟斗)。我不在乎ftp是否需要花费更长的时间来传输流量,但是我想知道是否有人对如何批处理文件进行移动,以便在ftp处理完成时将删除已传输的文件。然后转到下一批。这就是我的想法:
多线程应用程序,第一个线程运行应用程序,第二个线程是计时器,计时器每\'N \'分钟创建一个文本文件,并在该时间段内创建所有文件。 StreamRead文件,然后将文本文件移动到另一个位置(可能创建一个临时文件夹),然后通过ftp将这些文件发送出去,然后删除文件,文件夹和文本文件...同时,正在写入和临时显示更多文本文件创建的文件夹。这听起来可行吗?我会采纳任何人的建议,只是在寻找最快,最可靠的途径。
请不要要求查看代码,考虑到我们正在使用假设,因此没有理由看到它。
解决方法
实际上,您不知道为什么需要将所有工作保留在单个应用程序中并处理线程复杂性的更多详细信息,因此可以争论将生成文件的部分和通过FTP将文件传输的部分保留在单独的应用程序中。
责任分离。确保每个应用程序仅执行一项工作,并且正确,快速地完成它。
一个Serivce或应用程序(无论是台式机还是网络)生成文件。
另一个服务会监视一个文件夹并将所有传入文件移入临时文件,执行所需的操作,FTP和删除。
看到我不知道您的设置以及文件的内容来源,在一个应用程序中编写它可能是您建议的最佳选择。
基本上可以回答您的问题。是的,听起来确实可行。
如何实现它以及您对实现的满意程度取决于您。
如果您在实施过程中遇到困难,请随时通过一些代码示例来发布新威胁中的任何问题,以了解如何实施特定功能以及遇到的问题。
假设在此之前,您认为能够管理所需实现的任何方法都是完全有效的。
编辑
看到您说您已经完成了生成文件的应用程序,并且您已经有了一个解决方案,FTP意味着使用2个单独的应用程序听起来更合理。
然后,您所需要的只是围绕FTP解决方案包装服务,并度过快乐的日子。
如果它已经在工作,则无需与生成文件的原始应用程序交互。
为什么要冒险破坏它,除非您必须在其中添加fTP功能并且别无选择。
, 我将创建一个服务,并使用FileSystemWatcher,System.Threading.Timer或同时使用这两个方法将传入文件添加到并发集合中(如果FileSystemWatcher的缓冲区超限,可能会丢失文件,因此最好使用计时器来拾取任何文件错过了)。当文件进入时,我会将它们移到一个单独的文件夹中,并使用.NET 4.0任务进行处理。然后,我将在后续步骤中对原始任务进行任何必要的后期处理。您可以具有处理任何故障的继续步骤,以及在成功时发生的不同继续步骤。这些任务中的每一个都会在线程池中启动一个线程,并将为您管理。
这是OnlyOnFaulted延续任务的http://msdn.microsoft.com/zh-cn/library/dd997415.aspx中的示例。您可能有第二个继续任务,该任务仅在成功时运行。
var task1 = Task.Factory.StartNew(() =>
{
throw new MyCustomException(\"Task1 faulted.\");
})
.ContinueWith((t) =>
{
Console.WriteLine(\"I have observed a {0}\",t.Exception.InnerException.GetType().Name);
},TaskContinuationOptions.OnlyOnFaulted);
,我以前的工作也从事类似的工作。我将外部进程转储文件到某个文件夹中。这是我遵循的算法:
在要转储文件的源目录上运行FileSystemWatcher
找到新文件后,按日期升序处理目录中的所有文件。 (在您的情况下是ftp文件)
处理完文件后,我将它们移至“已处理”目录(在这种情况下,您可以删除它们)
注意事项:
我可以拥有多少个开放的ftp连接/处理线程
处理另一个文件时,FileSystemWatcher可以并且将引发事件。如何处理/将其发送到适当的线程
, 您需要在文件的生产者和使用者(FTP主机)之间插入一个队列,以便在生产者太快时能够缓冲文件。这需要某种形式的多线程甚至多个进程。
您提出了一种解决方案,其中队列是文件系统,这很可能,但在许多情况下并不理想。您必须正确锁定才能避免传输半满或空的文件等。如果您决定使用文件系统,根据我的经验,不能将“ 1”用于该目的。使用计时器运行任务说每秒获取新文件要可靠得多。
其他队列技术可能是内存队列(但是您必须考虑如何处理崩溃),私有Microsoft消息队列或SQL Server Broker队列。最佳解决方案在很大程度上取决于您的要求。
FTP并不是真正的事务性的,您可以决定使用非事务性的队列(MSMQ和SQL Server Broker都是事务性的),但是您仍应尝试围绕事务的概念来构建应用程序,在该概念中创建文件并排入队列并交付。如果无法交付,则将其留在队列中,稍后再试交付。如果无法将其排入队列,则生产者应重试将其排入队列等。您不希望文件永远不会交付或两次交付。
从您的问题尚不清楚,您将如何使用FTP,但是我建议您使用开放源代码或商业库来直接从应用程序中使用FTP,而不用掏空to2ѭ。这将使您的应用程序能够明智地保持FTP连接打开,以避免过多的重新连接等。
您还应该考虑如何处理队列过大的情况。一种选择是停止生产者,直到队列大小减小到阈值以下。
,
启动一个每秒触发一次的计时器。
在计时器的经过的事件处理程序中,停止计时器。
获取传入目录中所有文件的列表。
尝试以独占方式打开每个文件。这样可以防止您读取仍在写入的文件。
将每个文件复制到暂存目录,然后从传入目录中将其删除。
移走列表中的所有文件后,请通过FTP将文件发送到暂存目录中。
通过FTP下载文件后,将其从暂存目录中删除。
启动计时器。
计时器的已用处理程序在线程池上为您运行,您应该需要任何更高级的线程管理。由于您的主要限制是FTP带宽,因此在上传文件之前,对其他线程进行任何其他操作都没有什么好处。
这种方法可以在系统崩溃的情况下为您提供保护。在下一个周期中,将提取暂存目录中未发送的文件。传入目录中的文件也是如此。
如果您的FTP接收方可以处理压缩文件,则可以通过压缩暂存目录的内容并将其作为一个文件发送来提高吞吐量。
, 我将使用BlockingCollections设置线程链。
一个生产者线程使用计时器或FileSystemWatcher等读取可用的文件,并将其存储在BlockingCollection中。它还将文件存储在列表中,以确保仅添加一次。
var availableFiles = new BlockingCollection<string>();
var processedFiles = new BlockingCollection<string>();
var newFiles = new HashSet<string>();
...
lock (newFiles) {
foreach (var file in Directory.GetFiles())
if (!newFiles.Contains(file)) {
availableFiles.Add(file);
newFiles.Add(file);
}
}
一个或多个ftp线程发送文件,然后将它们放入已处理的集合中
foreach (var file in availableFiles.GetConsumingEnumerable()) {
SendFileOverFtp(file);
processedFiles.Add(file);
}
一个线程清理处理过的文件
foreach (var file in processedFiles.GetConsumingEnumerable()) {
lock (newFiles) {
File.Delete(file);
newFiles.Remove(file);
}
}
另一种选择是让生产线程也将文件读入内存并删除它们。在这种情况下,您可以跳过最后一个阶段和newFiles集合
,在这种情况下,作为FTP服务器的所有者,我还要求您找到一种尽可能保持登录状态的方法。
登录/注销通常比单个文件传输“更昂贵”(就计算,配置阻止等而言)。