Буфер IIS запрашивает содержимое перед действием контроллера - PullRequest
0 голосов
/ 29 мая 2019

Я пытаюсь настроить «внешний» веб-API, который сможет получать в больших сообщениях http (+ 1 ГБ) и перенаправлять поток на другой «внутренний» веб-API, который записывает содержимое запроса в файл.У меня есть модель моей реализации, основанная на примерах использования пользовательского WebHostBufferPolicySelector и использования метода UseBufferedInputStream в методе контроллера.Он работает, как и ожидалось, при использовании IIS Express, при этом не требуется значительного увеличения объема памяти, но как только мой код развернут в IIS, объем памяти существенно увеличивается и приводит к OOM.

Я поместил операторы трассировки в методы моего контроллера и в мой WebHostBufferPolicySelector.UseBufferedInputStream и проверил, что UseBufferedInputStream всегда возвращает false, а методы моего контроллера получают удар.Единственное отличие, которое я заметил, заключается в том, что при отладке метки времени между UseBufferedInputStream и моим методом контроллера очень близки.Хотя временные метки размещены на IIS, они очень далеко друг от друга, что позволяет предположить, что между вызовом UseBufferedInputStream и вызовом моего метода контроллера что-то промежуточное полностью буферизует запрос.

Я ищу несколько советов, чтобы выяснить, что вызывает буферизацию запроса и как его не буферизовать и не использовать потоковую передачу полностью.

Клиент приходит навнешний веб-интерфейс API с типом контента application / octet-stream с Transfer Encoding of Chucked.

Используется для построения реализации

https://forums.asp.net/t/2018289.aspx?Web+API2+WebHostBufferPolicySelector+UseBufferedInputStream+override

https://www.strathweb.com/2012/09/dealing-with-large-files-in-asp-net-web-api/

Метод Proxy Web Api Controller

        <HttpPost, Route("postLargeFile")>
        Protected Overridable Async Function PostLargeFile() As Threading.Tasks.Task(Of IHttpActionResult)
            Configuration.Services.GetTraceWriter.Info(Request, $"{Me.GetType.Namespace}.{NameOf(MyProxyController)}", "Started {0}", NameOf(MyProxyController.PostLargeFile))

            Dim internalHttpClient As HttpClient
            Dim fowardingContent As StreamContent = Nothing
            Dim fowardingMessage As HttpRequestMessage = Nothing
            Dim fowardingResponse As HttpResponseMessage = Nothing
            Dim externalResponse As HttpResponseMessage = Nothing

            Try
                internalHttpClient = New HttpClient()
                internalHttpClient.BaseAddress = "https://myinternalService.com"

                fowardingMessage = New HttpRequestMessage(HttpMethod.Post, "https://myinternalService.com/saveLargeFile")
                fowardingContent = New StreamContent(HttpContext.Current.Request.GetBufferlessInputStream(True))
                CopyContentHeaders(Request.Content, fowardingContent)

                fowardingMessage.Headers.TransferEncodingChunked = True
                fowardingMessage.Content = fowardingContent

                fowardingResponse = Await internalHttpClient.SendAsync(fowardingMessage, HttpCompletionOption.ResponseHeadersRead)

                externalResponse = New HttpResponseMessage(fowardingResponse.StatusCode)
                externalResponse.Content = New StreamContent(Await fowardingResponse.Content.ReadAsStreamAsync)
                CopyContentHeaders(fowardingResponse.Content, externalResponse.Content)

                Return New Results.ResponseMessageResult(externalResponse)

            Catch ex As Exception
                Return InternalServerError(ex)
            Finally
                Configuration.Services.GetTraceWriter.Info(Request, $"{Me.GetType.Namespace}.{NameOf(MyProxyController)}", "Finished {0}", NameOf(MyProxyController.PostLargeFile))
            End Try
        End Function

Метод внутреннего веб-контроллера Api

        <HttpPost, Route("saveLargeFile")>
        Protected Overridable Async Function SaveLargeFile() As Threading.Tasks.Task(Of IHttpActionResult)
            Configuration.Services.GetTraceWriter.Info(Request, $"{Me.GetType.Namespace}.{NameOf(MyInternalController)}", "Started {0}", NameOf(MyInternalController.PostLargeFile))

            Dim bufferlessStream As IO.Stream
            Dim fowardingContent As StreamContent = Nothing

            Try

                bufferlessStream = HttpContext.Current.Request.GetBufferlessInputStream()
                Using fileStream As IO.FileStream = IO.File.Create("MyFile.txt")
                    bufferlessStream.CopyTo(fileStream)
                    fileStream.Flush()
                End Using

                Return New Results.StatusCodeResult(Net.HttpStatusCode.Created, Me)

            Catch ex As Exception
                Return InternalServerError(ex)
            Finally
                Configuration.Services.GetTraceWriter.Info(Request, $"{Me.GetType.Namespace}.{NameOf(MyInternalController)}", "Finished {0}", NameOf(MyInternalController.PostLargeFile))
            End Try
        End Function

Конфигурация выбора политики

Public Class MyBufferPolicySelector
    Inherits Http.WebHost.WebHostBufferPolicySelector

    Public Property Tracer As ITraceWriter

    Public Overrides Function UseBufferedInputStream(hostContext As Object) As Boolean
        UseBufferedInputStream = False
        Tracer?.Info(Nothing, $"{Me.GetType.Namespace}.{NameOf(MyBufferPolicySelector)}", "{0} UseBufferedInputStream={1}", HttpContext.Current?.Request?.Url?.AbsoluteUri, UseBufferedInputStream)
        Return UseBufferedInputStream
    End Function
End Class

WebApiConfig для внутреннего и внешнегоВеб-API

Public Module WebApiConfig

    Public Sub Register(ByVal config As HttpConfiguration)
        Dim tracer As SystemDiagnosticsTraceWriter

        ' Web API configuration and services

        ' Web API routes
        config.MapHttpAttributeRoutes()

        tracer = config.EnableSystemDiagnosticsTracing
        tracer.IsVerbose = True
        tracer.MinimumLevel = Tracing.TraceLevel.Debug

        GlobalConfiguration.Configuration.Services.Replace(GetType(IHostBufferPolicySelector), New MyBufferPolicySelector() With {.Tracer = tracer})

    End Sub

End Module

1 Ответ

0 голосов
/ 30 мая 2019

Мне удалось выяснить, что стало причиной буферизации в IIS.Приведенная ниже ссылка приводит меня к параметру uploadReadAheadSize в IIS.Это было превышено.Таким образом, это приведет к тому, что IIS полностью прочитает / буфера в запросе, прежде чем передать его в модуль, где существует конвейер веб-API (контроллеры веб-API).Установив значение по умолчанию, я увидел, что мои большие записи в файлах не буферизуются, объем памяти пула приложений остается низким, исключений не хватает, а также значительное повышение производительности.Отлично!

Но теперь у меня та же проблема, что описана в ссылке ниже.Когда требуется SSL, установите в IIS, что требуется в наших средах, не связанных с разработкой, необходимо увеличить uploadReadAheadSize, чтобы ssl мог работать в модуле ssl, как я полагаю.Это может быть связано с некоторым пересмотром SSL.

Кто-нибудь может описать способ предотвращения буферизации в SSL, чтобы сохранить низкий объем памяти и исключить нехватку памяти для больших сообщений http?

Загрузка больших файлов при использовании ssl иклиентские сертификаты (uploadReadAheadSize), но не хотят, чтобы все данные были доступны для чтения

...