+doc,htc #18600,18597 documents where/how to deal with failure in Http
This commit is contained in:
parent
2f2e07666e
commit
573a69e2b9
20 changed files with 532 additions and 60 deletions
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.javadsl.server;
|
||||
|
||||
//#binding-failure-high-level-example
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.dispatch.OnFailure;
|
||||
import akka.http.javadsl.model.ContentTypes;
|
||||
import akka.http.javadsl.server.*;
|
||||
import akka.http.javadsl.server.values.Parameters;
|
||||
import akka.http.scaladsl.Http;
|
||||
import scala.concurrent.Future;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class HighLevelServerBindFailureExample {
|
||||
public static void main(String[] args) throws IOException {
|
||||
// boot up server using the route as defined below
|
||||
final ActorSystem system = ActorSystem.create();
|
||||
|
||||
// HttpApp.bindRoute expects a route being provided by HttpApp.createRoute
|
||||
Future<Http.ServerBinding> bindingFuture =
|
||||
new HighLevelServerExample().bindRoute("localhost", 8080, system);
|
||||
|
||||
bindingFuture.onFailure(new OnFailure() {
|
||||
@Override
|
||||
public void onFailure(Throwable failure) throws Throwable {
|
||||
System.err.println("Something very bad happened! " + failure.getMessage());
|
||||
system.shutdown();
|
||||
}
|
||||
});
|
||||
|
||||
system.shutdown();
|
||||
}
|
||||
}
|
||||
//#binding-failure-high-level-example
|
||||
|
|
@ -5,26 +5,43 @@
|
|||
package docs.http.javadsl.server;
|
||||
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.dispatch.OnFailure;
|
||||
import akka.http.impl.util.JavaMapping;
|
||||
import akka.http.impl.util.Util;
|
||||
import akka.http.javadsl.Http;
|
||||
import akka.http.javadsl.IncomingConnection;
|
||||
import akka.http.javadsl.ServerBinding;
|
||||
import akka.http.javadsl.model.*;
|
||||
import akka.http.javadsl.model.ContentTypes;
|
||||
import akka.http.javadsl.model.HttpRequest;
|
||||
import akka.http.javadsl.model.HttpResponse;
|
||||
import akka.http.javadsl.model.Uri;
|
||||
import akka.japi.JavaPartialFunction;
|
||||
import akka.japi.function.Function;
|
||||
import akka.japi.function.Procedure;
|
||||
import akka.stream.ActorMaterializer;
|
||||
import akka.stream.Materializer;
|
||||
import akka.stream.javadsl.Flow;
|
||||
import akka.stream.javadsl.Sink;
|
||||
import akka.stream.javadsl.Source;
|
||||
import akka.stream.stage.Context;
|
||||
import akka.stream.stage.PushStage;
|
||||
import akka.stream.stage.SyncDirective;
|
||||
import akka.stream.stage.TerminationDirective;
|
||||
import akka.util.ByteString;
|
||||
import scala.Function1;
|
||||
import scala.concurrent.Await;
|
||||
import scala.concurrent.Future;
|
||||
import scala.concurrent.duration.FiniteDuration;
|
||||
import scala.runtime.BoxedUnit;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class HttpServerExampleDocTest {
|
||||
|
||||
public static void bindingExample() throws Exception {
|
||||
//#binding-example
|
||||
ActorSystem system = ActorSystem.create();
|
||||
|
|
@ -45,6 +62,116 @@ public class HttpServerExampleDocTest {
|
|||
//#binding-example
|
||||
Await.result(serverBindingFuture, new FiniteDuration(3, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
public static void bindingFailureExample() throws Exception {
|
||||
//#binding-failure-handling
|
||||
ActorSystem system = ActorSystem.create();
|
||||
Materializer materializer = ActorMaterializer.create(system);
|
||||
|
||||
Source<IncomingConnection, Future<ServerBinding>> serverSource =
|
||||
Http.get(system).bind("localhost", 80, materializer);
|
||||
|
||||
Future<ServerBinding> serverBindingFuture =
|
||||
serverSource.to(Sink.foreach(
|
||||
new Procedure<IncomingConnection>() {
|
||||
@Override
|
||||
public void apply(IncomingConnection connection) throws Exception {
|
||||
System.out.println("Accepted new connection from " + connection.remoteAddress());
|
||||
// ... and then actually handle the connection
|
||||
}
|
||||
})).run(materializer);
|
||||
|
||||
serverBindingFuture.onFailure(new OnFailure() {
|
||||
@Override
|
||||
public void onFailure(Throwable failure) throws Throwable {
|
||||
// possibly report the failure somewhere...
|
||||
}
|
||||
}, system.dispatcher());
|
||||
//#binding-failure-handling
|
||||
Await.result(serverBindingFuture, new FiniteDuration(3, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
public static void connectionSourceFailureExample() throws Exception {
|
||||
//#incoming-connections-source-failure-handling
|
||||
ActorSystem system = ActorSystem.create();
|
||||
Materializer materializer = ActorMaterializer.create(system);
|
||||
|
||||
Source<IncomingConnection, Future<ServerBinding>> serverSource =
|
||||
Http.get(system).bind("localhost", 8080, materializer);
|
||||
|
||||
Flow<IncomingConnection, IncomingConnection, BoxedUnit> failureDetection =
|
||||
Flow.of(IncomingConnection.class).transform(() ->
|
||||
new PushStage<IncomingConnection, IncomingConnection>() {
|
||||
@Override
|
||||
public SyncDirective onPush(IncomingConnection elem, Context<IncomingConnection> ctx) {
|
||||
return ctx.push(elem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminationDirective onUpstreamFailure(Throwable cause, Context<IncomingConnection> ctx) {
|
||||
// signal the failure to external monitoring service!
|
||||
return super.onUpstreamFailure(cause, ctx);
|
||||
}
|
||||
});
|
||||
|
||||
Future<ServerBinding> serverBindingFuture =
|
||||
serverSource
|
||||
.via(failureDetection) // feed signals through our custom stage
|
||||
.to(Sink.foreach(
|
||||
new Procedure<IncomingConnection>() {
|
||||
@Override
|
||||
public void apply(IncomingConnection connection) throws Exception {
|
||||
System.out.println("Accepted new connection from " + connection.remoteAddress());
|
||||
// ... and then actually handle the connection
|
||||
}
|
||||
})).run(materializer);
|
||||
//#incoming-connections-source-failure-handling
|
||||
Await.result(serverBindingFuture, new FiniteDuration(3, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
public static void connectionStreamFailureExample() throws Exception {
|
||||
//#connection-stream-failure-handling
|
||||
ActorSystem system = ActorSystem.create();
|
||||
Materializer materializer = ActorMaterializer.create(system);
|
||||
|
||||
Source<IncomingConnection, Future<ServerBinding>> serverSource =
|
||||
Http.get(system).bind("localhost", 8080, materializer);
|
||||
|
||||
Flow<HttpRequest, HttpRequest, BoxedUnit> failureDetection =
|
||||
Flow.of(HttpRequest.class).transform(() ->
|
||||
new PushStage<HttpRequest, HttpRequest>() {
|
||||
@Override
|
||||
public SyncDirective onPush(HttpRequest elem, Context<HttpRequest> ctx) {
|
||||
return ctx.push(elem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminationDirective onUpstreamFailure(Throwable cause, Context<HttpRequest> ctx) {
|
||||
// signal the failure to external monitoring service!
|
||||
return super.onUpstreamFailure(cause, ctx);
|
||||
}
|
||||
});
|
||||
|
||||
Flow<HttpRequest, HttpResponse, BoxedUnit> httpEcho =
|
||||
Flow.of(HttpRequest.class)
|
||||
.via(failureDetection)
|
||||
.map(request -> {
|
||||
Source<ByteString, ?> bytes = request.entity().getDataBytes();
|
||||
HttpEntity entity = HttpEntities.create(ContentTypes.TEXT_PLAIN, (Source<ByteString, Object>) bytes);
|
||||
return HttpResponse.create()
|
||||
.withEntity(entity);
|
||||
});
|
||||
|
||||
Future<ServerBinding> serverBindingFuture =
|
||||
serverSource.to(Sink.foreach(con -> {
|
||||
System.out.println("Accepted new connection from " + con.remoteAddress());
|
||||
con.handleWith(httpEcho, materializer);
|
||||
}
|
||||
)).run(materializer);
|
||||
//#connection-stream-failure-handling
|
||||
Await.result(serverBindingFuture, new FiniteDuration(3, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
public static void fullServerExample() throws Exception {
|
||||
//#full-server-example
|
||||
ActorSystem system = ActorSystem.create();
|
||||
|
|
|
|||
|
|
@ -42,11 +42,11 @@ HttpRequest
|
|||
|
||||
An ``HttpRequest`` consists of
|
||||
|
||||
- a method (GET, POST, etc.)
|
||||
- a URI
|
||||
- a seq of headers
|
||||
- an entity (body data)
|
||||
- a protocol
|
||||
- a method (GET, POST, etc.)
|
||||
- a URI
|
||||
- a seq of headers
|
||||
- an entity (body data)
|
||||
- a protocol
|
||||
|
||||
Here are some examples how to construct an ``HttpRequest``:
|
||||
|
||||
|
|
@ -64,10 +64,10 @@ HttpResponse
|
|||
|
||||
An ``HttpResponse`` consists of
|
||||
|
||||
- a status code
|
||||
- a list of headers
|
||||
- an entity (body data)
|
||||
- a protocol
|
||||
- a status code
|
||||
- a list of headers
|
||||
- an entity (body data)
|
||||
- a protocol
|
||||
|
||||
Here are some examples how to construct an ``HttpResponse``:
|
||||
|
||||
|
|
|
|||
|
|
@ -31,11 +31,11 @@ akka-http-jackson
|
|||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
configuration
|
||||
http-model
|
||||
server-side/low-level-server-side-api
|
||||
server-side/websocket-support
|
||||
routing-dsl/index
|
||||
client-side/index
|
||||
configuration
|
||||
|
||||
.. _jackson: https://github.com/FasterXML/jackson
|
||||
|
|
@ -6,10 +6,10 @@ Directives
|
|||
A directive is a wrapper for a route or a list of alternative routes that adds one or more of the following
|
||||
functionality to its nested route(s):
|
||||
|
||||
* it filters the request and lets only matching requests pass (e.g. the `get` directive lets only GET-requests pass)
|
||||
* it modifies the request or the ``RequestContext`` (e.g. the `path` directives filters on the unmatched path and then
|
||||
passes an updated ``RequestContext`` unmatched path)
|
||||
* it modifies the response coming out of the nested route
|
||||
* it filters the request and lets only matching requests pass (e.g. the `get` directive lets only GET-requests pass)
|
||||
* it modifies the request or the ``RequestContext`` (e.g. the `path` directives filters on the unmatched path and then
|
||||
passes an updated ``RequestContext`` unmatched path)
|
||||
* it modifies the response coming out of the nested route
|
||||
|
||||
akka-http provides a set of predefined directives for various tasks. You can access them by either extending from
|
||||
``akka.http.javadsl.server.AllDirectives`` or by importing them statically with
|
||||
|
|
@ -46,7 +46,7 @@ MethodDirectives
|
|||
MiscDirectives
|
||||
Contains directives that validate a request by user-defined logic.
|
||||
|
||||
:ref:`PathDirectives-java`
|
||||
:ref:`path-directives-java`
|
||||
Contains directives to match and filter on the URI path of the incoming request.
|
||||
|
||||
RangeDirectives
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
.. _PathDirectives-java:
|
||||
.. _path-directives-java:
|
||||
|
||||
PathDirectives
|
||||
==============
|
||||
|
|
|
|||
|
|
@ -74,3 +74,27 @@ quickly without running them over the network and helps with writing assertions
|
|||
Read more about :ref:`http-testkit-java`.
|
||||
|
||||
.. _DRY: http://en.wikipedia.org/wiki/Don%27t_repeat_yourself
|
||||
|
||||
.. _handling-http-server-failures-high-level-scala:
|
||||
|
||||
Handling HTTP Server failures in the High-Level API
|
||||
---------------------------------------------------
|
||||
There are various situations when failure may occur while initialising or running an Akka HTTP server.
|
||||
Akka by default will log all these failures, however sometimes one may want to react to failures in addition
|
||||
to them just being logged, for example by shutting down the actor system, or notifying some external monitoring
|
||||
end-point explicitly.
|
||||
|
||||
Bind failures
|
||||
^^^^^^^^^^^^^
|
||||
For example the server might be unable to bind to the given port. For example when the port
|
||||
is already taken by another application, or if the port is privileged (i.e. only usable by ``root``).
|
||||
In this case the "binding future" will fail immediatly, and we can react to if by listening on the Future's completion:
|
||||
|
||||
.. includecode:: ../../code/docs/http/javadsl/server/HighLevelServerBindFailureExample.java
|
||||
:include: binding-failure-high-level-example
|
||||
|
||||
|
||||
.. note::
|
||||
For a more low-level overview of the kinds of failures that can happen and also more fine-grained control over them
|
||||
refer to the :ref:`handling-http-server-failures-low-level-java` documentation.
|
||||
|
||||
|
|
|
|||
|
|
@ -93,9 +93,9 @@ this connection.
|
|||
|
||||
Requests are handled by calling one of the ``handleWithXXX`` methods with a handler, which can either be
|
||||
|
||||
- a ``Flow<HttpRequest, HttpResponse, ?>`` for ``handleWith``,
|
||||
- a function ``Function<HttpRequest, HttpResponse>`` for ``handleWithSyncHandler``,
|
||||
- a function ``Function<HttpRequest, Future<HttpResponse>>`` for ``handleWithAsyncHandler``.
|
||||
- a ``Flow<HttpRequest, HttpResponse, ?>`` for ``handleWith``,
|
||||
- a function ``Function<HttpRequest, HttpResponse>`` for ``handleWithSyncHandler``,
|
||||
- a function ``Function<HttpRequest, Future<HttpResponse>>`` for ``handleWithAsyncHandler``.
|
||||
|
||||
Here is a complete example:
|
||||
|
||||
|
|
@ -165,4 +165,64 @@ that is a stage that "upgrades" a potentially encrypted raw connection to the HT
|
|||
|
||||
You create an instance of the layer by calling one of the two overloads of the ``Http.get(system).serverLayer`` method,
|
||||
which also allows for varying degrees of configuration. Note, that the returned instance is not reusable and can only
|
||||
be materialized once.
|
||||
be materialized once.
|
||||
|
||||
.. _handling-http-server-failures-low-level-java:
|
||||
|
||||
Handling HTTP Server failures in the Low-Level API
|
||||
--------------------------------------------------
|
||||
|
||||
There are various situations when failure may occur while initialising or running an Akka HTTP server.
|
||||
Akka by default will log all these failures, however sometimes one may want to react to failures in addition to them
|
||||
just being logged, for example by shutting down the actor system, or notifying some external monitoring end-point explicitly.
|
||||
|
||||
There are multiple things that can fail when creating and materializing an HTTP Server (similarily, the same applied to
|
||||
a plain streaming ``Tcp`` server). The types of failures that can happen on different layers of the stack, starting
|
||||
from being unable to start the server, and ending with failing to unmarshal an HttpRequest, examples of failures include
|
||||
(from outer-most, to inner-most):
|
||||
|
||||
- Failure to ``bind`` to the specified address/port,
|
||||
- Failure while accepting new ``IncommingConnection`` s, for example when the OS has run out of file descriptors or memory,
|
||||
- Failure while handling a connection, for example if the incoming ``HttpRequest`` is malformed.
|
||||
|
||||
This section describes how to handle each failure situation, and in which situations these failures may occur.
|
||||
|
||||
Bind failures
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
The first type of failure is when the server is unable to bind to the given port. For example when the port
|
||||
is already taken by another application, or if the port is privileged (i.e. only usable by ``root``).
|
||||
In this case the "binding future" will fail immediatly, and we can react to if by listening on the Future's completion:
|
||||
|
||||
.. includecode:: ../../code/docs/http/javadsl/server/HttpServerExampleDocTest.java
|
||||
:include: binding-failure-handling
|
||||
|
||||
Once the server has successfully bound to a port, the ``Source<IncomingConnection, ?>`` starts running and emiting
|
||||
new incoming connections. This source technically can signal a failure as well, however this should only happen in very
|
||||
dramantic situations such as running out of file descriptors or memory available to the system, such that it's not able
|
||||
to accept a new incoming connection. Handling failures in Akka Streams is pretty stright forward, as failures are signaled
|
||||
through the stream starting from the stage which failed, all the way downstream to the final stages.
|
||||
|
||||
Connections Source failures
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
In the example below we add a custom ``PushStage`` (see :ref:`stream-customize-java`) in order to react to the
|
||||
stream's failure. We signal a ``failureMonitor`` actor with the cause why the stream is going down, and let the Actor
|
||||
handle the rest – maybe it'll decide to restart the server or shutdown the ActorSystem, that however is not our concern anymore.
|
||||
|
||||
.. includecode:: ../../code/docs/http/javadsl/server/HttpServerExampleDocTest.java
|
||||
:include: incoming-connections-source-failure-handling
|
||||
|
||||
Connection failures
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The third type of failure that can occur is when the connection has been properly established,
|
||||
however afterwards is terminated abruptly – for example by the client aborting the underlying TCP connection.
|
||||
To handle this failure we can use the same pattern as in the previous snippet, however apply it to the connection's Flow:
|
||||
|
||||
.. includecode:: ../../code/docs/http/javadsl/server/HttpServerExampleDocTest.java
|
||||
:include: connection-stream-failure-handling
|
||||
|
||||
These failures can be described more or less infrastructure related, they are failing bindings or connections.
|
||||
Most of the time you won't need to dive into those very deeply, as Akka will simply log errors of this kind
|
||||
anyway, which is a reasonable default for such problems.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue