Асинхронный обработчик http, работающий с файлами .csv в asp.net - PullRequest
1 голос
/ 20 апреля 2011

Я разработал приложение для оценки в реальном времени, основанное на подходе длинного опроса, или Comet, как его еще называют.Я использовал ASP.NET 4.0, работающий на IIS 6 (Windows 2003 - только два ЦП, что не сильно мне помогает с наличием потоков в пуле).

Данные поступают в виде.csv файлы, которые вставляются в исходную папку на веб-сервере, которую я затем импортирую с помощью Microsoft JET 4.0 OleDb Provider и отображаю различными способами, в зависимости от части приложения.

Механизм части длинного опроса опирается на IHttpAsyncHandler. Поскольку это приложение для оперативного скоринга, пользователь посещает веб-сайт, получает регулярный ответ с текущими данными и при загрузке тела отправляет новый запрос через jquery ajax на асинхронный httpобработчик.

Этот обработчик затем сохраняет запрос в очереди и возвращает (обычно) поток обратно в пул потоков.Как только это происходит, я создаю событие ручного сброса и удерживаю операцию, пока создается объект наблюдателя файловой системы и отправляется для поиска изменений в папке источника данных csv.

После запуска события onChange я установил событие ручного сброса, и асинхронная операция может возобновиться с получением новых, обновленных файлов CSV и ответить клиенту свежими данными.

Это было бы хорошо, если бы я не получал ошибки все время.В общем, очень общий способ, приложение работает, но у меня есть проблема, которую я не могу точно определить.

А именно, я не уверен, связана ли проблема с доступом к файлам csv, поскольку они могутбыть заблокированным процессом, который доставляет их на сервер (передача ftp со спортивного объекта).Или это, может быть, мое (ab) использование IHttpAsyncHandler, или, может быть, просто у меня недостаточно процессоров и потоков (в которые мне трудно поверить, поскольку у меня только около 3000 уникальных посетителей каждый день.)не знаю час по часам).

Возможно ли, что IIS 6 windows 2003 с двумя процессорами не может поддерживать такого рода приложения?

Вот ошибки, которые я продолжаю получать:

СобытиеТип: ошибка Источник события: ASP.NET 4.0.30319.0 Категория события: нет КОД события: 1325 Дата: 20/04/2011 Время: 15:33:14 Пользователь: н / Д Компьютер: xxx Описание: Произошло необработанное исключение, ипроцесс был прерван.

ИД приложения: / LM / W3SVC / 1 / ROOT ИД процесса: 5264 Исключение: System.Data.OleDb.OleDbException Сообщение: неуказанная ошибка

StackTrace: at System.Data.OleDb.OleDbConnectionInternal..ctor (OleDbConnectionString constr, соединение OleDbConnection)
в System.Data.OleDb.OleDbConnectionFactory.CreateConnection (параметры DbConnectionOptions, объект poolGroupProviderInfo.ConvisionConnectDirectoryPlayOntConject.Connect_DirectoryCon роли_объектов.(DbConnection owningConnection, DbConnectionPoolGroup poolGroup) в System.Data.ProviderBase.DbConnectionFactory.GetConnection (DbConnection owningConnection) в System.Data.ProviderBase.DbConnectionClosed.OpenConnection (DbConnection outerConnection, DbConnectionFactory connectionFactory) в System.Data.OleDb.OleDbConnectionNervNerVision AtVision (at)..AsynchOperation.StartAsyncTask (Object workItemState) в System.Threading.QueueUserWorkItemCallback.WaitCallback_Context (состояние объекта) в System.Threading.ExecutionContext.runTryCode (объект userData) в System.Runtime.CupEventUneCupCountChountCountCountChountWireChantsWireSecantsServicesSec.userData) в System.Threading.ExecutionContext.RunInternal (ExecutionContext executeContext, обратный вызов ContextCallback, состояние объекта) в System.Threading.ExecutionContext.Run (ExecutionContext executeContext, обратный вызов ContextCallback, состояние объекта, логическое значениеignoreSyncCtx) в System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem () в System.Threading.ThreadPoolWorkQueue.Dispatch () в System.Threading._ThreadPoolWaitCallback.PerformWaitCallback ()

А также:

Тип события: ошибка Источник события: .NET Событие 4: сообщение об ошибке Категория: нет Код события: 5000 Дата: 20.04.2011 Время: 15:33:14 Пользователь: N / AComputer: xxx Описание: EventType clr20r3, P1 w3wp.exe, P2 6.0.3790.3959, P3 45d6968e, P4 system.data, P5 4.0.0.0, P6 4ba1e064, P7 1ea3, P8 87, P9 system.data.oledb.oledbexception, P10 NIL. Для получения дополнительной информации см. Справку и Центр поддержки на 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 д.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 эм ... д.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 ср.ед.м.00b8: 2е 00 64 00 61 00 74 00 ..d.a.t.00c0: 61 00 2e 00 6f 00 6c 00 ... 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 Категория события времени выполнения: нет Идентификатор события: 1026 Дата: 20.04.2011 Время: 15:34:26 Пользователь: N / A Компьютер: xxx Описание: Приложение: w3wp.exe Версия Framework: 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) в [ProjectNamespace] .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, Boolean) в 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

, а затем класс AsynchOperation, который реализует IAsyncResult:

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 должен указывать доступ к файлу как чтение / запись, но яя не уверен, что это работает.

EDIT # 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 Описание: Произошло необработанное исключение, и процесс был прерван.

ИД приложения: / LM / W3SVC / 1 / ROOT

ИД процесса: 6408

Исключение: System.InvalidOperationException

Сообщение: неверная попыткаЗвоните Читайте, когда читаетеr закрыто.

StackTrace: в System.Data.OleDb.OleDbDataReader.Read () в SwatchTiming.AsynchOperation.StartAsyncTask (Object workItemState) в System.Threading.QueueUserWorkItemCallback.WaitxtallingConConnect).ExecutionContext..ExecutionContext.Run (ExecutionContext executeContext, обратный вызов ContextCallback, состояние объекта, логическое значение ignoreSyncCtx) в System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem (Wread.Work.Web)PerformWaitCallback ()

, а также:

Тип события: ошибка Источник события: Eve Runtime Event Категория: Нет КОД события: 1026 Дата: 22/04/2011 Время: 08:47:53 Пользователь: н / Д Компьютер: EUW0002184 Описание: Приложение: w3wp.exe Framework Версия: v4.0.30319 Описание: Процесс завершен из-занеобработанное исключение.Информация об исключении: System.InvalidOperationException Стек: в System.Data.OleDb.OleDbDataReader.Read () в SwatchTiming.AsynchOperation.StartAsyncTask (System.Object) в System.Threading.QueueUserWorkItemCallback.WaitCallback.WationOntject_ject.runTryCode (System.Object) в System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup (TryCode, CleanupCode, System.Object) в System.Threading.ExecutionContext.RunInternal (System.Threading.ExContionCject).) в System.Threading.ExecutionContext.Run (System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) в System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExueWorkItemIThueWorkItemIThue.Dispatch () в System.Threading._ThreadPoolWaitCallback.PerformWaitCallback ()

Итак, когда обработчик пытается использовать новый метод readCSV, я получаю эти ошибкиrs ... Есть еще предложения?:) Спасибо, Smudge, и спасибо всем остальным!

1 Ответ

1 голос
/ 22 апреля 2011

В дополнение к одному или двум комментариям, которые я добавил до сих пор .... =)

Как вы заметили в своем третьем редакторе, после того, как читатель был удален, вы не можете получить данные из него.

Идея состоит в том, что вы создаете и открываете свое соединение. Получите данные как можно раньше, когда соединение открыто, а затем утилизируйте все ресурсы, которые использовались, когда у вас есть данные.

В вашем случае вы открываете и закрываете соединение в функции readCSV, затем передаете закрытое считывающее устройство обратно в «StartAsyncTask». Возможно, вы могли бы немного реорганизовать ... Вместо использования OleDbDataReader вы можете использовать OleDbDataAdapter . Используя адаптер, вы можете вызвать метод Fill для заполнения набора данных.

Как только набор данных заполнен, он находится в памяти. Вы можете закрыть адаптер, закрыть соединение, утилизировать оба (операторы using) и передать набор данных обратно в метод StartAsync?

Прокомментируйте здесь, если вам нужны какие-либо примеры этого.

Удачи!

EDIT:

Краткое примечание о работе с многопоточностью, которую вы выполняете ...

Что касается настроек IIS, если вы используете свой веб-сайт на нескольких процессах, имейте в виду, что на каком-то этапе у вас, вероятно, будет 2 (или более) процесса, ожидающих наблюдателя файловой системы. Когда FSW обнаружит изменение, он уведомит оба ваших процесса; в непредсказуемом порядке, но, вероятно, в быстрой последовательности, что, в свою очередь, приведет к тому, что два отдельных потока начнут читать файлы. Вы можете столкнуться с проблемами на этом этапе, когда два потока запрашивают у Jet Jet Provider одновременное открытие одного и того же файла (ов). Убедитесь, что у вас достаточно логики для ловли исключений, чтобы помочь.

Возможно, вам даже придется подумать об использовании мьютексов , если это так, чтобы позволить одному процессу за раз обрабатывать результаты, но я редко бы хотел поощрять их.

В примечании об исключениях потоков взгляните на эту статью , которая, по моему мнению, остается верной в IIS7.5 / .Net 4. Будьте очень осторожны с вашими рабочими потоками, исключения могут устранить сайт, если не пойман.

...