Pengantar Akka HTTP

1. Ikhtisar

Dalam tutorial ini, dengan bantuan model Akka's Actor & Stream, kita akan belajar cara mengatur Akka untuk membuat API HTTP yang menyediakan operasi CRUD dasar.

2. Ketergantungan Maven

Untuk memulai, mari kita lihat dependensi yang diperlukan untuk mulai bekerja dengan Akka HTTP:

 com.typesafe.akka akka-http_2.12 10.0.11   com.typesafe.akka akka-stream_2.12 2.5.11   com.typesafe.akka akka-http-jackson_2.12 10.0.11   com.typesafe.akka akka-http-testkit_2.12 10.0.11 test 

Kami tentu saja dapat menemukan versi terbaru perpustakaan Akka ini di Maven Central.

3. Membuat Aktor

Sebagai contoh, kita akan membangun HTTP API yang memungkinkan kita mengelola sumber daya pengguna. API akan mendukung dua operasi:

  • membuat pengguna baru
  • memuat pengguna yang sudah ada

Sebelum kami dapat menyediakan API HTTP, kami perlu mengimplementasikan aktor yang menyediakan operasi yang kami butuhkan:

class UserActor extends AbstractActor { private UserService userService = new UserService(); static Props props() { return Props.create(UserActor.class); } @Override public Receive createReceive() { return receiveBuilder() .match(CreateUserMessage.class, handleCreateUser()) .match(GetUserMessage.class, handleGetUser()) .build(); } private FI.UnitApply handleCreateUser() { return createUserMessage -> { userService.createUser(createUserMessage.getUser()); sender() .tell(new ActionPerformed( String.format("User %s created.", createUserMessage.getUser().getName())), getSelf()); }; } private FI.UnitApply handleGetUser() { return getUserMessage -> { sender().tell(userService.getUser(getUserMessage.getUserId()), getSelf()); }; } }

Pada dasarnya, kami memperluas kelas AbstractActor dan mengimplementasikan metode createReceive () .

Dalam createReceive () , kami memetakan jenis pesan masuk ke metode yang menangani pesan dari jenisnya masing-masing.

Jenis pesan adalah kelas kontainer serializable sederhana dengan beberapa bidang yang menjelaskan operasi tertentu . GetUserMessage dan memiliki satu kolom userId untuk mengidentifikasi pengguna yang akan dimuat. CreateUserMessage berisi objek Pengguna dengan data pengguna yang kita butuhkan untuk membuat pengguna baru.

Nanti, kita akan melihat bagaimana menerjemahkan permintaan HTTP yang masuk ke dalam pesan-pesan ini.

Pada akhirnya, kami mendelegasikan semua pesan ke instance UserService , yang menyediakan logika bisnis yang diperlukan untuk mengelola objek pengguna yang persisten.

Juga, perhatikan metode props () . Meskipun metode props () tidak diperlukan untuk memperluas AbstractActor , ini akan berguna nanti saat membuat ActorSystem .

Untuk diskusi yang lebih mendalam tentang aktor, lihat pengantar Akka Actors kami.

4. Mendefinisikan Rute HTTP

Memiliki aktor yang melakukan pekerjaan sebenarnya untuk kami, yang harus kami lakukan hanyalah menyediakan API HTTP yang mendelegasikan permintaan HTTP yang masuk ke aktor kami.

Akka menggunakan konsep rute untuk menggambarkan API HTTP. Untuk setiap operasi, kami membutuhkan rute.

Untuk membuat server HTTP, kami memperluas kelas kerangka kerja HttpApp dan mengimplementasikan metode rute :

class UserServer extends HttpApp { private final ActorRef userActor; Timeout timeout = new Timeout(Duration.create(5, TimeUnit.SECONDS)); UserServer(ActorRef userActor) { this.userActor = userActor; } @Override public Route routes() { return path("users", this::postUser) .orElse(path(segment("users").slash(longSegment()), id -> route(getUser(id)))); } private Route getUser(Long id) { return get(() -> { CompletionStage
    
      user = PatternsCS.ask(userActor, new GetUserMessage(id), timeout) .thenApply(obj -> (Optional) obj); return onSuccess(() -> user, performed -> { if (performed.isPresent()) return complete(StatusCodes.OK, performed.get(), Jackson.marshaller()); else return complete(StatusCodes.NOT_FOUND); }); }); } private Route postUser() { return route(post(() -> entity(Jackson.unmarshaller(User.class), user -> { CompletionStage userCreated = PatternsCS.ask(userActor, new CreateUserMessage(user), timeout) .thenApply(obj -> (ActionPerformed) obj); return onSuccess(() -> userCreated, performed -> { return complete(StatusCodes.CREATED, performed, Jackson.marshaller()); }); }))); } } 
    

Sekarang, ada cukup banyak boilerplate di sini, tetapi perhatikan bahwa kami mengikuti pola yang sama seperti sebelum operasi pemetaan, kali ini sebagai rute. Mari kita uraikan sedikit.

Di dalam getUser () , kita cukup membungkus id pengguna yang masuk dalam pesan berjenis GetUserMessage dan meneruskan pesan itu ke userActor kita .

Setelah aktor memproses pesan, pengendali onSuccess dipanggil, di mana kita menyelesaikan permintaan HTTP dengan mengirimkan respons dengan status HTTP tertentu dan badan JSON tertentu. Kami menggunakan Jackson marshaller untuk membuat serial jawaban yang diberikan oleh aktor menjadi string JSON.

Dalam postUser () , kami melakukan sesuatu dengan sedikit berbeda, karena kami mengharapkan badan JSON dalam permintaan HTTP. Kami menggunakan metode entity () untuk memetakan badan JSON yang masuk ke dalam objek User sebelum menggabungkannya ke dalam CreateUserMessage dan meneruskannya ke aktor kami. Sekali lagi, kami menggunakan Jackson untuk memetakan antara Java dan JSON dan sebaliknya.

Karena HttpApp mengharapkan kita menyediakan satu objek Route , kita menggabungkan kedua rute ke satu dalam metode routes . Di sini, kami menggunakan direktif jalur untuk akhirnya menyediakan jalur URL di mana API kami harus tersedia.

Kami mengikat rute yang disediakan oleh postUser () ke jalur / pengguna . Jika permintaan yang masuk bukan merupakan permintaan POST, Akka akan secara otomatis masuk ke cabang orElse dan mengharapkan jalur menjadi / users / dan metode HTTP menjadi GET.

Jika metode HTTP adalah GET, permintaan akan diteruskan ke rute getUser () . Jika pengguna tidak ada, Akka akan mengembalikan status HTTP 404 (Tidak Ditemukan). Jika metodenya bukan POST atau GET, Akka akan mengembalikan status HTTP 405 (Metode Tidak Diizinkan).

Untuk informasi lebih lanjut tentang cara menentukan rute HTTP dengan Akka, lihat dokumen Akka.

5. Memulai Server

Setelah kita membuat implementasi HttpApp seperti di atas, kita dapat memulai server HTTP kita dengan beberapa baris kode:

public static void main(String[] args) throws Exception { ActorSystem system = ActorSystem.create("userServer"); ActorRef userActor = system.actorOf(UserActor.props(), "userActor"); UserServer server = new UserServer(userActor); server.startServer("localhost", 8080, system); }

Kami cukup membuat ActorSystem dengan aktor tunggal tipe UserActor dan memulai server di localhost .

6. Kesimpulan

Dalam artikel ini, kita telah mempelajari dasar-dasar HTTP Akka dengan contoh yang menunjukkan cara menyiapkan server HTTP dan mengekspos titik akhir untuk membuat dan memuat sumber daya, mirip dengan REST API.

Seperti biasa, kode sumber yang disajikan di sini dapat ditemukan di GitHub.