正在登录另一个进程



我有一个IIS服务器。该服务器获取web请求,并为多个客户端进行一些计算。在这些计算中,有日志条目。这些日志条目使用一个标志,以便可以关闭它们。完全关闭日志条目可以让我获得非常高的性能提升。或者换句话说,日志记录需要很多时间,但它是必要的,所以关闭它不是一种选择。

我的想法是,我想将日志条目推送到另一个进程,这样就可以让服务器的计算按需快速运行,并让另一个过程编写日志,从而在需要较少资源时为它提供一些编写日志的优势。

首先,关于这个想法本身的几个问题。我的想法如下:

  • 将日志字符串从服务器发送到另一个进程
  • 将日志字符串保存到日志进程中的队列中
  • 让日志进程定期在另一个线程中检查队列中的条目,如果有要写的内容,则写入文件,然后休眠
  • 通过互斥锁限制对队列的访问以避免冲突

因此,首先的问题是,这甚至能提高客户体验的整体性能吗?还是只会造成开销,使问题恶化?

现在,让我们更具体一些

我使用c#IpcChannel类来实现这个系统的第一个实现。根据微软网站上的例子。我还没有衡量实施是否更快,因为仍有一些悬而未决的问题。

我在IIS服务器中实现了IpcClient。我在另一个进程中启动IpcServer。然后我启动一个IIS客户端。因此,这里的三个过程是:

  • IpcServer
  • IIS服务器,实现IpcClient
  • IIS客户端应用程序

据我所知,通信如下:

  • 用户在IIS客户端上调用某些函数
  • IIS客户端向IIS服务器发送Web请求
  • IIS服务器运行计算和日志,调用IpcClient方法
  • IpcClient方法被发送到IpcServer进程
  • IpcServer进程将日志stiring写入文件

现在是棘手的部分

我在IpcChannel中构建了一个调试日志,这意味着它每次检查队列中要写入实际日志的字符串时都会在日志中写入一行文本。停止IpcServer进程后,此操作将继续。我以为这会中断循环,但不知何故,所有这些都一直持续到我关闭IIS服务器进程。这让我很困惑。如果没有IpcServer,假设日志实际上是由IpcServer项目完成的,我会假设调试日志会停止,但它不会。你知道为什么会这样吗?

根据上面提供的微软示例,这是我的RemoteObject循环:

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
namespace RemoteProcedureCall
{
public class RemoteObject : MarshalByRefObject
{
const int MessageBuffer = 10;
Queue<string> Messages = new Queue<string>();
const string FilePath = "Test_Log.txt";
const string DebugLogFilePath = "DebugLog.txt";
Mutex QueueMutex = new Mutex(false, nameof(QueueMutex));
private int callCount = 0;
Thread WriterTask;
public int GetCount()
{
Console.WriteLine("GetCount has been called.");
callCount++;
return (callCount);
}
#region Shortcuts
/// <summary>
/// Calls <see cref="File.AppendAllText(string, string)"/> using <see cref="FilePath"/>.
/// </summary>
/// <param name="text"></param>
private void WriteLine(string text) => File.AppendAllText(FilePath, $"{text}{Environment.NewLine}");
/// <summary>
/// <see cref="WriteLine(string)"/> debug version
/// </summary>
/// <param name="text"></param>
private void DebugConsole(string text) => File.AppendAllText(DebugLogFilePath, $"{text}{Environment.NewLine}");
private void DequeueAndWriteMessage()
{
QueueMutex.WaitOne();
if (Messages.Count != 0)
WriteLine(Messages.Dequeue());
QueueMutex.ReleaseMutex();
}
private void Flush()
{
DebugConsole($"------------------------------!!Flushing!!------------------------------");
while (Messages.Count != 0)
DequeueAndWriteMessage();
}
#endregion Shortcuts
private void PrintMessagesTask()
{
DebugConsole($"Starting PrintMessagetast");
try {
while (true) {
DebugConsole($"Dequeue and write message in secondary thread");
DequeueAndWriteMessage();
if (Messages.Count < MessageBuffer) {
Thread.Sleep(5000);
DebugConsole("Thread.Sleep(5000);");
}
else {
Thread.Sleep(10);
DebugConsole("Thread.Sleep(10);");
}
}
}
finally {
Flush();
}
}
/// <summary>
/// Adds a Message to the <see cref="Messages"/> for them to later be posted to log file.
/// </summary>
/// <param name="text"></param>
public void AddMessage(string text)
{
DebugConsole($"Enqueue Message");
QueueMutex.WaitOne();
Messages.Enqueue($"Enqueueing Message Nr. {GetCount()}:");
Messages.Enqueue(text);
QueueMutex.ReleaseMutex();
}
public void StartWriterThread()
{
DebugConsole($"Starting second thread");
ThreadStart threadStart = new ThreadStart(PrintMessagesTask);
WriterTask = new Thread(threadStart);
WriterTask.Start();
DebugConsole($"Started second thread");
}
public void AbortWriterThread()
{
WriterTask.Abort();
}
}
}

IIS服务器调用"StartWriterThread()",然后循环一些"AddMessage"调用。

首先,我建议详细追踪哪些代码部分导致了延迟。(例如,通过System.Diagnostics.Stopwatch,代码中固有)

增加复杂性以补偿"假定问题"不是一个好主意。

在代码中找到优势区域后,优化就容易多了。

优化思路:

  • 避免使用复杂的c#"托管"函数来实现高性能函数
  • 有几种类型/方法可以访问/生成(日志)文件。根据具体情况选择合适的是至关重要的
  • 访问(日志)文件更有效,而不是生成多线程或使用堆栈
  • 预先计算/估计日志记录所需的最大频率
  • 在代码中查找所有阻塞/非异步部分并测量延迟
  • 文件访问在物理上受到限制。预先计算你的身体瓶颈。(例如,用于SSD等)
  • 让windows决定何时进行真正的物理访问。永远不要尝试刷新、清除缓存等
  • 使用超时处理来检测+避免滞后/交错效应
  • 与其实现允许"多个线程写入单个文件"的复杂解决方案,不如选择生成日志文件/文件夹层次结构,并在查看/处理时仅筛选/组合=更干净、更高性能的概念
  • 提示:如果您在代码中的不同位置使用日志文件访问,您(可能)已经拥有"多线程访问"(而不知道)
  • 与其使用多个应用程序,不如将它们合并为一个应用程序。所以没有必要使用任何类型的IPC

我在服务器端.NET应用程序中使用所有这些来处理多客户端设置的websockets(浏览器)+mqtt(物联网)。

websocket压力测试正在生成>>200Hz,用于与数据文件生成/日志记录进行双向通信。(i5服务器cpu,深渊+.NET应用程序)

BTW:

如果一般的应用程序/概念"太大而无法更改",您需要接受由此产生的(糟糕的)性能

软+硬件永远不会通过添加一些东西(开销)来弥补的薄弱概念而变得更好

使用ConcurrentBag对象并调用Add()方法而不是Console.WriteLine()来生成线程,然后在事件到达该线程时将其记录到一个文件中,写入文本并从并发包中删除会快得多。

如果需要实时监控,请使用FileSystemWatcher类创建一个单独的程序,该程序将显示对文件的实时更改。

相关内容

最新更新