← На главную

Простейший REST-cервис на Scala и Finagle

Согласно официальному описанию, Finagle – это расширяемая RPC система для JVM, используемая для построения «сильно многопоточных» (high-concurrent, что бы это ни значило) сервисов. Finagle изначально был создан ребятами из Twitter, но сейчас успешно применяется и в Foursquare, Pinterest, SoundCloud, Tumblr, а также ряде других компаний. В этой заметке мы познакомимся с основами использования Finagle, создав с его помощью очень простой REST-сервис.

Содержимое build.sbt будет таким:

name := "finagle-example" version := "0.1" scalaVersion := "2.11.6" libraryDependencies ++= Seq( "com.twitter" %% "finagle-http" % "6.25.0" )

(Хорошие новости – после долгих мучений Finagle наконец-то стал совместим со Scala 2.11!)

Минимальное приложение, которое можно представить, будет выглядеть так:

import com.twitter.finagle._ import com.twitter.util._ import org.jboss.netty.handler.codec.http._ object FinagleExample extends App { val service = new Service[HttpRequest, HttpResponse] { def apply(req: HttpRequest): Future[HttpResponse] = Future.value(new DefaultHttpResponse( req.getProtocolVersion, HttpResponseStatus.OK)) } val server = Http.serve(":8080", service) Await.ready(server) }

В Finagle сервис – это функция, которая преобразует запрос, в данном случае HttpRequest, в футуру ответа, в данном случае Future[HttpResponse]. Это, если подумать, очень крутая мысль. С футурами мы с вами уже хорошо знакомы. Однако Finagle использует собственные футуры, не из пакета scala.concurrent. Так получилось по историческим причинам, так как до версии 2.10 (если не путаю) в стандартной библиотеке Scala никаких футур не было, а те, что добавили, были старательно слизаны с Finagle’овских. В будущем вроде как планируется перевести Finagle на футуры из пакета scala.concurrent, а пока футуры с легкостью конвертируются одни в другие через имплиситы и промисы. Да, как вы могли заметить по импортам, Finagle является очень тонкой оберткой над Netty (крутейшая вещь!), имеющей намного более приятный API, без ужасов вроде постоянных foo.close(), bar.release() и подобного.

Рассмотрим чуть более сложный пример:

package me.eax.finagle_example import com.twitter.finagle._ import com.twitter.util._ import org.jboss.netty.handler.codec.http._ import com.twitter.finagle.http.{Http => _, _} import scala.collection.concurrent.TrieMap import java.nio.charset.Charset object FinagleExample extends App { val service = new Service[HttpRequest, HttpResponse] { val kv = TrieMap.empty[String, String] def apply(req: HttpRequest): Future[HttpResponse] = { Future { val resp = { Response(req.getProtocolVersion, HttpResponseStatus.OK) } val key = req.getUri req.getMethod match { case HttpMethod.GET => kv.get(key) match { case None => resp.setStatus(HttpResponseStatus.NOT_FOUND) case Some(value) => resp.setContentString(value) } case HttpMethod.POST => val value = { req.getContent.toString(Charset.forName("UTF-8")) } kv.update(key, value) case HttpMethod.DELETE => kv.remove(key) case _ => resp.setStatus(HttpResponseStatus.BAD_REQUEST) } resp } } } val server = Http.serve(":8080", service) Await.ready(server) }

Здесь мы имеем in-memory KV базу данных с как бы REST-интерфейсом:

$ curl localhost:8080/test -D - -o - HTTP/1.1 404 Not Found Content-Length: 0 $ curl -XPOST -d 'test-value' localhost:8080/test -D - -o - HTTP/1.1 200 OK Content-Length: 0 $ curl localhost:8080/test -D - -o - && echo HTTP/1.1 200 OK Content-Length: 10 test-value $ curl -XDELETE localhost:8080/test -D - -o - HTTP/1.1 200 OK Content-Length: 0 $ curl localhost:8080/test -D - -o - HTTP/1.1 404 Not Found Content-Length: 0 $ curl -XPUT localhost:8080/test -D - -o - HTTP/1.1 400 Bad Request Content-Length: 0

Код, как мне кажется, совершенно тривиальный и в дополнительных пояснениях совершенно не нуждается. Вообще, Finagle – исключительно простой и приятный в использовании фреймворк, этим многих и подкупает. Кстати, размер сервиса после assembly получается 16 Мб, не так уж много.

Есть веские основания полагать, что Finagle также можно легко использовать из Java и Kotlin. Если желаете, можете считать проверку этого утверждения своим домашним заданием!

Ссылки по теме:

А используете ли вы Finagle и если да, то с какими дополнительными пакетами и в каких проектах?

Дополнение: Akka HTTP на примере звонков и посылки SMS через Plivo