找回密码
 立即注册
首页 业界区 业界 使用IronPython检测ASP.NET程序状况

使用IronPython检测ASP.NET程序状况

诈知 2025-5-30 01:18:21
在上一篇文章中,我们在一个请求中执行了IronPython代码,通过这个方法我们可以轻松地的检查系统运行的状态,或对系统进行一些简单修改。但是这种做法只能检查系统在当前时刻的状态,在很多情况下,我们需要对系统的请求进行一段时间的采样。对于简单的数据(例如每秒执行的请求数量,请求时间),我们可以通过查看Performance Monitor中相关的计数器来获得一些概要的数据。但是,如果我们需要获取一些系统的详细状态,甚至是需要根据需要进行动态改变的自定义需求,则势必要深入到系统内部进行数据采集。因此,我们可能需要让代码执行“一段时间”,并将直接结果进行汇总输出。
让代码执行一段时间不难,简单地使用Thread.Sleep便可,也不会造成什么性能或吞吐量上的损失。关键就在于,在代码停留的这“一段时间”内,我们使用什么样的做法来采集数据。这时候,老赵脑海中立即浮现出的便是HttpModule般监听请求管道(Pipeline)中的各式事件。于是立马写出以下的代码(在前文的示例基础上进行修改):
  1. protected void btnExecute_Click(object sender, EventArgs e)
  2. {
  3.     ScriptEngine engine = Python.CreateEngine();
  4.     var scope = engine.CreateScope();
  5.     var script = engine.CreateScriptSourceFromString(
  6.         this.txtCode.Text, SourceCodeKind.Statements);
  7.     script.Execute(scope);
  8.     TextWriter writer = new StringWriter();
  9.     scope.SetVariable("logger", writer);
  10.     Action<HttpContext> traceRequest;
  11.     if (scope.TryGetVariable>("traceRequest", out traceRequest))
  12.     {
  13.         Action endTraceRequests;
  14.         scope.TryGetVariable("endTraceRequests", out endTraceRequests);
  15.         int waitTime;
  16.         if (!scope.TryGetVariable<int>("waitTime", out waitTime))
  17.         {
  18.             waitTime = 10000;
  19.         }
  20.         this.TraceRequests(traceRequest, waitTime, endTraceRequests);
  21.     }
  22.     this.txtOutput.Text = writer.ToString();
  23. }
  24. private void TraceRequests(Action<HttpContext> traceRequest, int waitTime, Action endTraceRequests)
  25. {
  26.     EventHandler handler = (sender, e) =>
  27.     {
  28.         try
  29.         {
  30.             traceRequest((sender as HttpApplication).Context);
  31.         }
  32.         catch { }
  33.     };
  34.     this.Context.ApplicationInstance.BeginRequest += handler;
  35.     Thread.Sleep(waitTime);
  36.     this.Context.ApplicationInstance.BeginRequest -= handler;
  37.     if (endTraceRequests != null) endTraceRequests();
  38. }
复制代码
再编译了IronPython代码之后,我们会设法获取其中的traceRequest和endTraceRequests函数,前者用于“记录每个请求”,而后者用于采样最后的“聚合”。此外,还会设法从代码中获取等待时间waitTime。然后,使用TraceRequest方法开始对当前请求进行采样。具体做法为监听当前Application的BeginRequest事件,并在每次获得请求时调用traceRequest委托进行“记录”。在等待时间过后,自然将委托从BeginRequest事件中剥离。最后,再通过endTraceRequests函数进行聚合输出。
代码逻辑很清晰,但可惜的是,上面这段代码不能生效。具体原因不明,可能是ASP.NET对这方面进行了限制,使得我们无法在HttpModule之外为请求管道动态添加事件处理函数(存疑,求证)。对此我们只能进行让步。不过,既然ASP.NET允许HttpModule监听管道事件,那么我们不如事先准备一个HttpModule监听各种事件,并且在合适的时候把这一事件转发给IronPython函数。我们这里还是以BeginRequest事件为例:
  1. public class IronPythonTraceModule : IHttpModule
  2. {
  3.     private class TraceRequestEventArgs : EventArgs
  4.     {
  5.         public TraceRequestEventArgs(HttpContext context)
  6.         {
  7.             this.HttpContext = context;
  8.         }
  9.         public HttpContext HttpContext { get; private set; }
  10.     }
  11.     public void Dispose() { }
  12.     public void Init(HttpApplication context)
  13.     {
  14.         context.BeginRequest += new EventHandler(OnBeginRequest);
  15.     }
  16.     static void OnBeginRequest(object sender, EventArgs e)
  17.     {
  18.         var traceRequest = IronPythonTraceModule.TraceRequest;
  19.         if (traceRequest != null)
  20.         {
  21.             var context = (sender as HttpApplication).Context;
  22.             traceRequest(null, new TraceRequestEventArgs(context));
  23.         }
  24.     }
  25.     private static event EventHandler<TraceRequestEventArgs> TraceRequest;
  26.     public static void TraceRequests(
  27.         Action<HttpContext> traceRequest,
  28.         Action endTraceRequests,
  29.         int milliseconds)
  30.     {
  31.         EventHandler<TraceRequestEventArgs> handler = (sender, e) =>
  32.         {
  33.             try
  34.             {
  35.                 traceRequest(e.HttpContext);
  36.             }
  37.             catch { }
  38.         };
  39.         IronPythonTraceModule.TraceRequest += handler;
  40.         Thread.Sleep(milliseconds);
  41.         IronPythonTraceModule.TraceRequest -= handler;
  42.         if (endTraceRequests != null) endTraceRequests();
  43.     }
  44. }
复制代码
IronPythonTraceModule需要放入应用程序中,它时刻对请求管道的BeginRequest进行监听,只是在合适的时候才会发起TraceRequest静态事件(显然这并不会对系统性能造成什么影响)。Module包函一个静态的TraceRequests方法,这便是给外部调用的接口。可以发现这段代码和之前的TraceRequests方法非常接近,唯一不同的只是动态添加/删除处理函数的事件是IronPythonTraceModule.TraceRequest,而不是HttpApplication.BeginRequest。于是,原来的代码也需要做一定修改:
  1. protected void btnExecute_Click(object sender, EventArgs e)
  2. {
  3.     ...
  4.     Action<HttpContext> traceRequest;
  5.     if (scope.TryGetVariable>("traceRequest", out traceRequest))
  6.     {
  7.         Action endTraceRequests;
  8.         scope.TryGetVariable("endTraceRequests", out endTraceRequests);
  9.         int waitTime;
  10.         if (!scope.TryGetVariable<int>("waitTime", out waitTime))
  11.         {
  12.             waitTime = 10000;
  13.         }
  14.         // 以下代码有所修改
  15.         IronPythonTraceModule.TraceRequests(traceRequest, endTraceRequests, waitTime);
  16.     }
  17.     this.txtOutput.Text = writer.ToString();
  18. }
复制代码
现在我们进行一番测试,简单地检测一下5秒钟内收到 了多少请求:
1.jpeg
使用自己的Module进行处理还有其他一些好处,比如可以提供更好的控制。事实上,目前文章里的解决方案有一些缺陷,并且肯定无法完全满足真实需求。不过我们完全可以对目前的做法进行改进,例如:

  • 保证代码执行的线程安全,包括事件添加时和traceRequest函数执行时。
  • 提供“采样特性”,而不是对每个请求都使用traceRequest函数进行处理。
  • 将Logger放入公用的空间,并在程序代码中植入检查功能,这样便可以得知当前系统中每个功能的执行时间(这就是Profiler的功能,不是吗?)。
不过,除了应对前一篇文章中所提到的负载均衡环境下的问题之外,这个解决方案还有另一个较为重要的情况需要特殊对待。如果使用目前的做法,每次采样都是通过一个请求进行的,所以它并不会在在请求队列阻塞时立即执行,但是采样的常见场景便是在队列阻塞时间检查状况,这显然形成了一个矛盾。不过要解决这个问题并非难事,只要采样不要通过IIS即可。例如您使用普通Socket,或“偷偷懒”使用WCF的TcpBinding进行采样便可——只要和被检查的应用程序在同一进程(即w3wp.exe,还有人用IIS5 吗?)中,便可以使用任何方式进行通信。
改变、探索的过程,其实都是在追求一种编程的美感。老赵喜欢“不拘一格”,只要是有价值的都会去设法尝试一番,探索的过程会遇到大量问题,解决问题后又能带来成就感以及新的感受。因为美感和成就感,所以产生乐趣,因为乐趣才有源源不断地动力。因为有这样的切身体会,所以在这里也建议大家,不妨放开思路,海纳百川。我们可以获得和想到的东西,远比预料中来的丰富。
 
相关文章:使用IronPython检测ASP.NET程序状况(上)

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册