I have a Micronaut application running with the below configuration:
micronaut:
server:
cors:
enabled: true
port: 8080
Now I have an enhancement where I want to call a 3rd party URL and get the response in my application (one of the module in my application). I used the below code snippet:
EmbeddedServer server = ApplicationContext.run(EmbeddedServer.class);
HttpClient client = server .getApplicationContext() .createBean(HttpClient.class, server.getURL());
HttpRequest req = HttpRequest.GET(urlHost);
HttpResponse<String> response = client.toBlocking().exchange(req, String.class);
But this is not working. I get port already in use. I did not find much help in google because Micronaut's HttpClient is usually used in Micronaut Test which is not in my case. Is this possible to use it in my application? If so how? Thanks in Advance.
It is because you are starting another server by ApplicationContext.run(EmbeddedServer.class).
You don't need it. It is enough to inject HttpClient into your class by constructor:
#Singleton
public class MyClient {
private final RxHttpClient client;
public MyClient(#Client("https://some.third-party.com") RxHttpClient client) {
this.client = client;
}
HttpResponse<String> getSomething(Integer id) {
URI uri = UriBuilder.of("/some-objects").path(id).build();
return client.toBlocking().exchange(HttpRequest.GET(uri), String.class);
}
}
If you have third-party server URL in application configuration under some-service.url path for example, then you can use #Client("${some-service.url}")
Another option is to define declarative client for the third-party server and then inject it in your classes where needed.
First define client interface for your third-party service:
#Client("some-service")
public interface SomeServiceClient {
#Get("/api/some-objects/{id}")
String getSomeObject(#QueryValue("id") Integer id);
}
Add client configuration for that service in application configuration (application.yaml):
micronaut:
http:
services:
some-service:
url: "https://some.third-party.com"
read-timeout: 1m
And then you can inject the SomeServiceClient where you need it:
#Singleton
public class SomeServiceConsumer {
private final SomeServiceClient client;
public SomeServiceConsumer(SomeServiceClient client) {
this.client = client;
}
void doWithSomething(Integer id) {
String object = client.getSomeObject(id);
... // processing of object here
}
}
You can find more information in a Micronaut documentation
https://guides.micronaut.io/latest/micronaut-http-client-gradle-java.html
Related
I have developed 2 microservices in Spring:
a UI-service
a Login-service, which also has UI (html-form) for testing
The UI-service is consuming the Login-service using the below uri, now i want not to give the url but to use Eureka naming server here
UI-service (code)
#RequestMapping("/log")
public String abc(HttpServletRequest request) {
final String uri = "http://localhost:8093/accounts/login";
// want to use Eureka discovery instead of directly consuming this service.
RestTemplate restTemplate = new RestTemplate();
String result = restTemplate.getForObject(uri, String.class);
return result;
//request.setAttribute("mode", "MODE_LOGIN");
}
initialise the Eureka client and fetch the registered apps:
List<Application> applications = eurekaClient.getApplications().getRegisteredApplications();
applications.stream()
.forEach(application -> {
application.getInstances().stream()
.forEach(instanceInfo -> {
// get instanceInfo.getHostName() instanceInfo.getPort()
});
});
Problem
How to forward requests in Spring Cloud application? I need to forward requests to other services depending on the part of uri.
For example
HTTP GET http://user-application/api/users, returns users JSON.
HTTP GET http://user-application/api/proxy/jobs-application/api/jobs, returns jobs JSON, but this request should be forwarded to another application:
HTTP GET http://jobs-application/api/jobs.
Any HTTP method is allowed, not only GET.
Context
I have a SpringBoot Application, User application which has REST end-points which return data.
For example GET http://user-application/api/users would return users in the JSON format.
User application also has an HTTP end-point which should forward the request to other applications - let's call one of them Jobs application.
This end-point is HTTP {ANY_METHOD} /api/proxy/{dynamic-service}/{dynamic-path} as an example,
GET http://user-application/api/proxy/jobs-application/api/jobs
Please, note, initial request comes to the User application, while then it is forwarded to the Jobs application.
Approaches
I put some my approaches which I think about. Maybe you have done similar things in the past, so you could share your experience doing so. Or even improve one of my approaches.
ProxyController approach
I would create a ProxyController in User application with mapping /proxy
#Controller
#RequestMaping("/proxy/**")
ProxyController
public void proxy(final HttpServletRequest request, HttpResponse response) {
final String requestUri = request.getRequestUri();
if (!requestUri.startsWith("/api/proxy/")) {
return null; // Do not proxy
}
final int proxyIndex = "/api/proxy/".lenght(); // Can be made a constant
final String proxiedUrl = requestUri.subString(proxyIndex, requestUri.lenght());
final Optional<String> payload = retrievePayload(request);
final Headers headers = retrieveHeaders(request);
final HttpRequest proxyRequest = buildProxyRequest(request, headers);
payload.ifPresent(proxyRequest::setPayload);
final HttpResponse proxyResponse = httpClient.execute(proxyRequest)
pdateResponse(response, proxyResponse);
}
The problem with this approach, I have to write a lot of code t build a proxy request, to check if it has payload and if it has, copy it into proxy request, then copy headers, cookies etc to the proxy request, copy HTTP verb into proxy request. Then when I get proxy response, I have to populate its details into the response.
Zuul approach
I was inspired by ZuulFilters:
https://www.baeldung.com/spring-rest-with-zuul-proxy
https://stackoverflow.com/a/47856576/4587961
#Component
public class ProxyFilter extends ZuulFilter {
private static final String PROXY_PART = "/api/proxy";
private static final int PART_LENGTH = PROXY_PART.length();
#Autowired
public ProxyFilter() {
}
#Override
public boolean shouldFilter() {
final RequestContext context = RequestContext.getCurrentContext();
final String requestURI = retrieveRequestUri(context);
return requestURI.startsWith(PROXY_PART);
}
#Override
public Object run() {
final RequestContext context = RequestContext.getCurrentContext();
final String requestURI = retrieveRequestUri(context);
final String forwardUri = requestURI.substring(PART_LENGTH);
context.setRouteHost(buildUrl(forwardUri));
return null;
}
#Override
public String filterType() {
return "proxy";
}
#Override
public int filterOrder() {
return 0;
}
private String retrieveRequestUri(final RequestContext context) {
final HttpServletRequest request = context.getRequest();
return request.getRequestURI();
}
private URL buildUrl(final String uri) {
try {
return new URL(uri);
} catch (MalformedURLException e) {
throw new RuntimeException(String.format("Failed to forward request uri %s}.", uri), e);
}
}
}
This code allows me to forward requests with less effort. However, we also use client side load balancer Ribbon and circuit breaker Hystrix in Spring Cloud Zuul out of box. How to enable these features? Will they be enabled out of box in context.setRouteHost(forwardUrl);
I would like to add another approach, maybe it can also work.
Static application.yml file to configure Zuul proxy approach
This approach does not requre dynamic Zuul Filters.
application.yml
zuul:
routes:
user-application:
path: /api/users/**
serviceId: user-service
stripPrefix: false
sensitiveHeaders:
# I have to define all other services similarly.
jobs-application:
path: /api/proxy/jobs/**
serviceId: jobs-application
stripPrefix: true
sensitiveHeaders:
It will work only if I know all the services my clients need to call before I deploy the User application. What if a new application is added dynamically? Then I will have to update the configuration.
We currently have some trouble on a productive server as it consumes way too much memory. One of the leaks could come from the jersey client. I found the following two other questions and a how to:
How to correctly share JAX-RS 2.0 client
Closing JAX RS Client/Response
https://blogs.oracle.com/japod/entry/how_to_use_jersey_client
What I get from it, I should reuse the Client and potentially also the WebTargets?
Also closing responses is advised, but how can I do this with .request()?
Code example, this is getting called about 1000 times per hour with different paths:
public byte[] getDocument(String path) {
Client client = ClientBuilder.newClient();
WebTarget target = client.target(config.getPublishHost() + path);
try {
byte[] bytes = target.request().get(byte[].class);
LOGGER.debug("Document size in bytes: " + bytes.length);
return bytes;
} catch (ProcessingException e) {
LOGGER.error(Constants.PROCESSING_ERROR, e);
throw new FailureException(Constants.PROCESSING_ERROR, e);
} catch (WebApplicationException e) {
LOGGER.error(Constants.RESPONSE_ERROR, e);
throw new FailureException(Constants.RESPONSE_ERROR, e);
} finally {
client.close();
}
}
So my question is how to properly use the API to prevent leaks for the above example?
Client instances should be reused
Client instances are heavy-weight objects that manage the underlying client-side communication infrastructure. Hence initialization as well as disposal of a Client instance may be a rather expensive operation.
The documentation advises to create only a small number of Client instances and reuse them when possible. It also states that Client instances must be properly closed before being disposed to avoid leaking resources.
WebTarget instances could be reused
You could reuse WebTarget instances if you perform multiple requests to the same path. And reusing WebTarget instances is recommended if they have some configuration.
Response instances should be closed if you don't read the entity
Response instances that contain an un-consumed entity input stream should be closed. This is typical for scenarios where only the response headers and the status code are processed, ignoring the response entity. See this answer for more details on closing Response instances.
Improving your code
For the situation mentioned in your question, you want you ensure that the Client instance is reused for all getDocument(String) method invocations.
For instance, if your application is CDI based, create a Client instance when the bean is constructed and dispose it before its destruction. In the example below, the Client instance is stored in a singleton bean:
#Singleton
public class MyBean {
private Client client;
#PostConstruct
public void onCreate() {
this.client = ClientBuilder.newClient();
}
...
#PreDestroy
public void onDestroy() {
this.client.close();
}
}
You don't need to (or maybe you can't) reuse the WebTarget instance (the requested path changes for each method invocation). And the Response instance is automatically closed when you read the entity into a byte[].
Using a connection pool
A connection pool can be a good performance improvement.
As mentioned in my older answer, by default, the transport layer in Jersey is provided by HttpURLConnection. This support is implemented in Jersey via HttpUrlConnectorProvider. You can replace the default connector if you want to and use a connection pool for better performance.
Jersey integrates with Apache HTTP Client via the ApacheConnectorProvider. To use it, add the following dependecy:
<dependency>
<groupId>org.glassfish.jersey.connectors</groupId>
<artifactId>jersey-apache-connector</artifactId>
<version>2.26</version>
</dependency>
And then create your Client instance as following:
PoolingHttpClientConnectionManager connectionManager =
new PoolingHttpClientConnectionManager();
connectionManager.setMaxTotal(100);
connectionManager.setDefaultMaxPerRoute(5);
ClientConfig clientConfig = new ClientConfig();
clientConfig.property(ApacheClientProperties.CONNECTION_MANAGER, connectionManager);
clientConfig.connectorProvider(new ApacheConnectorProvider());
Client client = ClientBuilder.newClient(clientConfig);
For additional details, refer to Jersey documentation about connectors.
Use the following example in this link to close Response on completed method: https://jersey.github.io/documentation/latest/async.html#d0e10209
final Future<Response> responseFuture = target().path("http://example.com/resource/")
.request().async().get(new InvocationCallback<Response>() {
#Override
public void completed(Response response) {
System.out.println("Response status code "
+ response.getStatus() + " received.");
//here you can close the response
}
#Override
public void failed(Throwable throwable) {
System.out.println("Invocation failed.");
throwable.printStackTrace();
}
});
tip 1 (Response or String):
You can close the response only when it is from type of Response class, not : String.
tip 2 (Auto-closing):
Referring to this question, When you read the entity, the response will be closed automatically:
String responseAsString = response.readEntity(String.class);
tip 3 (connection pooling):
Referring to this question, you can use connection-pools to have better performance. example:
public static JerseyClient getInstance() {
return InstanceHolder.INSTANCE;
}
private static class InstanceHolder {
private static final JerseyClient INSTANCE = createClient();
private static JerseyClient createClient() {
ClientConfig clientConfig = new ClientConfig();
clientConfig.property(ClientProperties.ASYNC_THREADPOOL_SIZE, 200);
clientConfig.property(ClientProperties.READ_TIMEOUT, 10000);
clientConfig.property(ClientProperties.CONNECT_TIMEOUT, 10000);
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
connectionManager.setMaxTotal(200);
connectionManager.setDefaultMaxPerRoute(100);
clientConfig.property(ApacheClientProperties.CONNECTION_MANAGER, connectionManager);
clientConfig.connectorProvider(new ApacheConnectorProvider());
JerseyClient client = JerseyClientBuilder.createClient(clientConfig);
//client.register(RequestLogger.requestLoggingFilter);
return client;
}
}
ATTENTION! By using this solution, if you don't close the response, you can not send more than 100 requests to server (setDefaultMaxPerRoute(100))
I am trying to communicate with a websocket server using Apache Camel AHC-Websocket Component with Akka Camel in Java. In this case, websocket endpoint is goint to be a well known websocket public service.
Using:
JDK 8.x
Akka Java API with Akka Camel, Akka Actor and Akka SLF4J 2.3.9
Apache Camel with AHC WS Component 2.14.1
I followed Akka Camel tutorial for Java located over here.
Short description: Every response received by UntypedProducerActor when I make a request returns CamelMessage with body field as null. But when I make request via ProducerTemplate I receive correct response.
Long description: I am getting strange behaviour from Akka Camel when I make a request and expect a response from the websocket endpoint. When I make a request to the endpoint via defined ActorRef, for example like so:
ActorRef wsProducer = getContext().actorOf(SimpleProducer.props("ahc-ws:echo.websocket.org"));
final Timeout timeout = new Timeout(3, TimeUnit.MINUTES);
final Future<Object> future = Patterns.ask(wsProducer, "Please, respond!", timeout);
final Object result = Await.result(future, timeout.duration());
I clearly see in the logs that Apache Camel websocket endpoint received a response:
DEBUG o.a.c.component.ahc.ws.WsEndpoint - received message --> Please, respond!
But the result object will be a CamelMessage with body field always set to null. The same CamelMessage present in public Object onTransformResponse(Object message) method of my SimpleProducer.
However, when I make request via ProducerTemplate like this:
final Camel camel = CamelExtension.get(getContext().system());
final CamelContext context = camel.context();
final ProducerTemplate template = camel.template();
Object result = template.requestBody("ahc-ws:echo.websocket.org", "Alpha is there!");
It works and result will contain correct response body: "Alpha is there!".
My SimpleProducer is pretty much the same as in tutorial:
public class SimpleProducer extends UntypedProducerActor {
private final LoggingAdapter LOG = Logging.getLogger(getContext().system(), this);
private final String mEndpointUri;
public SimpleProducer(final String serverUrl) {
mEndpointUri = serverUrl;
}
#Override
public String getEndpointUri() {
return mEndpointUri;
}
#Override
public Object onTransformResponse(Object message) {
return super.onTransformOutgoingMessage(message);
}
#Override
public boolean isOneway() {
return false;
}
public static Props props(final String endpointUri) {
return Props.create(new Creator<SimpleProducer>() {
private static final long serialVersionUID = 1L;
#Override
public SimpleProducer create() throws Exception {
return new SimpleProducer(endpointUri);
}
});
}
}
Maybe somebody had the same issue and can help me out?
I have a number of classes exposed as JAX-RS request "handlers", using javax.ws.rs.Path annotations. I want to add certain actions before every request and after each request. Also, I need to create a global application-wide exception handler, which will catch everything thrown by these handlers and protocol.
Is it possible to achieve this with standard JAX-RS without creating of a custom class inherited from com.sun.jersey.spi.container.servlet.ServletContainer (I'm using Jersey).
You can also use ExceptionMappers. This mechanism which catch the exception thrown by your service and convert it to the appropriate Response:
#Provider
public class PersistenceMapper implements ExceptionMapper<PersistenceException> {
#Override
public Response toResponse(PersistenceException arg0) {
if(arg0.getCause() instanceof InvalidDataException) {
return Response.status(Response.Status.BAD_REQUEST).build();
} else {
...
}
}
}
For more information see:
JAX-RS using exception mappers
You could create a proxy RESTful service and use this as the entry point to all your other RESTful services. This proxy can receive requests, do any pre-processing, call the RESTful service required, process the response and then return something to the caller.
I have a set up like this in a project I've been working on. The proxy performs functions like authentication, authorisation and audit logging. I can go into further details if you like.
Edit:
Here is an idea of how you might want to implement a proxy that supports GET requests;
#Path("/proxy")
public class Proxy
{
private Logger log = Logger.getLogger(Proxy.class);
#Context private UriInfo uriInfo;
#GET
#Path("/{webService}/{method}")
public Response doProxy(#Context HttpServletRequest req,
#PathParam("webService") String webService,
#PathParam("method") String method)
{
log.debug("log request details");
//implement this method to work out the URL of your end service
String url = constructURL(req, uriInfo, webService, method);
//Do any actions here before calling the end service
Client client = Client.create();
WebResource resource = client.resource(url);
try
{
ClientResponse response = resource.get(ClientResponse.class);
int status = response.getStatus();
String responseData = response.getEntity(String.class);
log.debug("log response details");
//Do any actions here after getting the response from the end service,
//but before you send the response back to the caller.
return Response.status(status).entity(responseData).build();
}
catch (Throwable t)
{
//Global exception handler here
//remember to return a Response of some kind.
}
}
You can use filters to read and modify all requests and responses.