Quick Start
Finagle is an asynchronous network stack for the JVM that you can use to build asynchronous Remote Procedure Call (RPC) clients and servers in Java, Scala, or any JVM-hosted language. Finagle provides a rich set of tools that are protocol independent.
The following Quick Start sections show how to implement simple RPC servers and clients in Scala and Java. The first example shows the creation a simple HTTP server and corresponding client. The second example shows the creation of a Thrift server and client. You can use these examples to get started quickly and have something that works in just a few lines of code. For a more detailed description of Finagle and its features, start with Finagle Overview and come back to Quick Start later.
Note: The examples in this section include both Scala and Java implementations. Other sections show only Scala examples. For more information about Java, see Java Design Patterns for Finagle.
Simple HTTP Server
Consider a very simple implementation of an HTTP server and client in which clients make HTTP GET requests and the server responds to each one with an HTTP 200 OK response.
The following server, which is shown in both Scala and Java, responds to a client's HTTP request with an HTTP 200 OK response:
Scala HTTP Server Implementation
val service: Service[HttpRequest, HttpResponse] = new Service[HttpRequest, HttpResponse] { // 1
def apply(request: HttpRequest) = Future(new DefaultHttpResponse(HTTP_1_1, OK)) // 2
}
val address: SocketAddress = new InetSocketAddress(10000) // 3
val server: Server = ServerBuilder() // 4
.codec(Http())
.bindTo(address)
.name("HttpServer")
.build(service)
Java HTTP Server Implementation
Service<HttpRequest, HttpResponse> service = new Service<HttpRequest, HttpResponse>() { // 1
public Future<HttpResponse> apply(HttpRequest request) {
return Future.value( // 2
new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK));
}
};
ServerBuilder.safeBuild(service, ServerBuilder.get() // 4
.codec(Http())
.name("HttpServer")
.bindTo(new InetSocketAddress("localhost", 10000))); // 3
HTTP Server Code Annotations
- Create a new Service that handles HTTP requests and responses.
- For each request, respond asynchronously with an HTTP 200 OK response. A Future instance represents an asynchronous operation that may be performed later.
- Specify the socket addresses on which your server responds; in this case, on port 10000 of localhost.
- Build a server that responds to HTTP requests on the socket and associate it with your service. In this case, the Server builder specifies
- an HTTP codec, which ensures that only valid HTTP requests are received by the server
- the host socket that listens for requests
- the association between the server and the service, which is specified by
.build
in Scala and the first argument tosafeBuild
in Java - the name of the service
Note: For more information about the Java implementation, see Java Design Patterns for Finagle.
Simple HTTP Client
The client, which is shown in both Scala and Java, connects to the server, and issues a simple HTTP GET request:
Scala HTTP Client Implementation
val client: Service[HttpRequest, HttpResponse] = ClientBuilder() // 1
.codec(Http())
.hosts(address)
.hostConnectionLimit(1)
.build()
// Issue a request, get a response:
val request: HttpRequest = new DefaultHttpRequest(HTTP_1_1, GET, "/") // 2
val responseFuture: Future[HttpResponse] = client(request) // 3
onSuccess { response => println("Received response: " + response) // 4
}
Java HTTP Client Implementation
Service<HttpRequest, HttpResponse> client = ClientBuilder.safeBuild(ClientBuilder.get() // 1
.codec(Http())
.hosts("localhost:10000")
.hostConnectionLimit(1));
// Issue a request, get a response:
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/"); // 2
client.apply(request).addEventListener(new FutureEventListener<HttpResponse>() { // 3
public void onSuccess(HttpResponse response) { // 4
System.out.println("received response: " + response);
}
public void onFailure(Throwable cause) {
System.out.println("failed with cause: " + cause);
}
});
HTTP Client Code Annotations
- Build a client that sends an HTTP request to the host identified by its socket address. In this case, the Client builder specifies
- an HTTP request filter, which ensures that only valid HTTP requests are sent to the server
- a list of the server's hosts that can process requests
- maximum number of connections from the client to the host
- to build this client service
- Create an HTTP GET request.
- Make the request to the host identified in your client.
- Specify a callback,
onSuccess
, that Finagle executes when the response arrives.
Note: Although the example shows building the client and execution of the built client on the same thread, you should build your clients only once and execute them separately. There is no requirement to maintain a 1:1 relationship between building a client and executing a client.
HTTP Client To A Standard Web Server
If you create a request using HttpVersion.HTTP_1_1, a validating server will require a Hosts header. We could fix this by setting the Host header as in
httpRequest.setHeader("Host", "someHostName")
More concisely, you can use the RequestBuilder object, shown below. In the following example, we use a Jetty server "someJettyServer:80" which happens to come with a small test file "/d.txt":
import org.jboss.netty.handler.codec.http.HttpRequest
import org.jboss.netty.handler.codec.http.HttpResponse
import com.twitter.finagle.Service
import com.twitter.finagle.builder.ClientBuilder
import com.twitter.finagle.http.Http
import com.twitter.finagle.http.RequestBuilder
import com.twitter.util.Future
object ClientToValidatingServer {
def main(args: Array[String]) {
val hostNamePort = "someJettyServer:80"
val client: Service[HttpRequest, HttpResponse] = ClientBuilder()
.codec(Http())
.hosts(hostNamePort)
.hostConnectionLimit(1)
.build()
val httpRequest = RequestBuilder().url("http://" + hostNamePort + "/d.txt").buildGet
val responseFuture: Future[HttpResponse] = client(httpRequest)
responseFuture onSuccess { response => println("Received response: " + response) }
}
}
Simple Client and Server for Thrift
Apache Thrift is a binary communication protocol that defines available methods using an interface definition language (IDL). Consider the following Thrift IDL definition for a Hello
service that defines only one method, hi
:
service Hello {
string hi();
}
To create a Finagle Thrift service, you must implement the FutureIface
Interface that Scrooge (a custom Thrift compiler) generates for your service. Scrooge wraps your service method return values with asynchronous Future
objects to be compatible with Finagle.
- If you are using sbt to build your project, the sbt-scrooge plugin automatically compiles your Thrift IDL. Note: The latest release version of this plugin is only compatible with sbt 0.11.2.
- If you are using maven to manage your project, maven-finagle-thrift-plugin can also compile Thrift IDL for Finagle.
Simple Thrift Server
In this Finagle example, the ThriftServer
object implements the Hello
service defined using the Thrift IDL.
Scala Thrift Server Implementation
object ThriftServer {
def main(args: Array[String]) {
// Implement the Thrift Interface
val processor = new Hello.ServiceIface { // 1
def hi() = Future.value("hi") // 2
}
val service = new Hello.Service(processor, new TBinaryProtocol.Factory()) // 3
val server: Server = ServerBuilder() // 4
.name("HelloService")
.bindTo(new InetSocketAddress(8080))
.codec(ThriftServerFramedCodec())
.build(service)
}
}
Java Thrift Server Implementation
Hello.FutureIface processor = new Hello.FutureIface() { // 1
public Future<String> hi() { // 2
return Future.value("hi");
}
};
ServerBuilder.safeBuild( // 4
new Hello.FinagledService(processor, new TBinaryProtocol.Factory()), // 3
ServerBuilder.get()
.name("HelloService")
.codec(ThriftServerFramedCodec.get())
// .codec(ThriftServerFramedCodecFactory$.MODULE$) previously
.bindTo(new InetSocketAddress(8080)));
Thrift Server Code Annotations
- Create a Thrift processor that implements the Thrift service interface, which is
Hello
in this example. - Implement the service interface. In this case, the only method in the interface is
hi
, which only returns the string"hi"
. The returned value must be aFuture
to conform the signature of a FinagleService
. (In a more robust example, the Thrift service might perform asynchronous communication.) - Create an adapter from the Thrift processor to a Finagle service. In this case, the
Hello
Thrift service usesTBinaryProtocol
as the Thrift protocol. - Build a server that responds to Thrift requests on the socket and associate it with your service. In this case, the Server builder specifies
- the name of the service
- the host addresses that can receive requests
- the Finagle-provided
ThriftServerFramedCodec
codec, which ensures that only valid Thrift requests are received by the server - the association between the server and the service
Simple Thrift Client
In this Finagle example, the ThriftClient
object creates a Finagle client that executes the methods defined in the Hello
Thrift service.
Scala Thrift Client Implementation
object ThriftClient {
def main(args: Array[String]) {
// Create a raw Thrift client service. This implements the
// ThriftClientRequest => Future[Array[Byte]] interface.
val service: Service[ThriftClientRequest, Array[Byte]] = ClientBuilder() // 1
.hosts(new InetSocketAddress(8080))
.codec(ThriftClientFramedCodec())
.hostConnectionLimit(1)
.build()
// Wrap the raw Thrift service in a Client decorator. The client provides
// a convenient procedural interface for accessing the Thrift server.
val client = new Hello.ServiceToClient(service, new TBinaryProtocol.Factory()) // 2
client.hi() onSuccess { response => // 3
println("Received response: " + response)
} ensure {
service.release() // 4
}
}
}
Java Thrift Client Implementation
Service<ThriftClientRequest, byte[]> service = ClientBuilder.safeBuild(ClientBuilder.get() // 1
.hosts(new InetSocketAddress(8080))
.codec(ThriftClientFramedCodec.get())
.hostConnectionLimit(1));
Hello.FinagledClient client = new Hello.FinagledClient( // 2
service,
new TBinaryProtocol.Factory(),
"HelloService",
new InMemoryStatsReceiver());
client.hi().addEventListener(new FutureEventListener<String>() {
public void onSuccess(String s) { // 3
System.out.println(s);
}
public void onFailure(Throwable t) {
System.out.println("Exception! " + t.toString());
}
});
Thrift Client Code Annotation
- Build a client that sends a Thrift protocol-based request to the host identified by its socket address. In this case, the Client builder specifies
- the host addresses that can receive requests
- the Finagle-provided
ThriftServerFramedCodec
codec, which ensures that only valid Thrift requests are received by the server - to build this client service
- Make a remote procedure call to the
Hello
Thrift service'sHi
method. This returns aFuture
that represents the eventual arrival of a response. - When the response arrives, the
onSuccess
callback executes to print the result. - Release resources acquired by the client.