I am relatively new to App Engine. I do not understand how to asynchronously make an HTTP request with Java. I would have thought that this was a very simple matter using Thread and Runnable. But it seems that App Engine does not permit their use.
public Hashtable someApiMethod(..) {
SomeEntity entity = new SomeEntity(..);
ObjectifyService.ofy().save().entity(entity).now();
makeSomeHttpRequest(entity);
return launchResponse;
}
My question is this: how do I implement the method makeSomeHttpRequest(..) such that it returns without waiting for the URLFetchService.fetchAsync to return. I have tried the following without success:
protected void makeSomeHttpRequest(SomeEntity entity) {
URLFetchService fetcher = URLFetchServiceFactory.getURLFetchService();
try {
URL url = new URL("https://www.example.com");
Future future = fetcher.fetchAsync(url);
HTTPResponse response = (HTTPResponse) future.get();
byte[] content = response.getContent();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bos.write(content);
String responseString = new String(bos.toByteArray());
int responseCode = response.getResponseCode();
// Here I will do something with the responseCode and responseString
if (responseCode == 200) entity.someValue = responseString;
} catch (IOException e) {
// handle this
} catch (InterruptedException e) {
// handle this
} catch (ExecutionException e) {
// handle this
}
}
Really what I am trying to do is perform this HTTP request without forcing the method someApiMethod to wait for the response.
Few things:
First. Future doesn't work this way. Method .get waits for result of Feature execution, so basically you're stopping current thread until other thread finishes its execution. You're making it synchronous, it doesn't make any sense. Usually you call .get much later, when all other work in current thread is finished
Second. Threads in Appengine are limited to current request, you have to complete all async processing during current request. So updating an entity in such way doesn't make much sense, it's still bounded to current request. I mean in your case makeSomeHttpRequest(entity); should work faster than return launchResponse;
What you really need is to send this data to TaskQueue and make processing of SomeEntity entity from there (but don't send entity itself, just send ID and load by id from queue task). Basically it's going to be a new request handler (servlet/controller/etc) that should load entity by id, execute makeSomeHttpRequest (synchronously) and return http status 200.
See TaskQueue docs: https://cloud.google.com/appengine/docs/java/taskqueue/
You need Push Queue most likely: https://cloud.google.com/appengine/docs/java/taskqueue/overview-push
Related
My current Lambda function is calling a 3rd party web service Synchronously.This function occasionally times out (current timeout set to 25s and cannot be increased further)
My code is something like:
handleRequest(InputStream input, OutputStream output, Context context) throws IOException {
try{
response = calling 3rd party REST service
}catch(Exception e){
//handle exceptions
}
}
1)I want to custom handle the timeout (tracking the time and handling few milli seconds before actual timeout) within my Lambda function by sending a custom error message back to the client.
How can I effectively use the
context.getRemainingTimeInMillis()
method to track the time remaining while my synchronous call is running? Planning to call the context.getRemainingTimeInMillis() asynchronously.Is that the right approach?
2)What is a good way to test the timeout custom functionality ?
I solved my problem by increasing the Lambda timeout and invoking my process in a new thread and timing out the Thread after n seconds.
ExecutorService service = Executors.newSingleThreadExecutor();
try {
Runnable r = () ->{
try {
myFunction();
} catch (Exception e) {
e.printStackTrace();
}
};
f = service.submit(r);
f.get(n, TimeUnit.MILLISECONDS);// attempt the task for n milliseconds
}catch(TimeoutException toe){
//custom logic
}
Another option is to use the
readTimeOut
property of the RestClient(in my case Jersey) to set the timeout.But I see that this property is not working consistently within the Lambda code.Not sure if it's and issue with the Jersey client or the Lambda.
You can try with cancellation token to return custom exceptions with lambda before timeout.
try
{
var tokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(1)); // set timeout value
var taskResult = ApiCall(); // call web service method
while (!taskResult.IsCompleted)
{
if (tokenSource.IsCancellationRequested)
{
throw new OperationCanceledException("time out for lambda"); // throw custom exceptions eg : OperationCanceledException
}
}
return taskResult.Result;
}
catch (OperationCanceledException ex)
{
// handle exception
}
I have this code for a HTTP GET method:
#Override
public Cert get(Representation entity) {
// ...
Cert cert = ct.fetchCertificate(Arrays.asList(domains));
return cert;
}
It's a certificate generator that returns the serialized Cert object into the client.
And inside the fetchCertificate method is a Thread.sleep method that causes the entire servlet/web application to halt until the method returns, causing the whole web application not able to response to additional HTTP requests:
// Poll for the challenge to complete.
try {
int attempts = 20;
while (challenge.getStatus() != Status.VALID && attempts-- > 0) {
LOG.info("CHALLENGE ATTEMPTS: " + attempts);
// Did the authorization fail?
if (challenge.getStatus() == Status.INVALID) {
throw new AcmeException("Challenge failed... Giving up.");
}
// Wait for a few seconds
Thread.sleep(5000L);
// Then update the status
challenge.update();
}
} catch(Exception e) {
// ...
}
In RxJava terms what is the best way to achieve this same functionality but without blocking the application.
You need to put this Whole HTTP request calling and sleep inside a secondary thread. It seems it blocks the main thread.
I have a Service class that communicates with my another process, let's say process_A, by local socket.
My Service class is as follows:
public class MyService extends Service {
private LocalSocket localSock;
private LocalSocketAddress localSockAddr;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent.getAction().equals(START_SERVICE_ACTION)) {
localSock = new LocalSocket();
localSockAddr = new LocalSocketAddress(LOCAL_SOCK_ADDR, LocalSocketAddress.Namespace.ABSTRACT);
try {
localSock.connect(localSockAddr);
} catch (IOException e) {
// Ignore
}
if (localSock.isConnected()) {
new LocalSockInitTask().execute(localSock);
}
} else if (intent.getAction().equals(STOP_SERVICE_ACTION)) {
new LocalSockTermTask().execute(localSock);
}
}
}
The behaviour should be as follows:
When my service is being started by user, the service uses LocalSocket.connect() to connect with process_A. Once connected successfully, the service executes an AsyncTask to send an INIT message to process_A and wait for an INIT message from process_A.
When my service is being stopped by user, the service executes another AsyncTask to send a TERM message to process_A and wait for a TERM message from process_A.
LocalSockInitTask.java:
public class LocalSockInitTask extends AsyncTask<LocalSocket, Void, Boolean> {
#Override
protected Boolean doInBackground(LocalSocket... params) {
LocalSocket localSock = params[0];
FileChannel inChannel;
FileChannel outChannel;
ByteBuffer sendBuf, recvBuf;
byte[] bytes;
String result, recvMsg;
int attempt;
try {
inChannel = new FileInputStream(localSock.getFileDescriptor()).getChannel();
outChannel = new FileOutputStream(localSock.getFileDescriptor()).getChannel();
// Send INIT Message
sendBuf = ByteBuffer.wrap(MSG_INIT.getBytes());
outChannel.write(sendBuf);
// Wait for INIT Message
recvBuf = ByteBuffer.allocate(BUFFER_SIZE);
attempt = 0;
while (inChannel.read(recvBuf) < 0) {
attempt++;
if(attempt == 5)
return false;
Thread.sleep(1000);
}
recvBuf.flip();
bytes = new byte[recvBuf.remaining()];
recvBuf.get(bytes);
result = new String(bytes);
if(!result.equals(MSG_INIT))
return false;
inChannel.close();
outChannel.close();
return true;
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
return false;
}
}
LocalSockTermTask.java is nearly doing the same as LocalSockInitTask.java, the major difference is just the message being send and receive is "MSG_TERM".
The Init task is doing perfectly, both write and read are successful. However, when executing the second AsyncTask (which is LocalSockTermTask), seems both write and read are unsuccessful. I've done some testing on this line:
inChannel.read(recvBuf);
In the first AsyncTask execution (LocalSockInitTask), if nothing can be read, this method will immediately return -1 and that's why I set a while loop and count the attempt.
In the second AsyncTask execution (LocalSockTermTask), if nothing can be read, this method will be blocked, and this makes my while loop and attempt count become useless. This cause the AsyncTask never complete. Also, My process_A is waiting for "MSG_TERM" to terminate, and it remains running, that's why I think outChannel.write(sendBuf) also failed in Term task.
Currently I am passing the LocalSocket object to both AsyncTask and create a pair of in/out FileChannel in the AsyncTask. I've also tried to create a pair of in/out FileChannel in the service and pass the two FileChannel to AsyncTask, but still facing the same problem.
Any help is greatly appreciated!
OK, I just found out that this is my careless mistake. The problem is solved.
My another process handles the TERM message incorrectly, so it just simply ignore the TERM message sent by my AsyncTask, and therefore it continues to run and wait for messages.
Since it ignores the TERM message, it won't send back a TERM message to my AsyncTask, and this cause the inChannel.read(recvBuf) has nothing to read.
The blocking behavior of inChannel.read(recvBuf) is absolutely normal, returning -1 should be the case that I use BufferedReader before I changed to use FileChannel.
In my servlet, I am executing the following code:
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(3000).setConnectTimeout(3000).build();
CloseableHttpAsyncClient client = HttpAsyncClients.custom().setDefaultRequestConfig(requestConfig).build();
try
{
client.start();
for (String request : preparedURLs)
{
client.execute(new HttpGet(request), new FutureCallback<HttpResponse>()
{
public void failed(Exception ex)
{
System.out.println("\n\nRequest Failed Due to : " + ex.getMessage());
}
public void completed(HttpResponse response)
{
System.out.println("\n\nRequest COMPLETED");
}
public void cancelled()
{
System.out.println("\n\nRequest CANCELLED");
}
});
System.out.println("\n\n" + request);
}
}
finally
{
System.out.println("\n*** Finally called ***\n\n");
client.close();
}
But I don't get any response at all. Following is printed in my catalina.out:
http://localhost:8080/servlet/?ps=true
http://localhost:8080/servlet/?ps=true
http://localhost:8080/servlet/?ps=false
*** Finally called ***
I have wrote this code, taking this as example from apache's official site.
I have only omitted the latch part. Is this some thing to do with latch?
If possible please explain the reason of failure too.
This latch that you have removed is actually the synchronization part of the example.
The idea of async client is to make some requests and wait for the responses on other thread. By removing the synchronization the execution directly passes to the finally block instead of waiting the responses and closes the HttpAsyncClient.
In order to get it back working add the latch code from the example.
This is the correct behaviour. You are creating a socket with a conditional Future, and then you proceed to close it without waiting for it.
The example code you linked use a "latch" variable to do so.
You may move the close code inside future callback, but probably this will confuse your waring.
Or you can use the same system of the example, with a shared synchronised counter (maybe just a final AtomicBoolean triggered by an aswer) and wait for it before continuing with your program flow, but that destroy the idea of using async call
I have generated a request for web services. I need to do do a check on my call. If the response is not returned within 5 seconds, another request will be shooted.
Pseudo Code :
webServiceClass response = xyz.getData();
If the response is not obtained in 5 seconds - send another request CheckData() to web services.This should be done for a maximum of 5 times.
I need to do this without using threads.
Try something like this (not tested but should give you the idea):
final MultiThreadedHttpConnectionManager httpConnections = new MultiThreadedHttpConnectionManager();
final HttpConnectionManagerParams connParams = manager.getParams();
final HttpClient httpClient = new HttpClient(manager);
final int connectionTimeout = 5000;
connParams.setConnectionTimeout(connectionTimeout);
try
{
// your web service call goes here
}
catch(ConnectTimeoutException cte)
{
if (isLoggingError())
{
logError(cte.getMessage());
}
}
catch(IOException ioe)
{
if (isLoggingError())
{
logError(ioe.getMessage());
}
}
finally
{
// make sure we always release the connection
method.releaseConnection();
}