Pooling v .NET

Václav Dajbych       25. 2. 2011       SQL, .NET       7119 zobrazení

Technika zvaná pooling je užitečná, protože dovoluje efektivně sdílet zdroje mezi uživateli. Aplikuje se typicky na vlákna nebo spojení s databází. V určitých případech však může být příčinou potíží, které nemusejí být hned zřejmé. Článek zmiňuje tři nebezpečí, která při využívání různých poolingů můžou vznikat. Popisuje, proč k nim dochází a navrhuje jejich řešení. Jejich ignorování může mít za následek vyčerpání systémových prostředků nebo bezpečností rizika.

SQL Connecting Pool

Programátor je z nejrůznějších zdrojů nabádán, aby spojení s databází navazoval na co možná nejkratší dobu. Typicky pomocí klauzule using:

public static class Database {
  public SqlConnection MyDB {
    get {
      return new SqlConnection("connection string");
    }
  }
}


using (var conn = Database.MyDB) {
  ...
}

Spojení s databází je uzavřeno po opuštění klauzule. ADO.NET však používá pooling pro spojení s databází. Otevřená spojení se uchovají pro pozdější použití. Nová instance SqlConnection tedy většinou vrací již vytvořené spojení, jen zresetované (reset je obstarán jednoduchým hand-shakem).

Výchozí kapacita poolu je sto spojení. Ta v něm zůstávají poměrně dlouho. Pokud je aplikace služba pracující v mnoha vláknech, může nastat situace, že se pool zahltí databázovými spojeními, která si drží jedna aplikační doména na úkor druhé. Tomu lze zabránit vypnutím poolingu v connection stringu parametrem Pooling=False, nebo upravit dobu, po kterou se spojení v poolu drží, parametrem Connection Lifetime.

Thread Pool

Vytvoření nového vlákna nepatří mezi nejrychlejší operace. Proto když nějaké vlákno skončí svou práci, je ponecháno při životě ještě nějakou chvíli. Když potom jiný kus kódu žádá o vytvoření vlákna, je mu podstrčeno vlákno neukončené a zhostí se jeho úlohy. Tím se ušetří čas a prostředky vytváření nových vláken. Tomuto systému se říká Thread Pool. Největší počet vláken, která si může držet, je takovýto:

  • 1023 v .NET 4.0 32 bit
  • 32768 v .NET 4.0 64 bit
  • 250 na procesorové jádro v .NET 3.5
  • 25 na procesorové jádro v .NET 2.0
  • v .NET 1.0 Thread Pool ještě nebyl

Pokud webová stránka v ASP.NET volá webové služby, měla by být asynchronní. Pokud tomu tak není, je během vyřizování požadavku na webovou službu blokováno vlákno, které sestavuje obsah webové stránky. Pokud je HTTP požadavků na server mnoho, může se stát, že se vyčerpají všechna volná vlákna z Thread Poolu a nová už nebude možné vytvořit kvůli nedostatku paměti nebo výpočetního výkonu. Většina vláken však jen bude čekat, než odpoví webová služba. To je zbytečné. Pokud je webová stránka asynchronní, je počátek jejího zpracování obsloužen jedním vláknem, které zavolá webovou službu a ukončí se. Až webová služba odpoví, je odpověď zpracována druhým vláknem, které nakonec obslouží i konec generování stránky. Během čekání na odpověď služby není vlákno blokováno a může pracovat na jiných úlohách.

<%@ Page Language="C#" AutoEventWireup="false" CodeBehind="default.aspx.cs" Inherits="AsynchronousWebParts.Default" Async="true" %>
<!DOCTYPE html>
<html>
  <head runat="server">
    <title>Asynchronous Web Parts</title>
  </head>
  <body>
    <form id="form" runat="server">
      <asp:GridView ID="grid" runat="server"/>
    </form>
  </body>
</html>

namespace AsynchronousWebParts {
  public partial class Default : System.Web.UI.Page {

    SqlConnection conn;
    SqlCommand cmd;

    public Default() {
      Load += new EventHandler(Page_OnLoad);
    }

    private void Page_OnLoad(object sender, EventArgs e) {

    string connStr = "Data Source=|DataDirectory|database.sdf;async=true";

    string sql = "SELECT * FROM [Database].[dbo].[Table]";

    conn = new SqlConnection(connStr);
    cmd = new SqlCommand(sql, conn);
    conn.Open();

    // launch data request asynchronously using page async task
    Page.RegisterAsyncTask(new PageAsyncTask(
      new BeginEventHandler(BeginGetData),
      new EndEventHandler(FinishGetData),
      new EndEventHandler(Timeout),
      null, true));
    }

    IAsyncResult BeginGetData(object src, EventArgs e, AsyncCallback cb, object state) {
      return cmd.BeginExecuteReader(cb, state);
    }

    private void FinishGetData(IAsyncResult ar) {
      grid.DataSource = cmd.EndExecuteReader(ar);
      grid.DataBind();
      conn.Close();
    }

    private void Timeout(IAsyncResult ar) {
      if (conn.State == ConnectionState.Open) conn.Close();
        // timed out
      }
    }
  }
}

Stránka musí obsahovat parametr async="true". Potom může registrovat asynchronní úlohu pomocí metody RegisterAsyncTask. Použití tohoto postupu se dá zabránit zablokování procesu, který obsluhuje celý Application Pool. Myslete na to, že i když je vše uděláno synchronně a nevyskytují se žádné problémy, můžou rázem vyvstat, pokud server poskytující webovou službu spadne. Každý požadavek rázem blokuje jedno vlákno na 30 sekund (podle toho, na kolik máte nastaven timeout).

Application Pool

Každý ThreadPool je jen pro jediný proces. Aby bylo sdílení vláken a dalších prostředků dostatečně účinné i na webových serverech, jeden proces může obsluhovat několik virtuálních serverů. Tím ovšem v některých případech padají i bezpečnostní opatření. Webová aplikace na jednom virtuálním serveru může číst data serveru jiného, pokud s ním sdílí stejný Application Pool. Proto je nejlepší mít pro každého zákazníka vlastní Application Pool a jejich data tak od sebe bezpečně izolovat.

 

hodnocení článku

0       Hodnotit mohou jen registrované uživatelé.

 

Mohlo by vás také zajímat

Windows Azure - díl 4.: SQL Database – Nasazení databáze, dostupnost a škálovatelnost

Tento díl seriálu je věnován migraci databáze do Windows Azure a také dostupnosti a škálovatelnosti této služby.

Fulltext v prostředí Windows Azure

Jeden antipattern, který dokáže asynchronní programování pořádně znepříjemnit

 

 

Nový příspěvek

 

Příspěvky zaslané pod tento článek se neobjeví hned, ale až po schválení administrátorem.

                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

Příspěvky zaslané pod tento článek se neobjeví hned, ale až po schválení administrátorem.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:  
Heslo:  

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback