Penggabungan

Sampel Pooling menunjukkan cara memperluas Windows Communication Foundation (WCF) untuk mendukung pengumpulan objek. Sampel menunjukkan cara membuat atribut yang secara sintetis dan semantik mirip ObjectPoolingAttribute dengan fungsionalitas atribut Enterprise Services. Pengumpulan objek dapat memberikan peningkatan dramatis pada performa aplikasi. Namun, itu dapat memiliki efek yang berlawanan jika tidak digunakan dengan benar. Pengumpulan objek membantu mengurangi overhead pembuatan ulang objek yang sering digunakan yang memerlukan inisialisasi ekstensif. Namun, jika panggilan ke metode pada objek yang dalam kumpulan memerlukan waktu yang cukup lama untuk diselesaikan, pemrosesan objek akan mengantrekan permintaan baru segera setelah ukuran kumpulan maksimum tercapai. Dengan demikian, mungkin gagal melayani sejumlah permintaan pembuatan objek dengan mengeluarkan pengecualian waktu habis.

Nota

Prosedur penyiapan dan instruksi build untuk sampel ini terletak di akhir topik ini.

Langkah pertama dalam membuat ekstensi WCF adalah memutuskan titik ekstensibilitas yang akan digunakan.

Di WCF istilah dispatcher mengacu pada komponen runtime yang bertanggung jawab untuk mengonversi pesan masuk menjadi pemanggilan metode pada layanan pengguna dan untuk mengonversi nilai pengembalian dari metode tersebut ke pesan keluar. Layanan WCF membuat dispatcher untuk setiap titik akhir. Klien WCF harus menggunakan dispatcher jika kontrak yang terkait dengan klien tersebut adalah kontrak dupleks.

Dispatcher kanal dan titik akhir menawarkan ekstensibilitas menyeluruh untuk kanal dan kontrak dengan memaparkan berbagai properti yang mengontrol perilaku dispatcher. Properti ini DispatchRuntime juga memungkinkan Anda memeriksa, memodifikasi, atau menyesuaikan proses pengiriman. Sampel ini berfokus pada properti InstanceProvider yang menunjuk ke objek yang menyediakan instans kelas layanan.

The IInstanceProvider

Di WCF, dispatcher membuat instans kelas layanan menggunakan InstanceProvider, yang mengimplementasikan antarmuka IInstanceProvider. Antarmuka ini memiliki tiga metode:

Kumpulan Objek

Implementasi kustom IInstanceProvider menyediakan semantik pengumpulan objek yang diperlukan untuk layanan. Oleh karena itu, sampel ini memiliki jenis ObjectPoolingInstanceProvider yang menyediakan implementasi kustom IInstanceProvider untuk pooling. Ketika Dispatcher memanggil metode GetInstance(InstanceContext, Message), implementasi kustom tidak membuat instans baru, melainkan mencari objek yang sudah ada di kumpulan memori. Jika ada yang tersedia, maka akan dikembalikan. Jika tidak, objek baru dibuat. Implementasi untuk GetInstance ditunjukkan dalam kode sampel berikut.

object IInstanceProvider.GetInstance(InstanceContext instanceContext, Message message)
{
    object obj = null;

    lock (poolLock)
    {
        if (pool.Count > 0)
        {
            obj = pool.Pop();
        }
        else
        {
            obj = CreateNewPoolObject();
        }
        activeObjectsCount++;
    }

    WritePoolMessage(ResourceHelper.GetString("MsgNewObject"));

    idleTimer.Stop();

    return obj;
}

Implementasi ReleaseInstance khusus menambahkan kembali instans yang dirilis ke kumpulan dan mengurangi nilai ActiveObjectsCount. Dispatcher dapat memanggil metode ini dari utas yang berbeda, dan oleh karena itu akses yang disinkronkan ke anggota tingkat kelas pada kelas ObjectPoolingInstanceProvider diperlukan.

void IInstanceProvider.ReleaseInstance(InstanceContext instanceContext, object instance)
{
    lock (poolLock)
    {
        pool.Push(instance);
        activeObjectsCount--;

        WritePoolMessage(
        ResourceHelper.GetString("MsgObjectPooled"));

        // When the service goes completely idle (no requests
        // are being processed), the idle timer is started
        if (activeObjectsCount == 0)
            idleTimer.Start();
    }
}

Metode ReleaseInstance menyediakan fitur "pembersihan inisialisasi". Biasanya pool mempertahankan jumlah minimum objek selama masa hidup pool. Namun, terdapat kemungkinan terjadinya periode penggunaan berlebihan yang memerlukan pembuatan objek tambahan di dalam kumpulan untuk memenuhi batas maksimum yang ditentukan dalam konfigurasi. Akhirnya, ketika kolam menjadi kurang aktif, objek-objek surplus tersebut dapat menjadi beban tambahan. Oleh karena itu, ketika activeObjectsCount mencapai nol, timer diam dimulai yang memicu dan melakukan siklus pembersihan.

Menambahkan Perilaku

Ekstensi lapisan dispatcher dikaitkan menggunakan perilaku berikut:

  • Perilaku Layanan. Ini memungkinkan penyesuaian seluruh runtime layanan.

  • Perilaku Titik Akhir. Ini memungkinkan penyesuaian titik akhir layanan, khususnya Dispatcher Saluran dan Titik Akhir.

  • Tingkah Laku Kontrak. Ini memungkinkan penyesuaian kelas ClientRuntime pada klien dan kelas DispatchRuntime pada layanan masing-masing.

Untuk tujuan ekstensi pengumpulan objek, perilaku layanan harus dibuat. Perilaku layanan dibuat dengan menerapkan antarmuka IServiceBehavior. Ada beberapa cara untuk membuat model layanan mengetahui perilaku kustom:

  • Menggunakan atribut kustom.

  • Secara imperatif menambahkannya ke kumpulan perilaku deskripsi layanan.

  • Memperluas file konfigurasi.

Sampel ini menggunakan atribut kustom. ServiceHost Ketika dibangun, ia memeriksa atribut yang digunakan dalam definisi jenis layanan dan menambahkan perilaku yang tersedia ke koleksi perilaku deskripsi layanan.

Antarmuka IServiceBehavior memiliki tiga metode di dalamnya -- Validate, , AddBindingParametersdan ApplyDispatchBehavior. Metode Validate ini digunakan untuk memastikan bahwa perilaku dapat diterapkan ke layanan. Dalam sampel ini, implementasi memastikan bahwa layanan tidak dikonfigurasi dengan Single. Metode AddBindingParameters ini digunakan untuk mengonfigurasi pengikatan layanan. Ini tidak diperlukan dalam skenario ini. ApplyDispatchBehavior digunakan untuk mengonfigurasi dispatcher layanan. Metode ini dipanggil oleh WCF ketika ServiceHost sedang diinisialisasi. Parameter berikut diteruskan ke metode ini:

  • Description: Argumen ini menyediakan deskripsi layanan untuk seluruh layanan. Ini dapat digunakan untuk memeriksa data deskripsi tentang titik akhir layanan, kontrak, pengikatan, dan data lainnya.

  • ServiceHostBase: Argumen ini menyediakan ServiceHostBase yang saat ini sedang diinisialisasi.

Dalam implementasi kustom IServiceBehavior, instance ObjectPoolingInstanceProvider baru dibuat dan ditetapkan ke properti InstanceProvider di masing-masing DispatchRuntime di ServiceHostBase.

void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription description, ServiceHostBase serviceHostBase)
{
    // Create an instance of the ObjectPoolInstanceProvider.
    ObjectPoolingInstanceProvider instanceProvider = new
           ObjectPoolingInstanceProvider(description.ServiceType,
                                                    minPoolSize);

    // Forward the call if we created a ServiceThrottlingBehavior.
    if (this.throttlingBehavior != null)
    {
        ((IServiceBehavior)this.throttlingBehavior).ApplyDispatchBehavior(description, serviceHostBase);
    }

    // In case there was already a ServiceThrottlingBehavior
    // (this.throttlingBehavior==null), it should have initialized
    // a single ServiceThrottle on all ChannelDispatchers.
    // As we loop through the ChannelDispatchers, we verify that
    // and modify the ServiceThrottle to guard MaxPoolSize.
    ServiceThrottle throttle = null;

    foreach (ChannelDispatcherBase cdb in
            serviceHostBase.ChannelDispatchers)
    {
        ChannelDispatcher cd = cdb as ChannelDispatcher;
        if (cd != null)
        {
            // Make sure there is exactly one throttle used by all
            // endpoints. If there were others, we could not enforce
            // MaxPoolSize.
            if ((this.throttlingBehavior == null) &&
                        (this.maxPoolSize != Int32.MaxValue))
            {
                throttle ??= cd.ServiceThrottle;
                if (cd.ServiceThrottle == null)
                {
                    throw new
InvalidOperationException(ResourceHelper.GetString("ExNullThrottle"));
                }
                if (throttle != cd.ServiceThrottle)
                {
                    throw new InvalidOperationException(ResourceHelper.GetString("ExDifferentThrottle"));
                }
             }

             foreach (EndpointDispatcher ed in cd.Endpoints)
             {
                 // Assign it to DispatchBehavior in each endpoint.
                 ed.DispatchRuntime.InstanceProvider =
                                      instanceProvider;
             }
         }
     }

     // Set the MaxConcurrentInstances to limit the number of items
     // that will ever be requested from the pool.
     if ((throttle != null) && (throttle.MaxConcurrentInstances >
                                      this.maxPoolSize))
     {
         throttle.MaxConcurrentInstances = this.maxPoolSize;
     }
}

Selain IServiceBehavior implementasi kelas ObjectPoolingAttribute memiliki beberapa anggota untuk menyesuaikan kumpulan objek menggunakan argumen atribut. Anggota ini termasuk MaxPoolSize, MinPoolSize, dan CreationTimeout, untuk mencocokkan sekumpulan fitur pooling objek yang disediakan oleh .NET Enterprise Services.

Perilaku kumpulan objek sekarang dapat ditambahkan ke layanan WCF dengan memberi anotasi pada implementasi layanan dengan atribut ObjectPooling khusus yang baru dibuat.

[ObjectPooling(MaxPoolSize=1024, MinPoolSize=10, CreationTimeout=30000)]
public class PoolService : IPoolService
{
  // …
}

Menjalankan sampel

Sampel menunjukkan manfaat performa yang dapat diperoleh dengan menggunakan pengumpulan objek dalam skenario tertentu.

Aplikasi layanan mengimplementasikan dua layanan -- WorkService dan ObjectPooledWorkService. Kedua layanan tersebut memiliki implementasi yang sama -- keduanya memerlukan inisialisasi yang mahal dan kemudian mengekspos metode DoWork() yang relatif murah. Satu-satunya perbedaan adalah bahwa ObjectPooledWorkService memiliki pooling objek yang dikonfigurasi.

[ObjectPooling(MinPoolSize = 0, MaxPoolSize = 5)]
public class ObjectPooledWorkService : IDoWork
{
    public ObjectPooledWorkService()
    {
        Thread.Sleep(5000);
        ColorConsole.WriteLine(ConsoleColor.Blue, "ObjectPooledWorkService instance created.");
    }

    public void DoWork()
    {
        ColorConsole.WriteLine(ConsoleColor.Blue, "ObjectPooledWorkService.GetData() completed.");
    }
}

Ketika Anda menjalankan klien, itu menghitung waktu saat memanggil WorkService sebanyak 5 kali. Kemudian melakukan pemanggilan ObjectPooledWorkService sebanyak 5 kali. Perbedaan waktu kemudian ditampilkan:

Press <ENTER> to start the client.

Calling WorkService:
1 - DoWork() Done
2 - DoWork() Done
3 - DoWork() Done
4 - DoWork() Done
5 - DoWork() Done
Calling WorkService took: 26722 ms.
Calling ObjectPooledWorkService:
1 - DoWork() Done
2 - DoWork() Done
3 - DoWork() Done
4 - DoWork() Done
5 - DoWork() Done
Calling ObjectPooledWorkService took: 5323 ms.
Press <ENTER> to exit.

Nota

Pertama kali klien menjalankan kedua layanan tampaknya membutuhkan waktu yang sama. Jika Anda menjalankan ulang sampelnya, Anda dapat melihat bahwa ObjectPooledWorkService pengembaliannya jauh lebih cepat karena instans objek tersebut sudah ada dalam kumpulan.

Untuk menyiapkan, mengompilasi, dan menjalankan sampel

  1. Pastikan Anda telah melakukan Prosedur Penyiapan One-Time untuk Sampel Windows Communication Foundation.

  2. Untuk membangun solusi, ikuti instruksi dalam Membangun Sampel Windows Communication Foundation.

  3. Untuk menjalankan sampel dalam konfigurasi satu atau lintas komputer, ikuti instruksi di Menjalankan Sampel Windows Communication Foundation.

Nota

Jika Anda menggunakan Svcutil.exe untuk meregenerasi konfigurasi untuk sampel ini, pastikan untuk mengubah nama titik akhir dalam konfigurasi klien agar sesuai dengan kode klien.