Bagikan melalui


Pengamat

Ada situasi di mana pola pesan/respons sederhana tidak cukup, dan klien perlu menerima pemberitahuan asinkron. Misalnya, pengguna mungkin ingin diberi tahu ketika pesan instan baru telah diterbitkan oleh teman.

Pengamat klien adalah mekanisme yang memungkinkan memberi tahu klien secara asinkron. Antarmuka pengamat harus mewarisi dari IGrainObserver, dan semua metode harus mengembalikan , void, Task, Task<TResult>ValueTask, atau ValueTask<TResult>. Jenis void pengembalian tidak disarankan karena dapat mendorong penggunaan async void pada implementasi, yang merupakan pola berbahaya karena dapat mengakibatkan aplikasi crash jika pengecualian dilemparkan dari metode . Sebagai gantinya, untuk skenario pemberitahuan upaya terbaik, pertimbangkan untuk menerapkan OneWayAttribute ke metode antarmuka pengamat. Ini akan menyebabkan penerima tidak mengirim respons untuk pemanggilan metode dan akan menyebabkan metode segera kembali di situs panggilan, tanpa menunggu respons dari pengamat. Biji-bijian memanggil metode pada pengamat dengan memanggilnya seperti metode antarmuka biji-bijian. Orleans Runtime akan memastikan pengiriman permintaan dan respons. Kasus penggunaan umum bagi pengamat adalah meminta klien untuk menerima pemberitahuan ketika peristiwa terjadi dalam Orleans aplikasi. Butir yang menerbitkan pemberitahuan tersebut harus menyediakan API untuk menambahkan atau menghapus pengamat. Selain itu, biasanya lebih mudah untuk mengekspos metode yang memungkinkan langganan yang ada dibatalkan.

Pengembang biji-bijian dapat menggunakan kelas utilitas seperti ObserverManager<TObserver> untuk menyederhanakan pengembangan jenis biji-bijian yang diamati. Tidak seperti biji-bijian, yang secara otomatis diaktifkan kembali sesuai kebutuhan setelah kegagalan, klien tidak toleran terhadap kesalahan: klien yang gagal mungkin tidak pernah pulih. Untuk alasan ini, utilitas menghapus langganan setelah durasi yang dikonfigurasi ObserverManager<T> . Klien yang aktif harus berlangganan ulang pada timer untuk menjaga langganan mereka tetap aktif.

Untuk berlangganan pemberitahuan, klien harus terlebih dahulu membuat objek lokal yang mengimplementasikan antarmuka pengamat. Kemudian memanggil metode di pabrik pengamat, CreateObjectReference', untuk mengubah objek menjadi referensi biji-bijian, yang kemudian dapat diteruskan ke metode langganan pada butir pemberitahuan.

Model ini juga dapat digunakan oleh biji-bijian lain untuk menerima pemberitahuan asinkron. Biji-bijian juga dapat mengimplementasikan IGrainObserver antarmuka. Tidak seperti dalam kasus langganan klien, butir berlangganan hanya mengimplementasikan antarmuka pengamat dan meneruskan referensi ke dirinya sendiri (misalnya this.AsReference<IMyGrainObserverInterface>()). Tidak perlu CreateObjectReference() karena biji-bijian sudah dapat diatasi.

Contoh kode

Mari kita asumsikan bahwa kita memiliki biji-bijian yang secara berkala mengirim pesan ke klien. Untuk kesederhanaan, pesan dalam contoh kami akan menjadi string. Kami pertama-tama menentukan antarmuka pada klien yang akan menerima pesan.

Antarmuka akan terlihat seperti ini

public interface IChat : IGrainObserver
{
    Task ReceiveMessage(string message);
}

Satu-satunya hal khusus adalah bahwa antarmuka harus mewarisi dari IGrainObserver. Sekarang setiap klien yang ingin mengamati pesan tersebut harus menerapkan kelas yang mengimplementasikan IChat.

Kasus paling sederhana akan menjadi sesuatu seperti ini:

public class Chat : IChat
{
    public Task ReceiveMessage(string message)
    {
        Console.WriteLine(message);
        return Task.CompletedTask;
    }
}

Di server, kita selanjutnya harus memiliki Grain yang mengirim pesan obrolan ini ke klien. Grain juga harus memiliki mekanisme bagi klien untuk berlangganan dan berhenti berlangganan sendiri untuk pemberitahuan. Untuk langganan, biji-bijian dapat menggunakan instans kelas ObserverManager<TObserver>utilitas .

Catatan

ObserverManager<TObserver> adalah bagian dari Orleans sejak versi 7.0. Untuk versi yang lebih lama, implementasi berikut dapat disalin.

class HelloGrain : Grain, IHello
{
    private readonly ObserverManager<IChat> _subsManager;

    public HelloGrain(ILogger<HelloGrain> logger)
    {
        _subsManager =
            new ObserverManager<IChat>(
                TimeSpan.FromMinutes(5), logger);
    }

    // Clients call this to subscribe.
    public Task Subscribe(IChat observer)
    {
        _subsManager.Subscribe(observer, observer);

        return Task.CompletedTask;
    }

    //Clients use this to unsubscribe and no longer receive messages.
    public Task UnSubscribe(IChat observer)
    {
        _subsManager.Unsubscribe(observer);

        return Task.CompletedTask;
    }
}

Untuk mengirim pesan ke klien, Notify metode ObserverManager<IChat> instans dapat digunakan. Metode ini mengambil Action<T> metode atau ekspresi lambda (di mana T jenisnya IChat di sini). Anda dapat memanggil metode apa pun pada antarmuka untuk mengirimkannya ke klien. Dalam kasus kami, kami hanya memiliki satu metode, ReceiveMessage, dan kode pengiriman kami di server akan terlihat seperti ini:

public Task SendUpdateMessage(string message)
{
    _subsManager.Notify(s => s.ReceiveMessage(message));

    return Task.CompletedTask;
}

Sekarang server kami memiliki metode untuk mengirim pesan ke klien pengamat, dua metode untuk berlangganan/berhenti berlangganan, dan klien telah menerapkan kelas yang dapat mengamati pesan biji-bijian. Langkah terakhir adalah membuat referensi pengamat pada klien menggunakan kelas yang kami terapkan Chat sebelumnya, dan membiarkannya menerima pesan setelah berlangganan.

Kode akan terlihat seperti ini:

//First create the grain reference
var friend = _grainFactory.GetGrain<IHello>(0);
Chat c = new Chat();

//Create a reference for chat, usable for subscribing to the observable grain.
var obj = _grainFactory.CreateObjectReference<IChat>(c);

//Subscribe the instance to receive messages.
await friend.Subscribe(obj);

Sekarang setiap kali biji-bijian kami di server memanggil metode , SendUpdateMessage semua klien berlangganan akan menerima pesan. Dalam kode klien kami, Chat instans dalam variabel c akan menerima pesan dan mengeluarkannya ke konsol.

Penting

Objek yang diteruskan ke CreateObjectReference ditahan melalui WeakReference<T> dan oleh karena itu akan menjadi sampah yang dikumpulkan jika tidak ada referensi lain.

Pengguna harus mempertahankan referensi untuk setiap pengamat yang tidak ingin mereka kumpulkan.

Catatan

Pengamat secara inheren tidak dapat diandalkan karena klien yang menghosting pengamat mungkin gagal dan pengamat yang dibuat setelah pemulihan memiliki identitas yang berbeda (acak). ObserverManager<TObserver> bergantung pada resubskripsi berkala oleh pengamat, seperti yang dibahas di atas, sehingga pengamat yang tidak aktif dapat dihapus.

Model eksekusi

IGrainObserver Implementasi didaftarkan melalui panggilan ke IGrainFactory.CreateObjectReference dan setiap panggilan ke metode tersebut membuat referensi baru yang menunjuk ke implementasi tersebut. Orleans akan menjalankan permintaan yang dikirim ke masing-masing referensi ini satu per satu, hingga selesai. Pengamat tidak masuk kembali dan oleh karena itu permintaan bersamaan kepada pengamat tidak akan diselingi oleh Orleans. Jika ada beberapa pengamat yang menerima permintaan secara bersamaan, permintaan tersebut dapat dijalankan secara paralel. Eksekusi metode pengamat tidak dipengaruhi oleh atribut seperti AlwaysInterleaveAttribute atau ReentrantAttribute: model eksekusi tidak dapat disesuaikan oleh pengembang.