Lanskap actor

Apache Pekko sebagai kelanjutan ekosistem actor setelah perubahan lisensi Akka tahun 2022.

Daftar isi

Topik ini memberikan gambaran menyeluruh tentang model actor, yaitu state yang terenkapsulasi, pesan asinkron, dan ketiadaan memori bersama, kemudian menjelaskan sejarah singkat Akka serta perubahan lisensinya pada tahun 2022. Apache Pekko diperkenalkan sebagai fork komunitas yang memelihara ekosistem actor di JVM di bawah Apache License 2.0. Sebuah contoh Pekko typed yang kecil namun utuh memperlihatkan cara actor merespons permintaan, dan bagian akhir memberikan panduan kapan actor perlu dipertimbangkan dibandingkan dengan Future.

Model actor dalam satu gambar besar#

Model actor (actor model) adalah skema konkurensi yang membagi program menjadi unit-unit kecil bernama actor. Setiap actor memiliki state sendiri yang terenkapsulasi, artinya tidak ada bagian program lain yang dapat membaca atau mengubah state itu secara langsung. Satu-satunya jalan masuk adalah pesan (message) yang dikirim secara asinkron, yakni pengirim melanjutkan pekerjaannya tanpa menunggu pesan diterima.

Setiap actor memproses pesan dari kotak suratnya (mailbox) satu per satu. Sifat ini membuat actor berperilaku layaknya unit pemrosesan tunggal, sehingga state internalnya tidak pernah diakses bersamaan oleh dua alur eksekusi. Karena tidak ada memori bersama, tidak ada pula kunci (lock), variabel atomik, atau mekanisme pengamanan lain yang biasanya mengiringi pemrograman alur bersama (shared-memory threading). Kesalahan seperti balapan data (data race) dan kebuntuan (deadlock) tidak muncul pada bagian yang dikelola oleh model ini.

Actor juga dapat mengubah perilakunya (behavior) di antara pesan. Perilaku adalah nilai yang menentukan cara actor merespons pesan berikutnya, sehingga mesin status (state machine) dapat dimodelkan secara alami, misalnya actor yang berpindah dari keadaan menunggu ke keadaan memproses. Seluruh pustaka actor di JVM, termasuk Akka dan Apache Pekko, dibangun di atas prinsip-prinsip yang sama.

Dari Akka ke Apache Pekko#

Akka adalah pustaka model actor untuk JVM yang dirilis pada tahun 2009 dan menjadi implementasi yang paling luas digunakan di ekosistem Scala. Ribuan aplikasi produksi mengandalkannya, dan dokumentasinya menjadi rujukan baku untuk pola-pola actor. Selama lebih dari satu dekade, lisensinya adalah Apache License 2.0, sehingga penggunaan komersial maupun nonkomersial berjalan tanpa hambatan.

Situasi berubah pada September 2022 ketika Lightbend selaku pengelola proyek mengubah lisensi Akka menjadi Business Source License 1.1 (BSL) untuk versi 2.7 ke atas. Lisensi BSL memperbolehkan penggunaan gratis untuk pengembangan, sedangkan penggunaan dalam produksi dikenai syarat, misalnya batas pendapatan tahunan perusahaan atau kewajiban membeli lisensi komersial. Kode sumber tetap terbuka untuk dilihat, tetapi proyek tersebut tidak lagi memenuhi definisi open source.

Kepastian lisensi itu mendorong lahirnya Apache Pekko, fork komunitas yang dibentuk dari basis kode Akka 2.6 dan ditempatkan di bawah naungan Apache Software Foundation. Pekko dilisensikan dengan Apache License 2.0 dan dipelihara oleh komunitas secara terbuka. API-nya meniru API Akka 2.6, sehingga kode yang sudah ada dapat dimigrasikan dengan mengganti namespace akka menjadi org.apache.pekko. Rilis 1.0.0 muncul pada pertengahan 2023, dan lini 1.1.x terus dikembangkan dengan dukungan lintas versi Scala, termasuk 2.13 dan 3.

Contoh kecil Pekko typed#

Varian typed dari Pekko menerapkan protokol pesan pada tingkat tipe data, yaitu setiap actor hanya menerima pesan dari satu tipe yang dideklarasikan secara eksplisit. Contoh berikut mendefinisikan actor penyapa dengan satu pesan bernama Sapa. Pesan itu membawa nama serta ActorRef balasan, dan program utama memperoleh jawaban melalui ask pattern.

scala penyapa.scala
//> using dep org.apache.pekko::pekko-actor-typed:1.1.4
//> using dep org.slf4j:slf4j-nop:2.0.17

import org.apache.pekko.actor.typed.{ActorRef, ActorSystem}
import org.apache.pekko.actor.typed.scaladsl.Behaviors
import org.apache.pekko.actor.typed.scaladsl.AskPattern._
import org.apache.pekko.util.Timeout

import scala.concurrent.Await
import scala.concurrent.duration._

object Penyapa:
  sealed trait Perintah
  final case class Sapa(nama: String, balasKe: ActorRef[String]) extends Perintah

  val penyapa: Behaviors.Receive[Perintah] =
    Behaviors.receiveMessage:
      case Sapa(nama, balasKe) =>
        balasKe ! s"Halo, $nama!"
        Behaviors.same

@main def lanskap(): Unit =
  val sistem = ActorSystem(Penyapa.penyapa, "sapaan")

  implicit val timeout: Timeout = Timeout(3.seconds)
  implicit val scheduler: org.apache.pekko.actor.typed.Scheduler = sistem.scheduler

  val jawaban1 = Await.result(sistem.ask(ref => Penyapa.Sapa("Dewi", ref)), 3.seconds)
  val jawaban2 = Await.result(sistem.ask(ref => Penyapa.Sapa("Ali", ref)), 3.seconds)
  println(jawaban1)
  println(jawaban2)

  sistem.terminate()
bash
scala-cli run penyapa.scala
text Keluaran
Halo, Dewi!
Halo, Ali!

Bagian object Penyapa mendefinisikan protokol pesan sebagai sealed trait Perintah, sehingga kompiler mengetahui seluruh jenis pesan yang mungkin diterima actor. Pesan Sapa menyertakan ActorRef[String] sebagai alamat balasan, dan actor mengirim jawabannya dengan operator !. Metode Behaviors.receiveMessage mengembalikan Behaviors.same, artinya actor tetap menggunakan perilaku yang sama untuk pesan berikutnya.

Pada program utama, ask pattern mengirim pesan dan mengembalikan Future[String], kemudian Await.result menunggu jawabannya hingga batas waktu tiga detik. Dua panggilan ask dieksekusi secara berurutan dan masing-masing ditunggu sebelum dicetak, sehingga urutan keluaran terjamin. Pemanggilan sistem.terminate() menghentikan sistem actor agar proses program berakhir dengan rapi.

Kapan actor dipertimbangkan dibanding Future#

Future mewakili satu nilai hasil yang akan tersedia di kemudian hari, sehingga cocok untuk operasi asinkron yang dieksekusi satu kali, selesai, lalu hasilnya dikonsumsi. Actor berada pada kategori lain, yaitu entitas berumur panjang yang terus menerima pesan sepanjang hidupnya. Actor menyimpan state di antara pesan-pesan itu dan memperbaruinya secara aman karena hanya satu pesan yang diproses pada satu waktu.

AspekFutureActor
Umur hidupsatu kali eksekusi, lalu hasilnya dikonsumsiberumur panjang, merespons pesan terus-menerus
Statetidak menyimpan state di antara operasistate terenkapsulasi dan diperbarui melalui pesan
Komposisitransformasi dan for-comprehensionprotokol pesan dan pengiriman di antara actor
Kesesuaianparalelisasi perhitungan, pemanggilan layananentitas yang menyimpan state, antrean, koordinasi banyak pesan

Aturan praktisnya sederhana. Gunakan Future untuk alur asinkron sederhana yang tidak menyimpan state, dan pertimbangkan actor apabila state bersama yang harus dilindungi semakin kompleks atau jumlah entitas konkuren yang berumur panjang semakin besar. Banyak aplikasi nyata mengombinasikan keduanya, yaitu Future untuk perhitungan dan pemanggilan layanan, sedangkan actor untuk menyimpan state dan mengatur koordinasi.

Ringkasan#

  • Model actor mengenkapsulasi state dalam unit yang memproses pesan asinkron satu per satu, sehingga tidak ada memori bersama, tidak ada kunci, dan balapan data tidak muncul.
  • Akka mengubah lisensinya menjadi BSL 1.1 pada tahun 2022, dan Apache Pekko melanjutkan ekosistem tersebut sebagai fork komunitas di bawah Apache License 2.0 dengan API yang setara.
  • Pekko typed mendeklarasikan protokol pesan bertipe, dan ask pattern mengembalikan Future yang dapat ditunggu dengan Await untuk memperoleh hasil secara deterministik.
  • Future cocok untuk operasi asinkron sekali eksekusi, sedangkan actor cocok untuk entitas berumur panjang yang menyimpan state.

Referensi#