git clone https://github.com/finagle/finagle-example-name-finder.git
What you need
Cloning the project
git clone https://github.com/finagle/finagle-example-name-finder.git
Downloading the models (more about this soon)
cd finagle-example-name-finder
sh download-models.sh
Note: we’ll use SBT today
travis@sidmouth finagle-example-name-finder(master)$ ./sbt
[info] ...
> console
[info] Starting scala interpreter...
[info]
Welcome to Scala version 2.10.4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_67).
Type in expressions to have them evaluated.
Type :help for more information.
scala> import com.twitter.util.Try
import com.twitter.util.Try
Kinds of "names" commonly recognized
On account of the bequest of the late Ezekiah Hopkins, of Lebanon, Pennsylvania, U. S. A., there is now another vacancy open which entitles a member of the League to a salary of £4 a week for purely nominal services.
On account of the bequest of the late Ezekiah Hopkins, of Lebanon, Pennsylvania, U. S. A., there is now another vacancy open which entitles a member of the League to a salary of £4 a week for purely nominal services.
val sentDetector = new SentenceDetectorME(new SentenceModel(sdStream))
val tokenizer = new TokenizerME(new TokenizerModel(tokStream))
val finder = new NameFinderME(new TokenNameFinderModel(nfStream))
val sentences = sentDetector.sentDetect(document)
val tokenized = sentences map { s => tokenizer.tokenize(s) }
val nameSpans: Seq[String] = tokenized map { tokens =>
Span.spansToStrings(finder.find(tokens), tokens)
}
finder.clearAdaptiveData()
java.lang.IllegalArgumentException: The span [268..276) is outside
the given text which has length 155!
def parseAndIncrement(input: String): Int = input.toInt + 1
def parseAndIncrement(input: String): Try[Int] =
Try { input.toInt } map { i => i + 1 }
def safeDivide(n: Int, d: Int): Try[Int] = Try { n / d }
val good = for {
n <- parseAndIncrement("5")
d <- parseAndIncrement("1")
result <- safeDivide(n, d)
} yield result
val bad1 = for {
n <- parseAndIncrement("5")
d <- parseAndIncrement("-1")
result <- safeDivide(n, d)
} yield result
val bad2 = for {
n <- parseAndIncrement("v")
d <- parseAndIncrement("1")
result <- safeDivide(n, d)
} yield result
val good = for {
n <- parseAndIncrement("5")
d <- parseAndIncrement("1")
result <- safeDivide(n, d)
} yield result
val sugarFreeGood = parseAndIncrement("5").flatMap { n =>
parseAndIncrement("1").flatMap { d =>
safeDivide(n, d)
}
}
val tries = Seq("1", "2", "3").map(parseAndIncrement)
Try.collect(tries)
bad2.getOrElse(0)
bad2.rescue {
case t: NumberFormatException => com.twitter.util.Return(0)
}
Like Try, but with an extra state
Like Try, can be combined using map, flatMap, handle, rescue, etc.
Also allows registration of callbacks:
When do these return?
import com.twitter.util.Future
val f1 = Future { Thread.sleep(5000) }
val f2 = Future { 0 }.map { _ => Thread.sleep(5000) }
val f3 = Future.value(Thread.sleep(5000))
val f4 = for {
a <- Future { 1 }
b <- Future { Thread.sleep(5000); 2 }
} yield a + b
When do these return?
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
val f1 = Future { Thread.sleep(5000) }
val f2 = Future { 0 }.map { _ => Thread.sleep(5000) }
val f3 = Future.successful(Thread.sleep(5000))
val f4 = for {
a <- Future { 1 }
b <- Future { Thread.sleep(5000); 2 }
} yield a + b
val service = new Service[HttpRequest, HttpResponse] {
def apply(req: HttpRequest): Future[HttpResponse] =
Future.value(new DefaultHttpResponse(
req.getProtocolVersion, HttpResponseStatus.OK))
}
We need to be careful with I/O, since Future.value blocks a Finagle thread
import com.twitter.util.FuturePool
val pool = FuturePool.unboundedPool
val f1 = pool { Thread.sleep(5000); 0 }
val f2 = pool { 0 }.flatMap { i => pool { expensiveOp(i) }}
for {
foo <- getFoo()
bar <- getBar(foo)
baz <- getBaz(foo) // Don't do this!
} yield (bar, baz)
What’s wrong here?
for {
foo <- getFoo()
bar = getBar(foo)
baz = getBaz(foo)
barValue <- bar
bazValue <- baz
} yield (barValue, bazValue)
for {
foo <- getFoo()
pair <- getBar(foo).join(getBaz(foo))
} yield pair
"Applicative" sequencing
Future.collect(futures: Seq[Future[Int]])
A service is a function
class Service[-Req, +Rep] extends (Req => Future[Rep])
import com.twitter.finagle.Service
import com.twitter.util.Future
val parserService = new Service[String, Int] {
def apply(request: String) = Future(request.toInt)
}
Servers make services available on the network over a protocol
import com.twitter.finagle.Http
val myHttpService: Service[HttpRequest, HttpResponse] = ???
val server = Http.serve(":8080", myHttpService)
The term "client" is overloaded
A client in our third sense
val client: Service[HttpRequest, HttpResponse] =
Http.newService("www.google.com:80")
Filters have a complicated-looking type:
class Filter[-ReqIn, +RepOut, +ReqOut, -RepIn]
extends (ReqIn, Service[ReqOut, RepIn]) => Future[RepOut]
Filters are actually relatively simple: they’re just service transformers
TimeoutFilter is an example of a SimpleFilter (doesn’t change types)
import com.twitter.conversions.time._
import com.twitter.finagle.util.DefaultTimer
import com.twitter.finagle.service.TimeoutFilter
val myTimeoutFilter =
new TimeoutFilter[String, Int](1.second, DefaultTimer.twitter)
import com.twitter.util.FuturePool
val slowParserService = new Service[String, Int] {
def apply(request: String) = FuturePool.unboundedPool {
Thread.sleep(5000); request.toInt
}
}
val myService = myTimeoutFilter andThen slowParserService
Finagle is designed to make it possible to define many components in a protocol-agnostic fashion
We need to define a method implementation for every function in our service
What’s wrong with NaiveNameRecognizerService?
(Hint: there’s more than one thing)
twitter-server: an alternative App
./sbt run
Then visit the admin page, metrics, etc. on port 9990
/
#