【问题标题】:Asynchronous http handler dealing with .csv files in asp.net在 asp.net 中处理 .csv 文件的异步 http 处理程序
【发布时间】:2011-08-09 18:25:00
【问题描述】:

我开发了一个基于长轮询方法的实时评分应用程序,或者他们也称之为 Comet。我使用了在 IIS 6 上运行的 ASP.NET 4.0(Windows 2003 - 只有两个 CPU,这对池中线程的可用性没有多大帮助)。

数据以.csv 文件的形式出现,这些文件粘贴到 Web 服务器上的源文件夹,然后我使用 Microsoft JET 4.0 OleDb Provider 导入,并使用不同的方法显示,具体取决于应用程序的一部分。

长轮询部分的引擎依赖于IHttpAsyncHandler.,因为它是一个实时评分应用程序,用户访问该网站,得到一个带有当前数据的定期响应,并且在身体负载时通过jquery ajax发送一个新请求到异步 http 处理程序。

然后,此处理程序将请求存储在队列中,并(通常)将线程返回到线程池。一旦发生这种情况,我将创建一个手动重置事件并保持操作,同时创建并发送一个文件系统观察程序对象以查找 csv 数据源文件夹中的更改。

一旦它触发了onChange 事件,我就设置了手动重置事件,并且允许异步操作通过获取新的、刷新的 csv 文件并以新数据响应客户端来恢复。

如果我没有一直出错,那就太好了。一般来说,非常非常一般的方式,应用程序正在运行,但我有一个我无法确定的问题。

也就是说,我不确定问题是否在于访问 csv 文件,因为它们可能被将它们带入服务器的进程锁定(从运动场馆进行的 ftp 传输)。或者可能是我(ab)使用IHttpAsyncHandler,或者只是我没有足够的 CPU 和线程(我很难相信,因为我每天只有大约 3000 个独立访客。我不知道小时数)。

有两个 CPU 的 IIS 6 windows 2003 是否可能无法支持此类应用程序?

这是我不断收到的错误:

事件类型:错误事件来源: ASP.NET 4.0.30319.0 事件类别: 无 事件 ID:1325 日期:20/04/2011 时间:15:33:14 用户:N/A 计算机:xxx 描述:未处理的异常 发生并且过程是 终止。

应用程序 ID:/LM/W3SVC/1/ROOT 进程 ID:5264 异常: System.Data.OleDb.OleDbException 消息:未指定的错误

堆栈跟踪:在 System.Data.OleDb.OleDbConnectionInternal..ctor(OleDbConnectionString constr, OleDbConnection 连接)
在 System.Data.OleDb.OleDbConnectionFactory.CreateConnection(DbConnectionOptions 选项,对象 poolGroupProviderInfo, DbConnectionPool 池,DbConnection 拥有对象)在 System.Data.ProviderBase.DbConnectionFactory.CreateNonPooledConnection(DbConnection 拥有连接, DbConnectionPoolGroup 池组)在 System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection 拥有连接)在 System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection 外部连接,数据库连接工厂 连接工厂)在 System.Data.OleDb.OleDbConnection.Open() 在 Broker.brCSV.readCSV(字符串 文件名)在 SwatchTiming.AsynchOperation.StartAsyncTask(对象 工作项状态)在 System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(对象 州)在 System.Threading.ExecutionContext.runTryCode(对象 用户数据)在 System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode 代码,CleanupCode backoutCode,对象 用户数据)在 System.Threading.ExecutionContext.RunInternal(ExecutionContext 执行上下文,上下文回调 回调,对象状态)在 System.Threading.ExecutionContext.Run(ExecutionContext 执行上下文,上下文回调 回调,对象状态,布尔值 ignoreSyncCtx) 在 System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() 在 System.Threading.ThreadPoolWorkQueue.Dispatch() 在 System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

还有:

事件类型:错误事件源:.NET 运行时 4.0 错误报告事件 类别:无事件 ID:5000 日期: 20/04/2011 时间:15:33:14 用户: N/A计算机:xxx 描述:EventType clr20r3,P1 w3wp.exe,P2 6.0.3790.3959,P3 45d6968e,P4 系统数据,P5 4.0.0.0,P6 4ba1e064, P7 1ea3、P8 87、P9 system.data.oledb.oledb 异常,P10 零。有关详细信息,请参阅帮助 和支持中心 http://go.microsoft.com/fwlink/events.asp.Data:0000: 63 00 6c 00 72 00 32 00
c.l.r.2.0008: 30 00 72 00 33 00 2c 00 0.r.3.,.0010: 20 00 77 00 33 00 77 00 .w.3.w.0018: 70 00 2e 00 65 00 78 00
p...e.x.0020: 65 00 2c 00 20 00 36 00 即,。 .6.0028:2e 00 30 00 2e 00 33 00 ..0...3.0030:37 00 39 00 30 00 2e 00 7.9.0...0038: 33 00 39 00 35 00 39 00 3.9.5.9.0040: 2c 00 20 00 34 00 35 00 ,. .4.5.0048:64 00 36 00 39 00 36 00 d.6.9.6.0050:38 00 65 00 2c 00 20 00 8.e.,. .0058: 73 00 79 00 73 00 74 00 s.y.s.t.0060: 65 00 6d 00 2e 00 64 00 e.m...d.0068:61 00 74 00 61 00 2c 00 a.t.a.,.0070: 20 00 34 00 2e 00 30 00 .4...0.0078: 2e 00 30 00 2e 00 30 00
..0...0.0080:2c 00 20 00 34 00 62 00 ,. .4.b.0088:61 00 31 00 65 00 30 00 a.1.e.0.0090:36 00 34 00 2c 00 20 00 6.4.,. .0098: 31 00 65 00 61 00 33 00 1.e.a.3.00a0: 2c 00 20 00 38 00 37 00 ,. .8.7.00a8: 2c 00 20 00 73 00 79 00 ,. .s.y.00b0: 73 00 74 00 65 00 6d 00 s.t.e.m.00b8: 2e 00 64 00 61 00 74 00 ..d.a.t.00c0: 61 00 2e 00 6f 00 6c 00 a...o.l.00c8: 65 00 64 00 62 00 2e 00 e.d.b ...00d0:6f 00 6c 00 65 00 64 00 o.l.e.d.00d8: 62 00 65 00 78 00 63 00 b.e.x.c.00e0: 65 00 70 00 74 00 69 00 e.p.t.i.00e8: 6f 00 6e 00 20 00 4e 00 在。 .N.00f0: 49 00 4c 00 0d 00 0a 00 I.L.....

还有……

事件类型:错误事件源:.NET 运行时事件类别:无事件 ID: 1026 日期:20/04/2011 时间:15:34:26 用户:N/A 计算机:xxx 描述:应用程序:w3wp.exe 框架版本:v4.0.30319 描述:过程是 由于未处理而终止 异常。异常信息: System.Data.OleDb.OleDbException

堆栈:在 System.Data.ProviderBase.DbConnectionClosed.OpenConnection(System.Data.Common.DbConnection, System.Data.ProviderBase.DbConnectionFactory) 在 System.Data.OleDb.OleDbConnection.Open() 在 Broker.brCSV.readCSV(System.String) 在 [项目命名空间].AsynchOperation.StartAsyncTask(System.Object) 在 System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(System.Object) 在 System.Threading.ExecutionContext.runTryCode(System.Object) 在 System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode, CleanupCode,System.Object)在 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) 在 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object,布尔值)在 System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() 在 System.Threading.ThreadPoolWorkQueue.Dispatch() 在 System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

最后还有几条信息。我曾尝试微调 IIS 6,但似乎没有太大帮助。

那么有人知道到底发生了什么,为什么我的网站每五分钟就崩溃一次?

编辑:这是我在处理程序中的代码,希望对您有所帮助

BeginProcessRequest 如下(除此之外我只是将IsReusable 设置为false):

Public Function BeginProcessRequest( _
    ByVal context As System.Web.HttpContext, _
    ByVal cb As System.AsyncCallback, _
    ByVal extraData As Object) _
    As System.IAsyncResult _
    Implements System.Web.IHttpAsyncHandler.BeginProcessRequest

    Dim asynch As New AsynchOperation(cb, context, extraData)
    asynch.StartAsyncWork() 

    Return asynch
End Function

然后是实现IAsyncResultAsynchOperation 类:

Class AsynchOperation
Implements IAsyncResult
Private _completed As Boolean
Private _state As [Object]
Private _callback As AsyncCallback
Private _context As HttpContext
Private mre As New ManualResetEvent(False)
Dim br As New Broker.brCSV
Dim brLiveGames As New Broker.brLiveGames

ReadOnly Property IsCompleted() As Boolean _
        Implements IAsyncResult.IsCompleted
    Get
        Return _completed
    End Get
End Property

ReadOnly Property AsyncWaitHandle() As WaitHandle _
        Implements IAsyncResult.AsyncWaitHandle
    Get
        Return Nothing
    End Get
End Property

ReadOnly Property AsyncState() As [Object] _
        Implements IAsyncResult.AsyncState
    Get
        Return _state
    End Get
End Property

ReadOnly Property CompletedSynchronously() As Boolean _
        Implements IAsyncResult.CompletedSynchronously
    Get
        Return False
    End Get
End Property

Public Sub New(ByVal callback As AsyncCallback, _
        ByVal context As HttpContext, _
        ByVal state As [Object])
    _callback = callback
    _context = context
    _state = state
    _completed = False
End Sub

Public Sub StartAsyncWork()
    ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf StartAsyncTask), Nothing)
End Sub

Private Sub StartAsyncTask(ByVal workItemState As [Object])


    Dim fsw As New FileSystemWatcher("D:\ClientRoot\Swatchtiming\bv\ReadData\")
    fsw.NotifyFilter = NotifyFilters.LastWrite
    AddHandler fsw.Changed, AddressOf OnChanged
    fsw.EnableRaisingEvents = True
    fsw.IncludeSubdirectories = False

    Dim aTimer As New System.Timers.Timer
    AddHandler aTimer.Elapsed, AddressOf OnTimerChanged
    aTimer.Interval = 60000
    aTimer.Enabled = True
    mre.WaitOne()

    Dim rdr As OleDbDataReader
    Dim i As Integer = 0
    Dim eventName As String = ""
    Dim dsFiles As DataSet = brLiveGames.getFileNameWithEventTitle()
    Dim teamClass As String = "TeamA"
    Dim serveIndicator As String = ""
    Dim serveImage As String = ""
    Dim serveSpeed As String = ""
    Dim fileName As String = ""
    Dim fileNumber As String = ""
    Dim matchID As String = ""
    Dim venueLocation As String = ""
    Dim set1, set2, set3 As String

    For i = 0 To Convert.ToInt16(dsFiles.Tables(0).Rows.Count) - 1
        If eventName <> dsFiles.Tables(0).Rows(i).Item("EventTitle") Then
            eventName = dsFiles.Tables(0).Rows(i).Item("EventTitle")
            _context.Response.Write("<div class='eventTitle'>" & eventName.ToString() & " <span class='bracketLink'>- <a href='Brackets.aspx?Brackets=" & dsFiles.Tables(0).Rows(0).Item("BracketsFile") & "'>View brackets</a></span></div>")
        End If
        rdr = br.readCSV(dsFiles.Tables(0).Rows(i).Item("fileName"))

        _context.Response.Write("<div class='matchView'>")
        While (rdr.Read)
            matchID = rdr.Item("Current_Match_Index")
            If venueLocation <> "" Then
                venueLocation = ""
            Else
                venueLocation = br.getVenueLocation(matchID)
                _context.Response.Write("<div class='matchTitle'>" + venueLocation + "</div>")
            End If
            set1 = IIf(IsDBNull(rdr.Item("SET_1")), "&nbsp;", rdr.Item("SET_1"))
            set2 = IIf(IsDBNull(rdr.Item("SET_2")), "&nbsp;", rdr.Item("SET_2"))
            set3 = IIf(IsDBNull(rdr.Item("SET_3")), "&nbsp;", rdr.Item("SET_3"))
            _context.Response.Write("<div class='" & teamClass & "'>")
            If teamClass <> "TeamB" Then
                teamClass = "TeamB"
            Else
                teamClass = "TeamA"
            End If
            serveIndicator = IIf(IsDBNull(rdr.Item("Service_Indicator")), "", rdr.Item("Service_Indicator"))
            If serveIndicator = "" Then
                serveImage = "<img src='images/css/serveIndicatorNone.png' alt='#' width='14' height='14' />"
            Else
                serveImage = "<img src='images/css/serveIndicator.png' alt='#' width='14' height='14' />"
            End If
            serveSpeed = IIf(IsDBNull(rdr.Item("Serve_Speed")), "&nbsp;", "Serve: " & rdr.Item("Serve_Speed") & " km/h")
            _context.Response.Write("<div class='flag'><img src='images/flags/" & rdr.Item("NOC") & ".jpg' alt='" & rdr.Item("NOC") & "' width='22' height='14' /></div><div class='NOC'>" & rdr.Item("NOC") & "</div><div class='serveIndicator'>" & serveImage & "</div><div class='teamName'>" & rdr.Item("Short_Team_Name") & "</div><div class='set1'>" & set1 & "</div><div class='set2'>" & set2 & "</div><div class='set3'>" & set3 & "</div><div class='serveSpeed'>" & serveSpeed & "</div>")
            _context.Response.Write("</div>")
        End While
        _context.Response.Write("</div>")
        rdr.Close()
    Next
    fsw.Dispose()
    dsFiles.Dispose()
    _context.Response.End()

    _completed = True
    _callback(Me)

End Sub

Private Sub OnChanged(ByVal sender As Object, ByVal e As FileSystemEventArgs)
    mre.Set()
End Sub

Private Sub OnTimerChanged(ByVal sender As Object, ByVal e As ElapsedEventArgs)
    mre.Set()
End Sub

End Class

编辑 #2:Broker.brCSV.readCSV(fileName) 的代码

Public Function readCSV(ByVal fileName As String) As OleDbDataReader
    Dim rdr As OleDbDataReader = Nothing
    Dim folderName = ("FolderName")
    Dim cnString As String = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & folderName & ";Extended Properties='text;HDR=Yes;FMT=Delimited';Mode=3"

    Dim cn As New OleDb.OleDbConnection(cnString)
    Dim cm As New OleDb.OleDbCommand("Select * from " & fileName, cn)
    cm.Connection.Open()
    rdr = cm.ExecuteReader(System.Data.CommandBehavior.CloseConnection)

    Return rdr
End Function

请注意连接字符串的结尾,特别是Mode参数。 msdn 声明这是您指定文件访问权限的方式,但可能是我没有以正确的方式解释说明......即,mode=3 应该将文件访问指定为读/写,但我'不确定它是否有效。

编辑 #3:新的 Broker.brCSV.readCSV() 引发 InvalidOperationException

根据好心助手 Smudge202 的建议,我将 Broker.brCSV.readCSV 方法的代码更改如下:

Public Function readCSV(ByVal fileName As String) As OleDbDataReader
    Dim folderName = ("Folder Name")
    Dim cnString As String = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & folderName & ";Extended Properties='text;HDR=Yes;FMT=Delimited';Mode=3"

    Using cn As New OleDb.OleDbConnection(cnString)
        Using cm As New OleDb.OleDbCommand("Select * from " & fileName, cn)
            cm.Connection.Open()
            Dim rdr As OleDbDataReader = cm.ExecuteReader(System.Data.CommandBehavior.CloseConnection)
            Return rdr
        End Using
    End Using

End Function

但是,在测试时,这段代码导致了以下错误:

事件类型:错误 事件源:ASP.NET 4.0.30319.0 事件类别:无 事件编号:1325 日期:22/04/2011 时间:08:46:33 用户:不适用 电脑:EUW0002184 描述: 发生未处理的异常,进程终止。

应用程序 ID:/LM/W3SVC/1/ROOT

进程 ID:6408

异常:System.InvalidOperationException

消息:阅读器关闭时调用 Read 的尝试无效。

StackTrace:在 System.Data.OleDb.OleDbDataReader.Read() 在 SwatchTiming.AsynchOperation.StartAsyncTask(对象 workItemState) 在 System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(对象状态) 在 System.Threading.ExecutionContext.runTryCode(对象 userData) 在 System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode 代码,CleanupCode backoutCode,对象 userData) 在 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext,ContextCallback 回调,对象状态) 在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext,ContextCallback 回调,对象状态,布尔 ignoreSyncCtx) 在 System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() 在 System.Threading.ThreadPoolWorkQueue.Dispatch() 在 System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

还有:

事件类型:错误 事件源:.NET 运行时 事件类别:无 事件编号:1026 日期:22/04/2011 时间:08:47:53 用户:不适用 电脑:EUW0002184 描述: 应用程序:w3wp.exe 框架版本:v4.0.30319 说明:进程因未处理的异常而终止。 异常信息:System.InvalidOperationException 堆: 在 System.Data.OleDb.OleDbDataReader.Read() 在 SwatchTiming.AsynchOperation.StartAsyncTask(System.Object) 在 System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(System.Object) 在 System.Threading.ExecutionContext.runTryCode(System.Object) 在 System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode、CleanupCode、System.Object) 在 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext,System.Threading.ContextCallback,System.Object) 在 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext,System.Threading.ContextCallback,System.Object,布尔) 在 System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() 在 System.Threading.ThreadPoolWorkQueue.Dispatch() 在 System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

所以,当处理程序尝试使用新的 readCSV 方法时,我会收到这些错误...还有什么建议吗? :) 谢谢 Smudge,也谢谢大家!

【问题讨论】:

  • 您的异常的堆栈跟踪似乎指向 OleDb 提供程序,我相信它是您用来读取传入 CSV 文件的提供程序?您在哪个阶段从 CSV 文件中获取数据?从您的描述来看,似乎有多个线程正在尝试同时使用提供程序,这将导致提供程序内出现 IoException (尽管老实说,我希望在堆栈跟踪中看到这一点)。另外,(请原谅长评论),当您收到信号时,您在 IHttpAsyncHandler 的等待句柄上持有的线程是否还活着? (超时?)
  • @Smudge202 感谢您的回复 Smudge。一旦文件系统观察者注意到源文件夹中的更改,我就会获取数据。我将编辑我的帖子,这样我就可以添加代码,这样你就可以仔细查看它了……再次感谢!
  • 感谢您添加代码。您是否还可以添加“Broker.brCSV.readCSV(String fileName)”的代码,这是您在 StartAsyncTask 中调用的导致问题的函数(请参阅堆栈跟踪)。再加点。循环不需要强制转换为 Int16,Rows.Count 已经是一个整数。此外,您可以完全摆脱计时器并使用重载的 mre.WaitOne(msT​​imeout)。将其与 Dim filesChanged As Boolean = mre.WaitOne(60000) 相结合,以确定文件是否确实发生了变化?
  • @Smudge202,非常感谢您花时间研究这个 :) 我会按照您建议的更换计时器进行更改,我将再次编辑原始帖子以提供Broker.brCSV.readCSV(fileName) 的代码。再次感谢您!
  • @Smudge202,更新:今天我对 IIS 的调整进行了更多尝试,最后似乎一切都正常了。不过,我已经实施了您的建议,并为此感谢您。但是,如果您在询问后发现我发布的代码部分有问题,请告诉我需要修复什么。再次感谢...

标签: asp.net csv iis-6 long-polling ihttpasynchandler


【解决方案1】:

除了我目前添加的一两个 cmets.... =)

正如您在第三次编辑中所注意到的,一旦读取器被释放,您就无法从中获取数据。

这个想法是您创建并打开您的连接。在连接打开时尽早获取数据,然后在获得数据后处理所有使用过的资源。

在您的情况下,您在 readCSV 函数中打开和关闭连接,然后将关闭的阅读器传递回“StartAsyncTask”。你可以做的也许是稍微重构......你可以使用OleDbDataAdapter,而不是使用OleDbDataReader。使用适配器,您可以调用 Fill method 来填充数据集。

一旦数据集被填充,它就在内存中。您可以关闭适配器、关闭连接、处理这两个('using' 语句)并将数据集传递回您的 StartAsync 方法吗?

如果您需要任何示例,请在此处评论。

祝你好运!

编辑:

关于您正在进行的多线程工作的简要说明...

关于 IIS 设置,如果您在多个进程上运行您的网站,请注意在某个阶段您可能有 2 个(或更多)进程正在等待文件系统观察程序。当 FSW 检测到更改时,它将通知您的两个进程;以不可预知的顺序,但很可能连续快速,这反过来会导致两个单独的线程开始读取文件。当两个线程请求 Jet Provider 同时打开相同的文件时,您可能会遇到问题。确保您有足够的异常捕获逻辑来提供帮助。

如果您是这种情况,您甚至可能需要考虑使用mutexes,以允许一次处理一个进程来处理结果,但我很少鼓励这样做。

关于线程异常,请查看this article,我相信它在 IIS7.5/.Net 4 中仍然适用。对工作线程要非常小心,如果未捕获异常,可能会导致网站瘫痪。

【讨论】:

  • 感谢您一直以来的支持。我希望你至少能从中得到一些精神上的满足:) 无论如何,关于 IIS 设置,我没有让应用程序使用两个进程(我想它被称为网络花园)。文件访问的最初问题(以及“另一个进程已锁定文件”的消息)告诉我,如果我只启用多个运行应用程序的进程,同样的事情可能会发生,这对我来说是合乎逻辑的。我已经根据您的建议准备了一个 2.0 版本,今晚将对其进行测试。我会告诉你结果如何......
  • 除此之外,有趣的是,在另一个操作中我确实使用了 oledbdataadapter,但不是在必要的操作中......当然,这部分应用程序从未给我带来任何问题: ) 所以我的昵称,我猜...
  • 哈哈,至少你已经有了使用适配器的所有模板代码。正确的网络花园注释,如果您需要这种方法,我会建议删除 FSW 代码并将其放置在 WCF 服务中,以允许单个 OOP 进程来管理文件。和以前一样,祝你好运。
  • 很高兴我通过这个应用程序学到了很多东西,并且到目前为止我在 wcf 中什么也没做,我可能会尝试这个建议作为一个学习项目......谢谢你的美好祝愿 :)
猜你喜欢
  • 1970-01-01
  • 2011-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-06
  • 1970-01-01
相关资源
最近更新 更多