Visão geral

O java-restify oferece suporte para requisições assíncronas de maneira bastante simples.

Um detalhe de implementação importante é que o java-restify diferencia a execução assíncrona do método da execução assíncrona da requisição.

Tipos de retorno assíncronos

Conforme comentado na documentação sobre tipos de retorno de método, alguns tipos, como Future, CompletableFuture, irão automaticamente forçar que o método seja executado em uma thread diferente.

public interface MyApi {
@Path("/customers/{id}") @Get
CompletableFuture<Customer> getCustomerById(@PathParameter String id);
}
MyApi myApi = new RestifyProxyBuilder()
.target(MyApi.class)
.build();
myApi.getCustomerById("abc123")
.thenAccept(customer -> ...) //outra thread

Outro tipo que pode ser utilizado como retorno do método é o AsyncEndpointCall, um objeto fornecido pelo java-restify que representa a execução assíncrona de uma requisição.

public interface MyApi {
@Path("/customers/{id}") @Get
AsyncEndpointCall<Customer> getCustomerById(@PathParameter String id);
}
MyApi myApi = new RestifyProxyBuilder()
.target(MyApi.class)
.build();
AsyncEndpointCall<Customer> call = myApi.getCustomerById("abc123"); // lazy - a requisição ainda não foi realizada
call.executeAsync()
.thenAccept(customer -> ...) //outra thread

Callbacks

Outra abordagem é, ao invés de lidar com o retorno do método, é utilizar argumentos de callback, usando a anotação @CallbackParameter:

Ao utilizar parâmetros anotados com @CallbackParameter, o retorno do método deve ser void.

import com.github.ljtfreitas.restify.http.contract.CallbackParameter;
import com.github.ljtfreitas.restify.http.client.call.async.EndpointCallSuccessCallback;
import com.github.ljtfreitas.restify.http.client.call.async.EndpointCallFailureCallback;
import com.github.ljtfreitas.restify.http.client.call.async.EndpointCallCallback;
public interface MyApi {
/* callback do tipo java.uil.function.BiConsumer:
uma função que recebe o objeto de resposta e a exceção (se houver)
*/
@Path("/customers/{id}") @Get
void getCustomerById(@PathParameter String id, @CallbackParameter BiConsumer<Customer, Throwable> callback);
/* EndpointCallSuccessCallback permite capturar a resposta deserializada como um objeto.
Essa interface possui um único método onSuccess(T response)
*/
@Path("/customers/{id}") @Get
void getCustomerById(@PathParameter String id, @CallbackParameter EndpointCallSuccessCallback<Customer> success);
/* EndpointCallFailureCallback permite capturar a exceção gerada pela requisição HTTP, se houver.
Essa exceção pode ser um problema de I/O ou uma resposta de erro (4xx, 5xx)
Essa interface possui um único método onFailure(Throwable throwable):
*/
@Path("/customers/{id}") @Get
void getCustomerById(@PathParameter String id, @CallbackParameter EndpointCallFailureCallback failure);
/* É possível usar parâmetros dos dois tipos
*/
@Path("/customers/{id}") @Get
void getCustomerById(@PathParameter String id, @CallbackParameter EndpointCallSuccessCallback<Customer> success, @CallbackParameter EndpointCallFailureCallback failure);
/* Existe uma terceira interface chamada EndpointCallCallback, que extende EndpointCallSuccessCallback e EndpointCallFailureCallback.
Essa interface também é uma opção caso você precise dos dois callbacks (sucesso e falha)
*/
@Path("/customers/{id}") @Get
void getCustomerById(@PathParameter String id, @CallbackParameter EndpointCallCallback<Customer> callback);
}

Configuração

Os handlers responsáveis pela execução de métodos assíncronos utilizam o mesmo thread pool configurado para requisições assíncronas. Por padrão, é utilizad um Executor criado a partir do método Executors.newCachedThreadPool).

Se a configuração padrão não atender as necessidades da sua aplicação, o Executor pode ser facilmente customizado:

ExecutorService myExecutor = Executors.newFixedThreadPool(10);
MyApi myApi = new RestifyProxyBuilder()
.async(myExecutor)
.target(MyApi.class)
.build();
// ou
MyApi myApi = new RestifyProxyBuilder()
.async()
.using(myExecutor)
.target(MyApi.class)
.build();

Clientes HTTP assíncronos

Os exemplos acima demonstram o suporte do java-restify para execução assíncrona de métodos, usando o tipo de retorno ou argumentos de callback. Nesse cenário, o java-restify irá apenas executar em outra thread os mesmos passos de qualquer chamada de método. O ponto a ser observado aqui é que a requisição HTTP continua a ser uma operação bloqueante, mas sendo realizada em uma thread separada.

Isso é útil, mas eventualmente pode ser importante utilizarmos os recursos assíncronos do próprio client HTTP, como por exemplo I/O não-bloqueante ou algum suporte especializado fornecido pela própria biblioteca. O java-restify também oferece uma API específica para clientes HTTP assíncronos. Os detalhes são explicados mais profundamente na documentação sobre clientes HTTP.

Se o java-restify estiver configurado para utilizar um cliente HTTP assíncrono, então os recursos específicos da implementação serão utilizados; se não for o caso (como é o padrão), a execução do método (incluindo a requisição HTTP) apenas será feita em uma thread separada (conforme detalhado acima).