上图展示了一个全面使用 Finagle 的未来体系结构。比如,User Service 是一个使用 Finalge Memcached客户端的 Finagle 服务器,并和 Finagle Kestrel Service 交谈。
在 Finagle 中,Future 对象是对于所有异步计算的统一抽象。一个 Future表示了一个尚未完成的计算,其可能成功也可能失败。使用 Future 两个最基本的方法是:
如果任务需要在计算结束之后继续异步执行,你可以指定一个成功回调函数和一个失败回调函数。回调函数通过 onSuccess 和onFailure 函数注册:
组合Future
Future 可以以有趣的方式组合或者转换,从而做到一些常常在函数式程序设计里面看到的组合行为。比如,你可以通过 map 把一个Future[String] 转换成 Future[Int]:
val stringFuture: Future[String] =Future("1")
val intFuture: Future[Int] =stringFuture map{ string =>
string.toInt
}
类似地,你还可以用 flatMap 把一系列 Future 串成一个流水线:
val authenticatedUser: Future[User] =
User.authenticate(email, password)
val lookupTweets: Future[Seq[Tweet]] =
authenticatedUser flatMap{ user =>
Tweet.findAllByUser(user)
}
在这个例子里面,User.authenticate() 是异步执行的;Tweet.findAllByUser()在最终结果上被调用。在 Scala 里面这可以用另一种方式表达,用 for 语句:
for {
user<- User.authenticate(email, password)
tweets<- Tweet.findAllByUser(user)
} yield tweets
当用 flatMap 或者 for 语句串联 Future的时候,处理错误和异常也非常简单。在上面的例子中,ifUser.authenticate() 异步地抛出了一个异常,接下来对于Tweet.findAllByUser() 的调用永远也不会发生。取而代之,流水线的结果表达式仍然是Future[Seq[Tweet]] 类型,但是它含有异常值而不是推文。你可以使用 onFailure回调函数或者其他组合计数来处理异常。
和其它异步编程技术(比如 CPS: Continuation-PassingStyle)相比,Future有一个很好的性质,就是你可以更容易的编写出清楚且鲁棒的异步代码,即使是带有复杂的散布/收集(scatter/gather)操作:
val severalFutures = Seq[Future[Int]] =
Seq(Tweet.find(1), Tweet.find(2), ...)
val combinedFuture: Future[Seq[Int]] =
Future.collect(severalFutures)
Service对象
Service 是一个函数,其接受一个请求,返回一个 Future 对象作为答复。注意客户端和服务器都是用 Service对象表示的。
要创建一个 Service 对象,你需要继承抽象的 Service 类并监听一个端口。下面是一个简单的 HTTP 服务器,监听端口10000:
val service = new Service[HttpRequest, HttpResponse] {
defapply(request: HttpRequest) =
Future(newDefaultHttpResponse(HTTP_1_1, OK))
}
val address = new InetSocketAddress(10000)
val server: Server[HttpRequest, HttpResponse] = ServerBuilder()
.name("MyWebServer")
.codec(Http())
.bindTo(address)
.build(service)
建立一个 HTTP 客户端就更简单了:
val client: Service[HttpRequest, HttpResponse] = ClientBuilder()
.codec(Http())
.hosts(address)
.build()
// Issue a request, geta response:
val request: HttpRequest =
newDefaultHttpRequest(HTTP_1_1, GET,"/")
client(request) onSuccess { response =>
println("Receivedresponse: " + response)
}
Filter对象
Filter 是一种把你的应用程序中不同的阶段的孤立出来组成一个流水线的有用的方式。比如,你可能需要在你的 Service开始接受请求前处理异常、授权等问题。
一个 Filter 包裹了一个 Service,且潜在地,把 Service 的输入和输出类型转换成其它类型。换一句话说,Filter是一个转换器。下面是一个用来确保一个 HTTP 请求有合法的 OAuth 证书的、且使用一个异步的认证服务的filter。
下面是一个修饰了 Service 的Filter:
classRequireAuthentication(a:Authenticator)extends Filter[...] {
defapply(
request:Request,
continue:Service[AuthenticatedRequest, HttpResponse]
)= {
a.authenticate(request)flatMap {
caseAuthResult(OK,passport)=>
continue(AuthenticatedRequest(request,passport))
caseAuthResult(Error(code))=>
Future.exception(newRequestUnauthenticated(code))
}
}
}
Finagle 是一个开源项目,使用 Apache License, Version 2.0。源代码和文档都可以在 GitHub 上找到。
鸣谢
Finagle 最早构思来自 Marius Eriksen 和 Nick Kallen。其它主要贡献人员有 AryaAsemanfar, David Helder, Evan Meagher, Gary McCue, Glen Sanford,Grant Monroe, Ian Ownbey, Jake Donham, James Waldrop, Jeremy Cloud,Johan Oskarsson, Justin Zhu, Raghavendra Prabhu, Robey Pointer,Ryan King, Sam Whitlock, Steve Jenson, Wanli Yang, WilhelmBierbaum, William Morgan, Abhi Khune, and Srini Rajagopal。
原文链接: http://engineering.twitter.com/2011/08/finagle-protocol-agnostic-rpc-system.html
原文发表日期:2011 年 8 月 19 日