Bagaimana cara spray.routing.HttpService mengirimkan permintaan?

Penafian: Saya tidak memiliki pengalaman scala untuk saat ini, jadi pertanyaan saya berhubungan dengan hal yang sangat mendasar.

Perhatikan contoh berikut (mungkin tidak lengkap):

import akka.actor.{ActorSystem, Props}
import akka.io.IO
import spray.can.Http
import akka.pattern.ask
import akka.util.Timeout
import scala.concurrent.duration._
import akka.actor.Actor
import spray.routing._
import spray.http._

object Boot extends App {
  implicit val system = ActorSystem("my-actor-system")
  val service = system.actorOf(Props[MyActor], "my")
  implicit val timeout = Timeout(5.seconds)
  IO(Http) ? Http.Bind(service, interface = "localhost", port = 8080)
}

class MyActor extends Actor with MyService {
  def actorRefFactory = context

  def receive = runRoute(myRoute)
}

trait MyService extends HttpService {
  val myRoute =
    path("my") {
      post {
        complete {
          "PONG"
        }
      }
    }
}

Pertanyaan saya adalah: apa yang sebenarnya terjadi ketika kontrol mencapai complete blok? Pertanyaannya sepertinya terlalu umum, jadi izinkan saya membaginya.

  1. Saya melihat penciptaan satu aktor dalam contoh. Apakah ini berarti aplikasinya single-threaded dan hanya menggunakan satu inti CPU?
  2. Apa yang terjadi jika saya memblokir panggilan di dalam complete?
  3. Jika hal. 1 benar dan hal. 2 akan diblokir, bagaimana cara mengirimkan permintaan untuk menggunakan semua CPU? Saya melihat dua cara: aktor per permintaan dan aktor per koneksi. Yang kedua tampaknya masuk akal, tetapi saya tidak dapat menemukan cara melakukannya menggunakan perpustakaan semprotan.
  4. Jika pertanyaan sebelumnya tidak relevan, apakah arahan detach akan berlaku? Dan bagaimana dengan meneruskan fungsi yang mengembalikan arahan Future ke complete? Apa perbedaan antara fungsi detach dan passing yang mengembalikan Masa Depan?
  5. Apa cara yang tepat untuk mengonfigurasi jumlah utas yang berfungsi dan menyeimbangkan permintaan/koneksi?

Akan lebih bagus jika Anda menunjukkan penjelasannya kepada saya di dokumentasi resmi. Ini sangat luas dan saya yakin saya melewatkan sesuatu.

Terima kasih.


person Unforgiven    schedule 16.08.2015    source sumber


Jawaban (1)


Itu dijawab di sini oleh Mathias - salah satu penulis Spray . Menyalin balasannya untuk referensi:

Pada akhirnya satu-satunya hal yang benar-benar melengkapi permintaan tersebut adalah panggilan ke requestContext.complete. Oleh karena itu, tidak masalah dari konteks thread atau Aktor mana panggilan ini dibuat. Yang penting adalah hal itu terjadi dalam periode "batas waktu permintaan" yang dikonfigurasi. Tentu saja Anda dapat mengeluarkan panggilan ini sendiri dengan cara apa pun, tetapi spray memberi Anda sejumlah konstruksi yang telah ditentukan sebelumnya yang mungkin lebih sesuai dengan arsitektur Anda daripada meneruskan RequestContext yang sebenarnya. Terutama ini adalah:

  1. Direktif complete, yang hanya menyediakan sedikit gula di atas literal fungsi ctx => ctx.complete(…) "mentah".
  2. Marshaller Masa Depan, yang memanggil ctx.complete dari penangan future.onComplete.
  3. Direktif produce, yang mengekstrak fungsi T => Unit yang nantinya dapat digunakan untuk menyelesaikan permintaan dengan instance tipe kustom.

Secara arsitektural, dalam banyak kasus, sebaiknya lapisan API tidak "bocor" ke dalam inti aplikasi Anda. Yaitu. aplikasi seharusnya tidak mengetahui apa pun tentang lapisan API atau HTTP. Seharusnya hanya menangani objek model domainnya sendiri. Oleh karena itu meneruskan RequestContext langsung ke inti aplikasi sebagian besar bukanlah solusi terbaik.

Beralih ke "bertanya" dan mengandalkan Marshaller Masa Depan adalah alternatif yang jelas, dapat dipahami dengan baik, dan cukup mudah. Muncul dengan kelemahan (kecil) yaitu permintaan dilengkapi dengan pemeriksaan batas waktu wajib itu sendiri yang secara logis tidak diperlukan (karena lapisan kaleng semprot sudah menangani batas waktu permintaan). Batas waktu permintaan diperlukan karena alasan teknis (sehingga PromiseActorRef yang mendasarinya dapat dibersihkan jika balasan yang diharapkan tidak pernah datang).

Alternatif lain untuk meneruskan RequestContext adalah direktif produce (misalnya produce(instanceOf[Foo]) { completer => …). Ini mengekstrak fungsi yang dapat Anda teruskan ke inti aplikasi. Saat logika inti Anda memanggil complete(foo) logika penyelesaian dijalankan dan permintaan selesai. Dengan demikian, inti aplikasi tetap terpisah dari lapisan API dan overhead menjadi minimal. Kelemahan dari pendekatan ini ada dua: pertama, fungsi pelengkap tidak dapat diserialkan, sehingga Anda tidak dapat menggunakan pendekatan ini melintasi batas-batas JVM. Dan kedua, logika penyelesaian sekarang berjalan langsung dalam konteks aktor inti aplikasi, yang mungkin mengubah perilaku runtime dengan cara yang tidak diinginkan jika Marshaller[Foo] harus melakukan tugas-tugas yang tidak sepele.

Alternatif ketiga adalah memunculkan aktor per permintaan di lapisan API dan memintanya menangani respons yang datang dari inti aplikasi. Maka Anda tidak perlu menggunakan permintaan. Namun, Anda berakhir dengan masalah yang sama dengan yang dimiliki oleh PromiseActorRef yang mendasari sebuah pertanyaan: bagaimana cara membersihkan jika tidak ada respons yang kembali dari inti aplikasi? Dengan aktor permintaan ulang, Anda memiliki kebebasan penuh untuk menerapkan solusi untuk pertanyaan ini. Namun, jika Anda memutuskan untuk mengandalkan waktu tunggu (misalnya melalui context.setReceiveTimeout), manfaat dari "bertanya" mungkin tidak ada.

Solusi mana yang paling sesuai dengan arsitektur Anda, Anda perlu memutuskan sendiri. Namun, seperti yang dapat saya tunjukkan, Anda memiliki beberapa alternatif untuk dipilih.

Untuk menjawab beberapa pertanyaan spesifik Anda: Hanya ada satu aktor/penangan yang melayani rute sehingga jika Anda memblokirnya, Semprotan akan memblokir. Ini berarti Anda ingin segera menyelesaikan rute atau mengirimkan pekerjaan menggunakan salah satu dari 3 opsi di atas.

Ada banyak contoh di web untuk 3 opsi ini. Cara termudah adalah dengan membungkus kode Anda dalam Future. Periksa juga opsi/contoh "aktor per permintaan". Pada akhirnya arsitektur Anda akan menentukan cara yang paling tepat.

Terakhir, Spray berjalan di atas Akka, sehingga semua konfigurasi Akka tetap berlaku. Lihat HOCON reference.conf dan application.conf untuk pengaturan threading Aktor.

person yǝsʞǝla    schedule 17.08.2015