Pola Pipa dan Filter

Azure Blob Storage
Azure Functions
Azure Queue Storage

Memecah tugas yang melakukan pemrosesan kompleks menjadi serangkaian elemen terpisah yang dapat digunakan kembali. Melakukannya dapat meningkatkan performa, skalabilitas, dan penggunaan kembali langkah-langkah awal dengan memungkinkan elemen tugas yang melakukan pemrosesan untuk disebarkan dan diskalakan secara independen. Pola Pipa dan Filter mendukung tingkat modularitas yang tinggi.

Konteks dan masalah

Anda memiliki alur tugas berurutan yang perlu Anda proses. Pendekatan yang mudah tetapi tidak fleksibel untuk mengimplementasikan aplikasi ini adalah dengan melakukan pemrosesan ini dalam modul monolitik. Namun, pendekatan ini kemungkinan akan mengurangi peluang untuk merefaktor kode, mengoptimalkannya, atau menggunakannya kembali jika bagian dari pemrosesan yang sama diperlukan di tempat lain dalam aplikasi.

Diagram berikut mengilustrasikan salah satu masalah dengan pemrosesan data menggunakan pendekatan monolitik, ketidakmampuan untuk menggunakan kembali kode di beberapa alur. Dalam contoh ini, aplikasi menerima dan memproses data dari dua sumber. Modul terpisah memproses data dari setiap sumber dengan melakukan serangkaian tugas untuk mengubah data sebelum meneruskan hasilnya ke logika bisnis aplikasi.

Diagram yang menunjukkan solusi yang diimplementasikan dengan modul monolitik.

Beberapa tugas yang dilakukan modul monolitik secara fungsional mirip, tetapi kode harus diulang dalam kedua modul dan kemungkinan digabungkan erat dalam modulnya. Selain ketidakmampuan untuk menggunakan kembali logika, pendekatan ini memperkenalkan risiko ketika persyaratan berubah. Anda harus ingat untuk memperbarui kode di kedua tempat.

Ada tantangan lain dengan implementasi monolitik yang tidak terkait dengan beberapa alur atau penggunaan kembali. Dengan monolit, Anda tidak memiliki kemampuan untuk menjalankan tugas tertentu di lingkungan yang berbeda atau menskalakannya secara independen. Beberapa tugas mungkin intensif komputasi dan akan mendapat manfaat dari berjalan pada perangkat keras yang kuat atau menjalankan beberapa instans secara paralel. Tugas lain mungkin tidak memiliki persyaratan yang sama. Selanjutnya, dengan monolit, sulit untuk menyusun ulang tugas atau menyuntikkan tugas baru dalam alur. Perubahan ini memerlukan pengulangan seluruh alur.

Solusi

Memecah pemrosesan yang diperlukan untuk setiap aliran menjadi satu set komponen terpisah (atau filter), masing-masing mengerjakan satu tugas. Tugas komposit harus menggunakan beberapa filter daripada satu. Filter disusun ke dalam alur dengan menyambungkan filter dengan pipa. Filter independen, mandiri, dan biasanya tanpa status. Filter menerima pesan dari pipa masuk dan menerbitkan pesan ke pipa keluar yang berbeda. Filter dapat mengubah pesan atau mengujinya terhadap satu atau beberapa kriteria untuk menyertakan logika kondisional. Pipa tidak melakukan perutean atau logika lainnya. Mereka hanya menyambungkan filter, meneruskan pesan output dari satu filter sebagai input ke filter berikutnya.

Filter bertindak secara independen dan tidak menyadari filter lain. Mereka hanya mengetahui skema input dan output mereka. Dengan demikian, filter dapat diatur dalam urutan apa pun selama skema input untuk filter apa pun cocok dengan skema output untuk filter sebelumnya. Menggunakan skema standar untuk semua filter meningkatkan kemampuan untuk menyusun ulang filter. Arsitektur pipa dan filter mendorong penggunaan kembali komposisi.

Kopling filter yang longgar memudahkan untuk:

  • Membuat alur baru yang terdiri dari filter yang ada
  • Memperbarui atau mengganti logika dalam filter individual
  • Menyusun ulang filter, jika perlu
  • Jalankan filter pada perangkat keras yang berbeda, jika diperlukan
  • Menjalankan filter secara paralel

Diagram ini menunjukkan solusi yang diimplementasikan dengan pipa dan filter:

Diagram yang menunjukkan solusi yang diimplementasikan dengan pipa dan filter.

Waktu yang diperlukan untuk memproses satu permintaan tergantung pada kecepatan filter terlambat dalam alur. Satu atau beberapa filter bisa menjadi hambatan, terutama jika sejumlah besar permintaan muncul dalam aliran dari sumber data tertentu. Kemampuan untuk menjalankan instans paralel filter lambat memungkinkan sistem untuk menyebarkan beban dan meningkatkan throughput.

Kemampuan untuk menjalankan filter pada instans komputasi yang berbeda memungkinkan mereka untuk diskalakan secara independen dan memanfaatkan elastisitas yang disediakan banyak lingkungan cloud. Filter yang intensif secara komputasi dapat berjalan pada perangkat keras berkinerja tinggi, sementara filter lain yang kurang menuntut dapat dihosting pada perangkat keras komoditas yang lebih murah. Filter bahkan tidak perlu berada di pusat data atau lokasi geografis yang sama, memungkinkan setiap elemen dalam alur berjalan di lingkungan yang dekat dengan sumber daya yang diperlukan. Upaya ini memerlukan teknik desain tertentu seperti olahpesan, multi-utas, dan sebagainya untuk memaksimalkan elastisitas setiap pipa atau filter. Diagram ini memperlihatkan contoh yang diterapkan ke alur untuk data dari Sumber 1:

Diagram yang memperlihatkan contoh yang diterapkan ke alur untuk data dari Sumber 1.

Jika input dan output filter disusun sebagai aliran, Anda dapat melakukan pemrosesan untuk setiap filter secara paralel. Filter pertama dalam alur dapat memulai pekerjaannya dan menghasilkan hasilnya, yang diteruskan langsung ke filter berikutnya dalam urutan sebelum filter pertama menyelesaikan pekerjaannya.

Menggunakan pola Pipa dan Filter bersama dengan pola Kompensasi Transaksi adalah pendekatan alternatif untuk menerapkan transaksi terdistribusi. Anda dapat memecah transaksi terdistribusi menjadi tugas terpisah yang dapat dikompensasi, yang masing-masing dapat diimplementasikan melalui filter yang juga mengimplementasikan pola Transaksi Kompensasi. Anda dapat menerapkan filter dalam alur sebagai tugas terpisah yang dihosting yang berjalan dekat dengan data yang mereka pertahankan.

Masalah dan pertimbangan

Pertimbangkan poin-poin berikut saat Anda memutuskan cara menerapkan pola ini:

  • Alam monolitik. Pola ini biasanya diimplementasikan sebagai alur monolitik, jadi untuk setiap perubahan, seluruh rantai filter harus diuji ujung ke ujung. Selain itu, toleransi kesalahan untuk seluruh proses perlu dipertimbangkan; jika filter atau pipa gagal, seluruh alur kemungkinan akan gagal.

  • Kompleksitas. Peningkatan fleksibilitas yang diberikan pola ini juga dapat memperkenalkan kompleksitas, terutama jika filter dalam alur didistribusikan di berbagai server.

  • Keandalan. Gunakan infrastruktur yang memastikan bahwa data yang mengalir di antara filter dalam pipa tidak hilang.

  • Sifat Idempoten. Jika filter dalam alur gagal setelah menerima pesan dan pekerjaan dijadwalkan ulang ke instans filter lain, bagian dari pekerjaan mungkin sudah selesai. Jika pekerjaan memperbarui beberapa aspek status global (seperti informasi yang disimpan dalam database), satu pembaruan dapat diulang. Masalah serupa mungkin terjadi jika filter gagal setelah memposting hasilnya ke filter berikutnya, tetapi sebelum menunjukkan bahwa filter berhasil menyelesaikan pekerjaannya. Dalam kasus ini, instans lain dari filter dapat mengulangi pekerjaan ini, menyebabkan hasil yang sama diposting dua kali. Skenario ini dapat mengakibatkan filter berikutnya dalam alur yang memproses data yang sama dua kali. Oleh karena itu, filter dalam alur harus dirancang agar idempotensi. Untuk informasi selengkapnya, lihat Pola Idempotency di blog Jonathan Oliver.

  • Pesan berulang. Jika filter dalam alur gagal setelah memposting pesan ke tahap berikutnya dari alur, instans lain dari filter mungkin dijalankan, dan itu akan memposting salinan pesan yang sama ke alur. Skenario ini dapat menyebabkan dua instans pesan yang sama diteruskan ke filter berikutnya. Untuk menghindari masalah ini, alur harus mendeteksi dan menghilangkan pesan duplikat.

    Catatan

    Jika Anda menerapkan alur dengan menggunakan antrean pesan (seperti antrean Azure Bus Layanan), infrastruktur antrean pesan mungkin menyediakan deteksi dan penghapusan pesan duplikat otomatis.

  • Konteks dan status. Dalam alur, setiap filter pada dasarnya berjalan secara terpisah dan tidak boleh membuat asumsi tentang cara mereka digunakan. Oleh karena itu, setiap filter harus disediakan dengan konteks yang memadai untuk melakukan pekerjaannya. Konteks ini dapat mencakup sejumlah besar informasi status. Jika filter menggunakan status eksternal, seperti data dalam database atau penyimpanan eksternal, maka Anda harus mempertimbangkan dampaknya pada performa. Setiap filter harus memuat, mengoperasikan, dan mempertahankan status tersebut, yang menambahkan overhead atas solusi yang memuat status eksternal satu kali.

  • Toleransi pesan. Filter harus toleran terhadap data dalam pesan masuk yang tidak dioperasikan. Mereka beroperasi pada data yang berkaitan dengan mereka dan mengabaikan data lain dan meneruskannya tidak berubah dalam pesan output.

  • Penanganan kesalahan - Setiap filter harus menentukan apa yang harus dilakukan dalam kasus kesalahan yang melanggar. Filter harus menentukan apakah gagal alur atau menyebarluaskan pengecualian.

Kapan menggunakan pola ini

Gunakan pola ini ketika:

  • Pemrosesan yang diperlukan oleh aplikasi dapat dengan mudah dipecah menjadi sebuah set langkah independen.

  • Langkah-langkah pemrosesan yang dilakukan oleh aplikasi memiliki persyaratan skalabilitas yang berbeda.

    Catatan

    Anda dapat mengelompokkan filter yang harus diskalakan bersama-sama dalam proses yang sama. Untuk informasi selengkapnya, lihat pola Konsolidasi Sumber Daya Komputasi.

  • Anda memerlukan fleksibilitas untuk memungkinkan pengurusan ulang langkah-langkah pemrosesan yang dilakukan aplikasi, atau untuk memungkinkan kemampuan untuk menambahkan dan menghapus langkah-langkah.

  • Sistem ini dapat memperoleh keuntungan dari mendistribusikan pemrosesan untuk langkah-langkah melintasi berbagai server.

  • Anda memerlukan solusi andal yang meminimalkan efek kegagalan dalam satu langkah saat data sedang diproses.

Pola ini mungkin tidak berguna jika:

  • Aplikasi mengikuti pola respons permintaan.

  • Pemrosesan tugas harus diselesaikan sebagai bagian dari permintaan awal, seperti skenario permintaan/respons.

  • Langkah-langkah pemrosesan yang dilakukan oleh aplikasi tidak independen, atau harus dilakukan bersama-sama sebagai bagian dari satu transaksi.

  • Jumlah informasi konteks atau status yang diperlukan langkah membuat pendekatan ini tidak efisien. Anda mungkin dapat mempertahankan informasi status ke database, tetapi jangan gunakan strategi ini jika beban tambahan pada database menyebabkan ketidakcocokan yang berlebihan.

Desain beban kerja

Arsitek harus mengevaluasi bagaimana pola Pipa dan Filter dapat digunakan dalam desain beban kerja mereka untuk mengatasi tujuan dan prinsip yang tercakup dalam pilar Azure Well-Architected Framework. Contohnya:

Pilar Bagaimana pola ini mendukung tujuan pilar
Keputusan desain keandalan membantu beban kerja Anda menjadi tahan terhadap kerusakan dan untuk memastikan bahwa keputusan tersebut pulih ke status berfungsi penuh setelah kegagalan terjadi. Tanggung jawab tunggal setiap tahap memungkinkan perhatian yang terfokus dan menghindari gangguan pemrosesan data yang dikombusikan.

- RE:01 Kesederhanaan
- Pekerjaan latar belakang RE:07

Seperti halnya keputusan desain apa pun, pertimbangkan tradeoff terhadap tujuan pilar lain yang mungkin diperkenalkan dengan pola ini.

Contoh

Anda dapat menggunakan urutan antrean pesan untuk menyediakan infrastruktur yang diperlukan untuk menerapkan alur. Antrean pesan awal menerima pesan yang tidak diolah yang menjadi awal penerapan pola pipa dan filter. Komponen yang diimplementasikan sebagai tugas filter mendengarkan pesan pada antrean ini, melakukan pekerjaannya, lalu memposting pesan baru atau yang diubah ke antrean berikutnya secara berurutan. Tugas filter lain dapat mendengarkan pesan pada antrean ini, memprosesnya, memposting hasilnya ke antrean lain, dan sebagainya, hingga langkah terakhir yang mengakhiri proses pipa dan filter. Diagram ini mengilustrasikan alur yang menggunakan antrean pesan:

Diagram memperlihatkan alur yang menggunakan antrean pesan.

Alur pemrosesan gambar dapat diimplementasikan menggunakan pola ini. Jika beban kerja Anda mengambil gambar, gambar dapat melewati serangkaian filter yang sebagian besar independen dan dapat diurutkan ulang untuk melakukan tindakan seperti:

  • moderasi konten
  • mengubah ukuran
  • Watermarking
  • Reorientasi
  • Penghapusan metadata exif
  • Publikasi jaringan pengiriman konten (CDN)

Dalam contoh ini, filter dapat diimplementasikan sebagai Azure Functions yang disebarkan secara individual atau bahkan satu aplikasi Azure Function yang berisi setiap filter sebagai penyebaran terisolasi. Penggunaan pemicu Azure Function, pengikatan input, dan pengikatan output dapat menyederhanakan kode filter dan bekerja secara otomatis dengan pipa berbasis antrean menggunakan pemeriksaan klaim ke gambar untuk diproses .

Diagram memperlihatkan alur pemrosesan gambar yang menggunakan Azure Queue Storage di antara serangkaian Azure Functions.

Berikut adalah contoh filter apa yang diimplementasikan sebagai Azure Function, yang dipicu dari pipa Queue Storage dengan klaim Periksa ke gambar, dan menulis pemeriksaan klaim baru ke pipa Queue Storage lain mungkin terlihat seperti. Kami telah mengganti implementasi dengan pseudocode dalam komentar untuk brevity. Kode lainnya seperti ini dapat ditemukan dalam demonstrasi pola Pipa dan Filter yang tersedia di GitHub.

// This is the "Resize" filter. It handles claim checks from input pipe, performs the
// resize work, and places a claim check in the next pipe for anther filter to handle.
[Function(nameof(ResizeFilter))]
[QueueOutput("pipe-fjur", Connection = "pipe")]  // Destination pipe claim check
public async Task<string> RunAsync(
  [QueueTrigger("pipe-xfty", Connection = "pipe")] string imageFilePath,  // Source pipe claim check
  [BlobInput("{QueueTrigger}", Connection = "pipe")] BlockBlobClient imageBlob)  // Image to process
{
  _logger.LogInformation("Processing image {uri} for resizing.", imageBlob.Uri);

  // Idempotency checks
  // ...

  // Download image based on claim check in queue message body
  // ...
  
  // Resize the image
  // ...

  // Write resized image back to storage
  // ...

  // Create claim check for image and place in the next pipe
  // ...
  
  _logger.LogInformation("Image resizing done or not needed. Adding image {filePath} into the next pipe.", imageFilePath);
  return imageFilePath;
}

Catatan

Spring Integration Framework memiliki implementasi pola pipa dan filter.

Langkah berikutnya

Anda mungkin menemukan sumber daya berikut berguna saat menerapkan pola ini:

Pola berikut mungkin juga relevan ketika Anda menerapkan pola ini:

  • Pola Claim-Check. Alur yang diimplementasikan menggunakan antrean mungkin tidak menahan item aktual yang dikirim melalui filter, tetapi sebaliknya pointer ke data yang perlu diproses. Contohnya menggunakan pemeriksaan klaim di Azure Queue Storage untuk gambar yang disimpan di Azure Blob Storage.
  • Pola Konsumen yang Bersaing. Alur dapat berisi beberapa instans dari satu atau beberapa filter. Pendekatan ini berguna untuk menjalankan instans paralel filter lambat. Ini memungkinkan sistem untuk menyebarkan beban dan meningkatkan throughput. Setiap instans filter bersaing untuk input dengan instans lain, tetapi dua instans filter seharusnya tidak dapat memproses data yang sama. Artikel ini menjelaskan pendekatannya.
  • Pola Konsolidasi Sumber Daya Komputasi. Mungkin untuk mengelompokkan filter yang harus diskalakan bersama-sama ke dalam satu proses. Artikel ini memberikan informasi selengkapnya tentang manfaat dan tradeoff strategi ini.
  • Pola Transaksi Kompensasi. Anda dapat menerapkan filter sebagai operasi yang dapat dibalik, atau yang memiliki operasi kompensasi yang memulihkan status ke versi sebelumnya jika ada kegagalan. Artikel ini menjelaskan bagaimana Anda dapat menerapkan pola ini untuk mempertahankan atau mencapai konsistensi akhir.
  • Pipa dan Filter - Pola Integrasi Perusahaan.