Согласно официальному описанию, 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

EnglishRussianUkrainian