|
| 1 | +//!!NODE_ROOT <section> |
| 2 | +include::../../includes.txt[] |
| 3 | +:java-oracle-future: https://docs.oracle.com/javase/8/docs/api/index.html?java/util/concurrent/Future.html |
| 4 | +:java-oracle-executorservice: https://docs.oracle.com/javase/8/docs/api/index.html?java/util/concurrent/ExecutorService.html |
| 5 | +:java-oracle-threadfactory: https://docs.oracle.com/javase/8/docs/api/index.html?java/util/concurrent/ThreadFactory.html |
| 6 | +[."topic"] |
| 7 | +[[basics-async,basics-async.title]] |
| 8 | += [[asynchronous-programming, Asynchronous Programming]]Asynchronous Programming |
| 9 | +:info_doctype: section |
| 10 | +:info_title: Asynchronous Programming |
| 11 | +:info_abstract: How asynchronous programming works in the {sdk-java} and best practices for \ |
| 12 | + handling exceptions |
| 13 | + |
| 14 | +[abstract] |
| 15 | +-- |
| 16 | +How asynchronous programming works in the {sdk-java} and best practices for handling exceptions |
| 17 | +-- |
| 18 | + |
| 19 | +You can use either _synchronous_ or _asynchronous_ methods to call operations on {AWS-services}. |
| 20 | +Synchronous methods block your thread's execution until the client receives a response from the |
| 21 | +service. Asynchronous methods return immediately, giving control back to the calling thread |
| 22 | +without waiting for a response. |
| 23 | + |
| 24 | +Because an asynchronous method returns before a response is available, you need a way to get the |
| 25 | +response when it's ready. The {sdk-java} provides two ways: _Future objects_ and __callback |
| 26 | +methods__. |
| 27 | + |
| 28 | +[[basics-async-future,basics-async-future.title]] |
| 29 | +== Java Futures |
| 30 | + |
| 31 | +Asynchronous methods in the {sdk-java} return a |
| 32 | +{java-oracle-future}[Future] |
| 33 | +object that contains the results of the asynchronous operation __in the future__. |
| 34 | + |
| 35 | +Call the `Future` ``isDone()`` method to see if the service has provided a response object yet. |
| 36 | +When the response is ready, you can get the response object by calling the `Future` ``get()`` |
| 37 | +method. You can use this mechanism to periodically poll for the asynchronous operation's results |
| 38 | +while your application continues to work on other things. |
| 39 | + |
| 40 | +Here is an example of an asynchronous operation that calls a {LAM} function, receiving a |
| 41 | +`Future` that can hold an |
| 42 | +link:sdk-for-java/v1/reference/com/amazonaws/services/lambda/model/InvokeResult.html["InvokeResult", type="documentation"] |
| 43 | +object. The `InvokeResult` object is retrieved only after `isDone()` is ``true``. |
| 44 | + |
| 45 | +[source,java] |
| 46 | +---- |
| 47 | +import com.amazonaws.services.lambda.AWSLambdaAsyncClient; |
| 48 | +import com.amazonaws.services.lambda.model.InvokeRequest; |
| 49 | +import com.amazonaws.services.lambda.model.InvokeResult; |
| 50 | +import java.nio.ByteBuffer; |
| 51 | +import java.util.concurrent.Future; |
| 52 | +import java.util.concurrent.ExecutionException; |
| 53 | +
|
| 54 | +public class InvokeLambdaFunctionAsync |
| 55 | +{ |
| 56 | + public static void main(String[] args) |
| 57 | + { |
| 58 | + String function_name = "HelloFunction"; |
| 59 | + String function_input = "{\"who\":\"SDK for Java\"}"; |
| 60 | +
|
| 61 | + AWSLambdaAsync lambda = AWSLambdaAsyncClientBuilder.defaultClient(); |
| 62 | + InvokeRequest req = new InvokeRequest() |
| 63 | + .withFunctionName(function_name) |
| 64 | + .withPayload(ByteBuffer.wrap(function_input.getBytes())); |
| 65 | +
|
| 66 | + Future<InvokeResult> future_res = lambda.invokeAsync(req); |
| 67 | +
|
| 68 | + System.out.print("Waiting for future"); |
| 69 | + while (future_res.isDone() == false) { |
| 70 | + System.out.print("."); |
| 71 | + try { |
| 72 | + Thread.sleep(1000); |
| 73 | + } |
| 74 | + catch (InterruptedException e) { |
| 75 | + System.err.println("\nThread.sleep() was interrupted!"); |
| 76 | + System.exit(1); |
| 77 | + } |
| 78 | + } |
| 79 | +
|
| 80 | + try { |
| 81 | + InvokeResult res = future_res.get(); |
| 82 | + if (res.getStatusCode() == 200) { |
| 83 | + System.out.println("\nLambda function returned:"); |
| 84 | + ByteBuffer response_payload = res.getPayload(); |
| 85 | + System.out.println(new String(response_payload.array())); |
| 86 | + } |
| 87 | + else { |
| 88 | + System.out.format("Received a non-OK response from {AWS}: %d\n", |
| 89 | + res.getStatusCode()); |
| 90 | + } |
| 91 | + } |
| 92 | + catch (InterruptedException | ExecutionException e) { |
| 93 | + System.err.println(e.getMessage()); |
| 94 | + System.exit(1); |
| 95 | + } |
| 96 | +
|
| 97 | + System.exit(0); |
| 98 | + } |
| 99 | +} |
| 100 | +---- |
| 101 | + |
| 102 | + |
| 103 | +[[basics-async-callback,basics-async-callback.title]] |
| 104 | +== Asynchronous Callbacks |
| 105 | + |
| 106 | +In addition to using the Java `Future` object to monitor the status of asynchronous requests, |
| 107 | +the SDK also enables you to implement a class that uses the |
| 108 | +link:sdk-for-java/v1/reference/com/amazonaws/handlers/AsyncHandler.html["AsyncHandler", type="documentation"] |
| 109 | +interface. `AsyncHandler` provides two methods that are called depending on how the request |
| 110 | +completed: `onSuccess` and ``onError``. |
| 111 | + |
| 112 | +The major advantage of the callback interface approach is that it frees you from having to poll |
| 113 | +the `Future` object to find out when the request has completed. Instead, your code can |
| 114 | +immediately start its next activity, and rely on the SDK to call your handler at the right time. |
| 115 | + |
| 116 | +[source,java] |
| 117 | +---- |
| 118 | +import com.amazonaws.services.lambda.AWSLambdaAsync; |
| 119 | +import com.amazonaws.services.lambda.AWSLambdaAsyncClientBuilder; |
| 120 | +import com.amazonaws.services.lambda.model.InvokeRequest; |
| 121 | +import com.amazonaws.services.lambda.model.InvokeResult; |
| 122 | +import com.amazonaws.handlers.AsyncHandler; |
| 123 | +import java.nio.ByteBuffer; |
| 124 | +import java.util.concurrent.Future; |
| 125 | +
|
| 126 | +public class InvokeLambdaFunctionCallback |
| 127 | +{ |
| 128 | + private class AsyncLambdaHandler implements AsyncHandler<InvokeRequest, InvokeResult> |
| 129 | + { |
| 130 | + public void onSuccess(InvokeRequest req, InvokeResult res) { |
| 131 | + System.out.println("\nLambda function returned:"); |
| 132 | + ByteBuffer response_payload = res.getPayload(); |
| 133 | + System.out.println(new String(response_payload.array())); |
| 134 | + System.exit(0); |
| 135 | + } |
| 136 | +
|
| 137 | + public void onError(Exception e) { |
| 138 | + System.out.println(e.getMessage()); |
| 139 | + System.exit(1); |
| 140 | + } |
| 141 | + } |
| 142 | +
|
| 143 | + public static void main(String[] args) |
| 144 | + { |
| 145 | + String function_name = "HelloFunction"; |
| 146 | + String function_input = "{\"who\":\"SDK for Java\"}"; |
| 147 | +
|
| 148 | + AWSLambdaAsync lambda = AWSLambdaAsyncClientBuilder.defaultClient(); |
| 149 | + InvokeRequest req = new InvokeRequest() |
| 150 | + .withFunctionName(function_name) |
| 151 | + .withPayload(ByteBuffer.wrap(function_input.getBytes())); |
| 152 | +
|
| 153 | + Future<InvokeResult> future_res = lambda.invokeAsync(req, new AsyncLambdaHandler()); |
| 154 | +
|
| 155 | + System.out.print("Waiting for async callback"); |
| 156 | + while (!future_res.isDone() && !future_res.isCancelled()) { |
| 157 | + // perform some other tasks... |
| 158 | + try { |
| 159 | + Thread.sleep(1000); |
| 160 | + } |
| 161 | + catch (InterruptedException e) { |
| 162 | + System.err.println("Thread.sleep() was interrupted!"); |
| 163 | + System.exit(0); |
| 164 | + } |
| 165 | + System.out.print("."); |
| 166 | + } |
| 167 | + } |
| 168 | +} |
| 169 | +---- |
| 170 | + |
| 171 | + |
| 172 | +[[basics-async-tips,basics-async-tips.title]] |
| 173 | +== Best Practices |
| 174 | + |
| 175 | +[[callback-execution,callback-execution.title]] |
| 176 | +=== Callback Execution |
| 177 | + |
| 178 | +Your implementation of `AsyncHandler` is executed inside the thread pool owned by the |
| 179 | +asynchronous client. Short, quickly executed code is most appropriate inside your `AsyncHandler` |
| 180 | +implementation. Long-running or blocking code inside your handler methods can cause contention |
| 181 | +for the thread pool used by the asynchronous client, and can prevent the client from executing |
| 182 | +requests. If you have a long-running task that needs to begin from a callback, have the callback |
| 183 | +run its task in a new thread or in a thread pool managed by your application. |
| 184 | + |
| 185 | +[[thread-pool-configuration,thread-pool-configuration.title]] |
| 186 | +=== Thread Pool Configuration |
| 187 | + |
| 188 | +The asynchronous clients in the {sdk-java} provide a default thread pool that should work for |
| 189 | +most applications. You can implement a custom |
| 190 | +{java-oracle-executorservice}[ExecutorService] |
| 191 | +and pass it to {sdk-java} asynchronous clients for more control over how the thread pools are |
| 192 | +managed. |
| 193 | + |
| 194 | +For example, you could provide an `ExecutorService` implementation that uses a custom |
| 195 | +{java-oracle-threadfactory}[ThreadFactory] |
| 196 | +to control how threads in the pool are named, or to log additional information about thread usage. |
| 197 | + |
| 198 | +[[s3-asynchronous-access,s3-asynchronous-access.title]] |
| 199 | +=== Asynchronous Access |
| 200 | + |
| 201 | +The |
| 202 | +link:sdk-for-java/v1/reference/com/amazonaws/services/s3/transfer/TransferManager.html["TransferManager", type="documentation"] |
| 203 | +class in the SDK offers asynchronous support for working with {S3}. `TransferManager` manages |
| 204 | +asynchronous uploads and downloads, provides detailed progress reporting on transfers, and |
| 205 | +supports callbacks into different events. |
0 commit comments