Использование потоков ThreadPool с продолжительными запросами ADO.NET. Это масштабируемое? - PullRequest
2 голосов
/ 18 ноября 2009

В настоящее время мы улучшаем приложение ASP.NET, которое выполняет кавычки для ряда продуктов.

В настоящее время существующий механизм котировок в основном представляет собой большую хранимую процедуру (2-3 секунды на вызов), за которой следует небольшой объем бизнес-логики, которая выполняется после вызова процедуры.

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

Наш текущий подход заключается в том, чтобы инкапсулировать каждую работу по продукту в потоке ThreadPool. Кажется, это работает намного лучше, но меня немного беспокоит, что, несмотря на то, что он хорошо работает с небольшим количеством пользователей, он будет хорошо масштабироваться в производственной среде?

Обратите внимание, что в настоящее время мы не используем асинхронные методы ADO.NET.

Примечание. Наш код, который вызывает ThreadPool, имеет регулятор, который ставит запросы в очередь, поэтому мы можем использовать только настраиваемое количество потоков из ThreadPool за один раз. Нам также не нужно ждать результатов котировки на той же странице, мы даем пользователю возможность прогрессировать и проверять наличие обновлений (страница результатов цитаты использует AJAX для проверки результатов).

Примечание: предпочтительным решением будет использование очереди сообщений, поскольку служба цитат является односторонней операцией. Однако сроки реализации проекта не дали нам времени сделать это.

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

Ответы [ 2 ]

6 голосов
/ 18 ноября 2009

Использование потоков ThreadPool с длинными выполнение запросов ADO.NET. Это масштабируемый

Короткий ответ - нет, он не масштабируется.

Причина в том, что потоки ThreadPool также используются для обработки обычных запросов ASP.NET (то же самое верно и для BeginInvoke). Количество этих потоков ограничено, и когда они израсходованы, входящие HTTP-запросы блокируются, пока поток не станет доступным.

Хотя вы можете увеличить количество потоков в пуле потоков ASP.NET, существуют и другие проблемы, например, тот факт, что количество потоков не является фиксированным; оно медленно увеличивается в ответ на нагрузку. Вы можете / должны использовать асинхронные страницы, но это все еще оставляет открытым вопрос о том, как вы на самом деле запускаете свой SP. Было бы намного лучше переключиться на асинхронные методы ADO.NET для этой части, если вы можете. Они также намного легче, чем использование потока для запроса.

Если это поможет, я подробно расскажу об этом в своей книге ( Сверхбыстрый ASP.NET ).

1 голос
/ 18 ноября 2009

ASP.NET имеет встроенные асинхронные обработчики, которые позволяют запускать запрос, он выполняется в потоке, не являющемся обработчиком, и может завершать в 3-м потоке (отличном от исходного потока обработчика запросов) все встроенные. Я использовал это много раз.

http://msdn.microsoft.com/en-us/magazine/cc163725.aspx

Напишите себе маленький вспомогательный метод, чтобы все это подключить.

/// <summary>
/// On load event override
/// </summary>
/// <param name="e">arguments to the event</param>
protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);
    string query = this.Page.Request.QueryString["query"];
    if (!string.IsNullOrEmpty(query))
    {
        var pat = new PageAsyncTask(this.BeginAsync, this.EndAsync, this.TimeOut, query);
        this.Page.RegisterAsyncTask(pat);
    }
    string me = string.Format(Format, System.Threading.Thread.CurrentThread.ManagedThreadId, "Onload");
    Trace.Write(me);
}
protected override void Render(HtmlTextWriter writer)
{
    string me = string.Format(Format, System.Threading.Thread.CurrentThread.ManagedThreadId, "Render");
    Trace.Write(me);
    this.Icompleted.Text = DateTime.Now.ToString();
    base.Render(writer);
}
/// <summary>
/// start the async task
/// </summary>
/// <param name="sender">original caller</param>
/// <param name="e">unused arguments</param>
/// <param name="cb">call back routine</param>
/// <param name="state">saved stated</param>
/// <returns>IAsyncResult to signal ender</returns>
private IAsyncResult BeginAsync(object sender, EventArgs e, AsyncCallback cb, object state)
{
    this.bsc = new YourWebServiceReferenceGoesHere();
    return this.bsc.BeginGetResponseXml("1", (string)state, "10", "1", cb, state);
}

/// <summary>
/// when the task completes
/// </summary>
/// <param name="ar">the async result</param>
private void EndAsync(IAsyncResult ar)
{
    XmlResponse response = this.bsc.EndGetResponseXml(ar);
    this.bsc.Close();
    this.bsc = null;
    this.PostProcess(response);
}

private void TimeOut(IAsyncResult ar)
{
    // currently we do nothing here.
}

/// <summary>
/// 
/// </summary>
private void PostProcess(XmlResponse response )
{
    string me = string.Format(Format, System.Threading.Thread.CurrentThread.ManagedThreadId, "bingsearch");
    Trace.Write(me);
    var xds = new XmlDataSource 
    { 
        EnableCaching = false, 
        CacheDuration = 0, 
        Data = response.Xml, 
        Transform = this.RemoveNamespaces() 
    };
    this.some.DataSource = xds;
    this.some.DataBind();
}
...