Prinsip dan Corak Reka Bentuk untuk Aplikasi Serentak

1. Gambaran keseluruhan

Dalam tutorial ini, kita akan membincangkan beberapa prinsip reka bentuk dan corak yang telah dibuat dari masa ke masa untuk membina aplikasi yang sangat serentak.

Walau bagaimanapun, perlu diperhatikan bahawa merancang aplikasi serentak adalah topik yang luas dan kompleks, dan oleh itu tidak ada tutorial yang boleh menyatakan lengkap dalam rawatannya. Apa yang akan kita bahas di sini adalah beberapa helah popular yang sering digunakan!

2. Asas Bersamaan

Sebelum kita melangkah lebih jauh, mari luangkan sedikit masa untuk memahami asas-asasnya. Sebagai permulaan, kita mesti menjelaskan pemahaman kita tentang apa yang kita sebut sebagai program serentak. Kami merujuk kepada program yang serentak jika banyak pengiraan berlaku pada masa yang sama .

Sekarang, perhatikan bahawa kami telah menyebutkan pengiraan yang berlaku pada masa yang sama - iaitu, mereka sedang berjalan pada masa yang sama. Walau bagaimanapun, mereka mungkin atau mungkin tidak dijalankan secara serentak. Penting untuk memahami perbezaannya kerana pengiraan secara serentak disebut sebagai selari .

2.1. Bagaimana Membuat Modul Serentak?

Penting untuk memahami bagaimana kita dapat membuat modul serentak. Terdapat banyak pilihan, tetapi kami akan memberi tumpuan kepada dua pilihan popular di sini:

  • Proses : Proses adalah contoh program yang dijalankan yang diasingkan dari proses lain dalam mesin yang sama. Setiap proses pada mesin mempunyai masa dan ruang tersendiri. Oleh itu, biasanya tidak mungkin untuk berkongsi memori antara proses, dan mereka mesti berkomunikasi dengan menyampaikan mesej.
  • Thread : Sebaliknya, benang hanyalah segmen proses . Terdapat banyak utas dalam program yang berkongsi ruang memori yang sama. Walau bagaimanapun, setiap utas mempunyai timbunan dan keutamaan yang unik. Benang boleh asli (dijadualkan secara asli oleh sistem operasi) atau hijau (dijadualkan oleh perpustakaan runtime).

2.2. Bagaimana Modul Serentak Berinteraksi?

Sangat sesuai jika modul serentak tidak perlu berkomunikasi, tetapi selalunya tidak demikian. Ini menimbulkan dua model pengaturcaraan serentak:

  • Memori Bersama : Dalam model ini, modul serentak berinteraksi dengan membaca dan menulis objek yang dikongsi dalam memori . Ini sering menyebabkan interleaving pengiraan serentak, menyebabkan keadaan perlumbaan. Oleh itu, ia boleh secara tidak ditentukan ke arah keadaan yang salah.
  • Penghantaran Mesej : Dalam model ini, modul serentak berinteraksi dengan menyampaikan mesej antara satu sama lain melalui saluran komunikasi . Di sini, setiap modul memproses mesej masuk secara berurutan. Oleh kerana tidak ada keadaan bersama, lebih mudah untuk diprogramkan, tetapi ini masih tidak bebas dari keadaan perlumbaan!

2.3. Bagaimana Modul Serentak Dilaksanakan?

Sudah lama sejak Moore's Law menghantam dinding sehubungan dengan kelajuan jam prosesor. Sebaliknya, sejak kita mesti berkembang, kita sudah mula memasukkan banyak pemproses ke cip yang sama, yang sering disebut pemproses multikore. Tetapi tetap tidak biasa untuk mendengar mengenai pemproses yang mempunyai lebih daripada 32 teras.

Sekarang, kita tahu bahawa satu teras hanya dapat menjalankan satu utas, atau satu set arahan, dalam satu masa. Walau bagaimanapun, jumlah proses dan utas masing-masing boleh mencapai ratusan dan ribuan. Jadi, bagaimana ia benar-benar berfungsi? Di sinilah sistem operasi mensimulasikan kesesuaian untuk kita . Sistem operasi mencapai ini dengan pemotongan masa - yang secara berkesan bermaksud bahawa pemproses bertukar antara benang dengan kerap, tidak dapat diramalkan, dan tidak ditentukan.

3. Masalah dalam Pengaturcaraan Serentak

Semasa kita membincangkan prinsip dan corak untuk merancang aplikasi serentak, adalah lebih baik untuk memahami terlebih dahulu apa masalah khasnya.

Untuk sebahagian besar, pengalaman kami dengan pengaturcaraan serentak melibatkan penggunaan benang asli dengan memori bersama . Oleh itu, kami akan memberi tumpuan kepada beberapa masalah umum yang timbul daripadanya:

  • Saling Pengecualian (Primer Sinkronisasi) : Benang interleaving perlu mempunyai akses eksklusif ke keadaan bersama atau memori untuk memastikan kebenaran program . Penyegerakan sumber bersama adalah kaedah yang popular untuk mencapai pengecualian bersama. Terdapat beberapa primitif penyegerakan yang tersedia untuk digunakan - sebagai contoh, kunci, monitor, semaphore, atau mutex. Walau bagaimanapun, pengaturcaraan untuk pengecualian bersama cenderung kepada ralat dan sering menyebabkan kesulitan prestasi. Terdapat beberapa isu yang dibincangkan dengan baik seperti ini seperti kebuntuan dan kunci hidup.
  • Pengalihan Konteks (Benang Berat) : Setiap sistem operasi mempunyai sokongan asli, walaupun bervariasi, untuk modul serentak seperti proses dan utas. Seperti yang telah dibahas, salah satu perkhidmatan asas yang disediakan oleh sistem operasi adalah menjadwalkan utas untuk dilaksanakan pada sejumlah prosesor melalui pemotongan waktu. Sekarang, ini secara berkesan bermaksud bahawa utas sering bertukar antara keadaan yang berbeza . Dalam prosesnya, keadaan semasa mereka perlu disimpan dan disambung semula. Ini adalah aktiviti yang memakan masa yang secara langsung mempengaruhi keseluruhan hasil.

4. Pola Reka Bentuk untuk Kesesuaian Tinggi

Sekarang, setelah kita memahami asas-asas pengaturcaraan serentak dan masalah umum di dalamnya, inilah masanya untuk memahami beberapa corak umum untuk mengelakkan masalah ini. Kita mesti mengulangi bahawa pengaturcaraan serentak adalah tugas yang sukar yang memerlukan banyak pengalaman. Oleh itu, mengikuti beberapa corak yang ditetapkan dapat menjadikan tugas lebih mudah.

4.1. Kesesuaian Berbasis Pelakon

Reka bentuk pertama yang akan kita bincangkan berkenaan dengan pengaturcaraan serentak disebut Model Pelakon. Ini adalah model matematik pengiraan serentak yang pada dasarnya memperlakukan semuanya sebagai pelakon . Pelakon boleh menyampaikan mesej antara satu sama lain dan, sebagai tindak balas kepada mesej, dapat membuat keputusan tempatan. Ini pertama kali dicadangkan oleh Carl Hewitt dan telah mengilhami sejumlah bahasa pengaturcaraan.

Konstruksi utama Scala untuk pengaturcaraan serentak adalah pelakon. Pelakon adalah objek biasa di Scala yang boleh kita buat dengan memberi contoh kelas Pelakon . Tambahan pula, perpustakaan Scala Actors menyediakan banyak operasi pelakon yang berguna:

class myActor extends Actor { def act() { while(true) { receive { // Perform some action } } } }

Dalam contoh di atas, panggilan ke kaedah penerimaan di dalam gelung tak terhingga menangguhkan pelakon sehingga ada mesej yang diterima. Setelah tiba, mesej dikeluarkan dari kotak surat pelakon, dan tindakan yang diperlukan diambil.

Model pelakon menghilangkan salah satu masalah asas dengan pengaturcaraan serentak - memori bersama . Pelakon berkomunikasi melalui mesej, dan setiap pelakon memproses mesej dari peti mel eksklusifnya secara berurutan. Walau bagaimanapun, kami melaksanakan pelakon melalui kumpulan utas Dan kami telah melihat bahawa benang asli boleh menjadi berat dan, oleh itu, jumlahnya terhad.

Sudah tentu ada corak lain yang dapat membantu kita di sini - kita akan membahasnya nanti!

4.2. Kesesuaian Berasaskan Acara

Reka bentuk berdasarkan acara secara terang-terangan menangani masalah bahawa benang asli mahal untuk bertelur dan beroperasi. Salah satu reka bentuk berasaskan acara adalah acara gelung. Gelung acara berfungsi dengan penyedia acara dan sekumpulan pengendali acara. Dalam penyediaan ini, gelung acara menyekat penyedia acara dan mengirimkan acara ke pengendali acara semasa ketibaan .

Pada dasarnya, gelung acara tidak lain adalah penghantar acara! Gelung acara itu sendiri dapat berjalan hanya pada satu utas asli. Jadi, apa yang sebenarnya berlaku dalam gelung acara? Mari lihat kod pseud dari gelung peristiwa yang sangat sederhana untuk contoh:

while(true) { events = getEvents(); for(e in events) processEvent(e); }

Pada dasarnya, semua gelung acara kami lakukan adalah untuk terus mencari peristiwa dan, apabila acara dijumpai, memprosesnya. Pendekatannya sangat mudah, tetapi ia memperoleh keuntungan dari reka bentuk berdasarkan acara.

Membangun aplikasi serentak menggunakan reka bentuk ini memberi lebih banyak kawalan pada aplikasi. Juga, ia menghilangkan beberapa masalah khas dari aplikasi multi-threaded - sebagai contoh, kebuntuan.

JavaScript melaksanakan gelung acara untuk menawarkan pengaturcaraan tidak segerak . Ini mengekalkan timbunan panggilan untuk melacak semua fungsi yang akan dilaksanakan. Ini juga menjaga barisan acara untuk menghantar fungsi baru untuk diproses. Gelung acara sentiasa memeriksa timbunan panggilan dan menambah fungsi baru dari barisan acara. Semua panggilan async dihantar ke API web, biasanya disediakan oleh penyemak imbas.

Gelung peristiwa itu sendiri boleh berjalan dalam satu utas, tetapi API web menyediakan utas yang berasingan.

4.3. Algoritma Tidak Menyekat

Dalam algoritma tidak menyekat, penggantungan satu utas tidak membawa kepada penggantungan benang lain. Kami melihat bahawa kami hanya mempunyai sebilangan kecil utas asli dalam aplikasi kami. Sekarang, algoritma yang menyekat thread jelas menurunkan throughput dengan ketara dan menghalang kita untuk membina aplikasi yang serentak.

Algoritma tanpa penyekat selalu menggunakan primitif atom perbandingan-dan-pertukaran yang disediakan oleh perkakasan yang mendasari . Ini bermaksud bahawa perkakasan akan membandingkan kandungan lokasi memori dengan nilai yang diberikan, dan hanya jika ia sama, ia akan mengemas kini nilainya ke nilai yang baru. Ini mungkin kelihatan mudah, tetapi secara berkesan memberikan kita operasi atom yang sebaliknya memerlukan penyegerakan.

Ini bermaksud bahawa kita harus menulis struktur data dan perpustakaan baru yang menggunakan operasi atom ini. Ini telah memberikan kami banyak pelaksanaan tanpa menunggu dan bebas kunci dalam beberapa bahasa. Java mempunyai beberapa struktur data yang tidak menyekat seperti AtomicBoolean , AtomicInteger , AtomicLong , dan AtomicReference .

Pertimbangkan aplikasi di mana beberapa utas cuba mengakses kod yang sama:

boolean open = false; if(!open) { // Do Something open=false; }

Jelasnya, kod di atas tidak selamat dari benang, dan tingkah lakunya di persekitaran berbilang benang tidak dapat diramalkan. Pilihan kami di sini adalah sama ada untuk menyegerakkan kod ini dengan kunci atau menggunakan operasi atom:

AtomicBoolean open = new AtomicBoolean(false); if(open.compareAndSet(false, true) { // Do Something }

Seperti yang dapat kita lihat, menggunakan struktur data yang tidak menyekat seperti AtomicBoolean membantu kita menulis kod yang selamat tanpa benang tanpa melibatkan kekurangan kunci!

5. Sokongan dalam Bahasa Pengaturcaraan

Kami telah melihat bahawa terdapat pelbagai cara untuk membina modul serentak. Walaupun bahasa pengaturcaraan membuat perbezaan, kebanyakannya bagaimana sistem operasi yang mendasari menyokong konsep tersebut. Namun, kerana serentak berdasarkan utas yang disokong oleh benang asli memukul dinding baru sehubungan dengan skalabilitas, kami selalu memerlukan pilihan baru.

Melaksanakan beberapa amalan reka bentuk yang telah kita bincangkan di bahagian terakhir terbukti berkesan. Walau bagaimanapun, kita mesti ingat bahawa program ini menyukarkan pengaturcaraan. Apa yang benar-benar kita perlukan adalah sesuatu yang memberikan kekuatan serentak berdasarkan utas tanpa kesan yang tidak diingini yang dibawa.

Satu penyelesaian yang ada pada kami adalah benang hijau. Benang hijau adalah utas yang dijadualkan oleh perpustakaan runtime dan bukannya dijadualkan secara asli oleh sistem operasi yang mendasari. Walaupun ini tidak menghilangkan semua masalah dalam konkurensi berdasarkan utas, ini pasti dapat memberi kita prestasi yang lebih baik dalam beberapa kes.

Sekarang, tidak sepele untuk menggunakan benang hijau melainkan bahasa pengaturcaraan yang kita pilih untuk menggunakannya menyokongnya. Tidak setiap bahasa pengaturcaraan mempunyai sokongan terbina dalam ini. Juga, apa yang kita sebut benang hijau secara longgar dapat dilaksanakan dengan cara yang sangat unik dengan bahasa pengaturcaraan yang berbeza. Mari lihat beberapa pilihan ini yang tersedia untuk kita.

5.1. Goroutine di Go

Goroutine dalam bahasa pengaturcaraan Go adalah utas ringan. Mereka menawarkan fungsi atau kaedah yang dapat berjalan serentak dengan fungsi atau kaedah lain. Goroutine sangat murah kerana hanya menempati beberapa kilobyte dalam ukuran timbunan .

Yang paling penting, goroutine digandakan dengan bilangan benang asli yang lebih sedikit. Lebih-lebih lagi, goroutine berkomunikasi antara satu sama lain menggunakan saluran, sehingga mengelakkan akses ke memori bersama. Kami mendapat hampir semua yang kami perlukan, dan meneka apa - tanpa melakukan apa-apa!

5.2. Proses di Erlang

Di Erlang, setiap utas pelaksanaan disebut proses. Tetapi, ia tidak seperti proses yang telah kita bincangkan setakat ini! Proses Erlang ringan dengan jejak memori yang kecil dan cepat dibuat dan dilupuskan dengan overhead penjadualan yang rendah.

Di bawah kesudahannya, proses Erlang tidak lain adalah fungsi yang dijalankan oleh jadual waktu. Lebih-lebih lagi, proses Erlang tidak berkongsi data apa pun, dan mereka saling berkomunikasi dengan menyampaikan mesej. Inilah sebab mengapa kita memanggil "proses" ini!

5.3. Serat di Jawa (Proposal)

Kisah persamaan dengan Java telah menjadi evolusi berterusan. Java memang mempunyai sokongan untuk benang hijau, paling tidak untuk sistem operasi Solaris. Walau bagaimanapun, ini dihentikan kerana rintangan di luar skop tutorial ini.

Sejak itu, serentak di Jawa adalah mengenai benang asli dan cara bekerja dengan pintar! Tetapi untuk alasan yang jelas, kita mungkin akan segera mendapatkan abstraksi serentak baru di Jawa, yang disebut serat. Project Loom mengusulkan untuk memperkenalkan kesinambungan bersama dengan serat, yang dapat mengubah cara kita menulis aplikasi serentak di Java!

Ini hanyalah sekilas dari apa yang tersedia dalam bahasa pengaturcaraan yang berbeza. Terdapat cara yang jauh lebih menarik yang cuba ditangani oleh bahasa pengaturcaraan lain untuk bersamaan.

Lebih-lebih lagi, perlu diperhatikan bahawa gabungan corak reka bentuk yang dibincangkan di bahagian terakhir, bersama dengan sokongan bahasa pengaturcaraan untuk abstraksi seperti benang hijau, dapat menjadi sangat kuat ketika merancang aplikasi yang sangat serentak.

6. Aplikasi Serentak Tinggi

Aplikasi dunia nyata sering mempunyai pelbagai komponen yang saling berinteraksi melalui wayar. Kami biasanya mengaksesnya melalui internet, dan terdiri daripada beberapa perkhidmatan seperti perkhidmatan proksi, gerbang, perkhidmatan web, pangkalan data, perkhidmatan direktori, dan sistem fail.

Bagaimana kita memastikan kesesuaian yang tinggi dalam situasi seperti itu? Mari kita terokai beberapa lapisan ini dan pilihan yang kita ada untuk membina aplikasi yang sangat serentak.

Seperti yang telah kita lihat di bahagian sebelumnya, kunci untuk membangun aplikasi yang serasi dengan tinggi adalah dengan menggunakan beberapa konsep reka bentuk yang dibincangkan di sana. Kita perlu memilih perisian yang sesuai untuk pekerjaan - yang sudah merangkumi beberapa amalan ini.

6.1. Lapisan Web

Web biasanya merupakan lapisan pertama di mana permintaan pengguna tiba, dan penyediaan untuk persetujuan tinggi tidak dapat dielakkan di sini. Mari lihat apa beberapa pilihan:

  • Node (juga disebut NodeJS atau Node.js) adalah runtime JavaScript open-source open-platform yang dibina pada mesin JavaScript V8 Chrome. Node berfungsi dengan baik dalam mengendalikan operasi I / O tidak segerak. Sebab Node melakukannya dengan baik adalah kerana ia melaksanakan gelung peristiwa pada satu utas. Gelung peristiwa dengan bantuan panggilan balik menangani semua operasi penyekat seperti I / O secara tidak segerak.
  • nginx adalah pelayan web sumber terbuka yang biasa kita gunakan sebagai proksi terbalik antara penggunaannya yang lain. Alasan nginx memberikan kesesuaian yang tinggi adalah kerana ia menggunakan pendekatan yang tidak segerak, berdasarkan peristiwa. nginx beroperasi dengan proses induk dalam satu utas. Proses induk mengekalkan proses pekerja yang melakukan pemprosesan sebenar. Oleh itu, pekerja memproses setiap permintaan secara serentak.

6.2. Lapisan Aplikasi

Semasa merancang aplikasi, terdapat beberapa alat untuk membantu kami membina kesesuaian yang tinggi. Mari kita periksa beberapa perpustakaan dan kerangka kerja yang tersedia untuk kita:

  • Akka adalah toolkit yang ditulis dalam Scala untuk membina aplikasi yang sangat serentak dan diedarkan di JVM. Pendekatan Akka dalam menangani persetujuan adalah berdasarkan model pelakon yang telah kita bincangkan sebelumnya. Akka mencipta lapisan antara pelakon dan sistem yang mendasari. Kerangka ini menangani kerumitan dalam membuat dan menjadwalkan utas, menerima dan mengirim mesej.
  • Project Reactor adalah perpustakaan reaktif untuk membina aplikasi yang tidak menyekat pada JVM. Ia berdasarkan spesifikasi Aliran Reaktif dan berfokus pada pengurusan pesanan dan permintaan pesanan yang cekap (tekanan balik). Pengendali dan penjadual reaktor dapat mengekalkan kadar throughput yang tinggi untuk mesej. Beberapa kerangka kerja yang popular menyediakan pelaksanaan reaktor, termasuk Spring WebFlux dan RSocket.
  • Netty adalah kerangka aplikasi rangkaian yang tidak segerak, berdasarkan peristiwa. Kita boleh menggunakan Netty untuk mengembangkan pelayan dan klien protokol yang serentak. Netty memanfaatkan NIO, yang merupakan kumpulan API Java yang menawarkan pemindahan data asinkron melalui penyangga dan saluran. Ia menawarkan beberapa kelebihan seperti throughput yang lebih baik, latensi yang lebih rendah, penggunaan sumber yang lebih sedikit, dan meminimumkan salinan memori yang tidak diperlukan.

6.3. Lapisan Data

Akhirnya, tidak ada aplikasi yang lengkap tanpa datanya, dan data berasal dari penyimpanan berterusan. Apabila kita membincangkan persetujuan yang tinggi berkenaan dengan pangkalan data, kebanyakan tumpuan tetap pada keluarga NoSQL. Ini terutama disebabkan oleh skalabiliti linear yang dapat ditawarkan pangkalan data NoSQL tetapi sukar dicapai dalam varian relasional. Mari lihat dua alat popular untuk lapisan data:

  • Cassandra adalah pangkalan data diedarkan NoSQL sumber terbuka dan bebas yang menyediakan ketersediaan tinggi, skalabilitas tinggi, dan toleransi kesalahan pada perkakasan komoditi. Walau bagaimanapun, Cassandra tidak menyediakan transaksi ACID yang merangkumi beberapa jadual. Oleh itu, jika aplikasi kita tidak memerlukan ketekalan dan urus niaga yang kuat, kita dapat memanfaatkan operasi latensi rendah Cassandra.
  • Kafka adalah platform streaming yang diedarkan . Kafka menyimpan aliran rekod dalam kategori yang disebut topik. Ia dapat memberikan skalabiliti mendatar linier untuk kedua-dua pengeluar dan pengguna rekod sementara, pada masa yang sama, memberikan kebolehpercayaan dan ketahanan yang tinggi. Partisi, replika, dan broker adalah beberapa konsep asas di mana ia menyediakan konkurensi yang diedarkan secara besar-besaran.

6.4. Lapisan Cache

Tidak ada aplikasi web di dunia moden yang bertujuan untuk persetujuan tinggi yang mampu memukul pangkalan data setiap masa. Itu membuat kita memilih cache - lebih baik cache dalam memori yang dapat menyokong aplikasi kita yang serentak:

  • Hazelcast adalah kedai objek dan memori komputer yang diedarkan, mesra awan, dan komputer yang menyokong pelbagai struktur data seperti Peta , Set , Senarai , MultiMap , RingBuffer , dan HyperLogLog . Ia mempunyai replikasi bawaan dan menawarkan ketersediaan tinggi dan pembahagian automatik.
  • Redis adalah gedung struktur data dalam memori yang terutama kami gunakan sebagai cache . Ia menyediakan pangkalan data nilai kunci dalam memori dengan ketahanan pilihan. Struktur data yang disokong merangkumi rentetan, hash, senarai, dan set. Redis mempunyai replikasi bawaan dan menawarkan ketersediaan tinggi dan pembahagian automatik. Sekiranya kami tidak memerlukan kegigihan, Redis dapat menawarkan kami cache yang kaya dengan rangkaian, dalam memori dengan prestasi yang luar biasa.

Sudah tentu, kita hampir tidak menggaru permukaan apa yang ada untuk kita membuat aplikasi yang serentak. Penting untuk diperhatikan bahawa, lebih daripada perisian yang ada, keperluan kita harus membimbing kita untuk membuat reka bentuk yang sesuai. Sebilangan pilihan ini mungkin sesuai, sementara yang lain mungkin tidak sesuai.

Dan, jangan lupa bahawa terdapat banyak pilihan lain yang mungkin lebih sesuai dengan keperluan kita.

7. Kesimpulannya

Dalam artikel ini, kami membincangkan asas-asas pengaturcaraan serentak. Kami memahami beberapa aspek asas bersamaan dan masalah yang boleh ditimbulkannya. Selanjutnya, kami menjalani beberapa corak reka bentuk yang dapat membantu kami menghindari masalah khas dalam pengaturcaraan serentak.

Akhirnya, kami melalui beberapa kerangka kerja, perpustakaan, dan perisian yang tersedia untuk kami untuk membina aplikasi yang sangat serentak dari hujung ke hujung.