【发布时间】:2019-07-28 21:22:21
【问题描述】:
我正在创建一个 c# 控制台应用程序,它将遍历给定文件夹(和子文件夹)以加密所有文件(二进制或文本)并更新 sqlserver 数据库中的IsEncrypted 标志。客户端上将有数百万个文件需要加密。我们计划在每天的非工作时间(例如从每晚 10 点开始运行 8 小时)按计划任务运行应用程序。
我有两个选择:
选项 1
使用Parallel.ForEach 处理文件。
public void Process(ProcessorOptions options, ProcessorParameter parameter)
{
int counter = 0;
CancellationTokenSource cts = new CancellationTokenSource();
ParallelOptions parallelOptions = new ParallelOptions();
parallelOptions.CancellationToken = cts.Token;
try
{
parallelOptions.MaxDegreeOfParallelism = Environment.ProcessorCount;
if (options.NumberOfThreads > 0)
{
parallelOptions.MaxDegreeOfParallelism = options.NumberOfThreads;
}
if (options.StopTime != 0)
{
Timer timer = new Timer(callback => { cts.Cancel(); }, null, options.StopTime * 60000, Timeout.Infinite);
}
List<string> storagePaths = parameter.StoragePaths;
Log("Process Started...");
foreach (var path in storagePaths)
{
Parallel.ForEach(TraverseDirectory(path, f => f.Extension != ".enc"), parallelOptions, file =>
{
if (file.Name.IndexOf("SRSCreate.dir") < 0)
{
ProcessFile(parameter, file.FullName, file.Directory.Name, file.Name);
counter++;
}
});
}
Log(string.Format("Process Files Ended... Total File Count = {0}", counter));
}
catch (OperationCanceledException ex)
{
log.WriteWarningEntry(string.Format("Reached stop time = {0} min, explicit cancellation triggered. Total number of files processed = {1}", options.StopTime, counter.ToString()), ex);
}
catch (Exception ex)
{
log.WriteErrorEntry(ex);
}
finally
{
cts.Dispose();
}
}
我做了基准测试,发现处理 2000 个文件几乎需要 7-8 分钟。我可以做些什么来提高性能吗?此外,确定下一次运行(第二天)从哪里开始的最佳方法是什么?
选项 2
使用RabbitMQ 的现有设计来推送带有文件路径的消息,以处理文件以实现可扩展性和维护列表。
public void Process(ProcessorOptions options, ProcessorParameter parameter)
{
try
{
using (IConnection connection = parameter.ConnectionFactory.CreateConnection())
{
using (IModel channel = connection.CreateModel())
{
var queueName = parameter.TopicSubscription.DeriveQueueName();
var queueDeclareResponse = channel.QueueDeclare(queueName, true, false, false, null);
EventingBasicConsumer consumer = new EventingBasicConsumer(channel);
consumer.Received += (o, e) =>
{
string messageContent = Encoding.UTF8.GetString(e.Body);
FileData message = JsonConvert.DeserializeObject(messageContent, typeof(FileData)) as FileData;
ProcessFile(parameter, message.EntityId, message.Attributes["Id"], message.Attributes["filename"]);
};
string consumerTag = channel.BasicConsume(queueName, true, consumer);
}
}
}
catch (Exception ex)
{
log.WriteErrorEntry(ex);
}
finally
{
Trace.Exit(method);
}
}
在配置StopTime 之后,我仍然需要弄清楚如何停止阅读消息。性能不是很好,我看到处理 2000 个文件大约需要 25 - 30 分钟。我们认为我们可以在一台机器或多台机器上运行应用程序的多个副本来处理单个队列以进行扩展。您认为,我可以更改此代码以使其更优化吗?
最后一个问题:您认为是否还有其他选项比上述选项更高效和可扩展?
注意:
1) 方法ProcessFile 调用加密逻辑和更新数据库的逻辑。
2)我们遍历文件夹而不是从数据库开始,因为文件系统中可能存在数据库中尚不存在的文件。
【问题讨论】:
-
你很可能会达到盒子的 io 限制。首先检查实际的限制组件。否则这很难回答。哦,一个问题:如果您的数据库不同步怎么办?这会是个大问题吗?
-
这个问题有点问题,因为你在一个问题中问了很多事情......这一切都归结为瓶颈是什么,磁盘是什么(ssd?你在写回相同的驱动器?还是不同的驱动器?)。使用单线程读取器、并行加密器、单线程写入器可能会更好
-
@Stefan,这些文件存储在服务器上,由客户端应用程序查看。客户端应用程序依靠数据库根据 IsEncrypted 标志来识别文件是否需要解密才能查看。所以,数据库必须同步。
-
@KeithNicholas,很抱歉在一篇文章中问了这么多问题。我只想把所有东西都放在一个地方,我相信这个问题也会对其他人有所帮助。我同意有时你可以用代码做很多事情,然后你必须考虑硬件。这就是为什么我们考虑 RabbitMQ 的可扩展性的原因,如果客户端有硬件,他们可以投入更多的计算机、磁盘 (SSD) 等来加倍处理。单线程读写器不会阻塞系统?
-
好吧,您需要最大化磁盘吞吐量,从多个线程访问磁盘可能会破坏磁盘的任何读/写缓存。我会做一堆实验来看看。我真的不明白你认为rabbitmq会为你做什么。但是您根本没有真正描述过部署架构。从您的问题看来,磁盘-> 内存-> 加密-> 磁盘....如果加密比磁盘内存更昂贵并再次返回磁盘,那么使用分布式系统进行加密可能会有所帮助。但我可能会为此使用 Akka 之类的东西。
标签: c# rabbitmq filesystems