Skip to content
This repository has been archived by the owner on Jul 29, 2021. It is now read-only.

Commit

Permalink
feat: retry connection between client and server
Browse files Browse the repository at this point in the history
  • Loading branch information
NicolasGeraud committed Jun 3, 2019
1 parent 6d4bd00 commit 8ceddde
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 1 deletion.
6 changes: 6 additions & 0 deletions gravitee-repository-gateway-bridge-http-client/pom.xml
Expand Up @@ -74,6 +74,12 @@
<artifactId>vertx-web-common</artifactId>
<version>${vertx.version}</version>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-circuit-breaker</artifactId>
<version>3.7.0</version>
<scope>compile</scope>
</dependency>

</dependencies>

Expand Down
Expand Up @@ -16,13 +16,21 @@
package io.gravitee.repository.bridge.client.http;

import io.gravitee.common.http.HttpHeaders;
import io.gravitee.common.http.HttpStatusCode;
import io.gravitee.common.util.Version;
import io.gravitee.repository.bridge.client.utils.VertxCompletableFuture;
import io.vertx.circuitbreaker.CircuitBreaker;
import io.vertx.circuitbreaker.CircuitBreakerOptions;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.client.HttpResponse;
import io.vertx.ext.web.client.WebClient;
import io.vertx.ext.web.client.WebClientOptions;
import io.vertx.ext.web.client.impl.HttpContext;
import io.vertx.ext.web.client.impl.WebClientInternal;
import io.vertx.ext.web.codec.BodyCodec;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.FactoryBean;
Expand Down Expand Up @@ -50,10 +58,16 @@ public class WebClientFactory implements FactoryBean<WebClient> {

private final String propertyPrefix;

private CircuitBreaker circuitBreaker;

private String clientVersion = Version.RUNTIME_VERSION.MAJOR_VERSION;

public WebClientFactory(String propertyPrefix) {
this.propertyPrefix = propertyPrefix + ".http.";
}

private static final long retryDuration = 5000L;

@Override
public WebClient getObject() throws Exception {
WebClientOptions options = getWebClientOptions();
Expand All @@ -69,7 +83,62 @@ public WebClient getObject() throws Exception {
client.addInterceptor(new BasicAuthorizationInterceptor(username, password));
}

return client;
circuitBreaker = CircuitBreaker.create(
"cb-repository-bridge-client",
vertx,
new CircuitBreakerOptions()
.setMaxRetries(Integer.MAX_VALUE)
.setTimeout(2000))
.retryPolicy(retryCount -> retryDuration);

VertxCompletableFuture<WebClientInternal> completableConnection = VertxCompletableFuture.from(vertx, validateConnection(client));
if (completableConnection.isCompletedExceptionally()) {
throw new IllegalStateException("Unable to connect to the bridge server.");
}

return completableConnection.get();
}

private Future<WebClientInternal> validateConnection(WebClientInternal client) {
logger.info("Validate Bridge Server connection ...");
return circuitBreaker.execute(
future -> client.get("/_bridge").as(BodyCodec.string()).send(response -> {
if (response.succeeded()) {
HttpResponse<String> httpResponse = response.result();

if (httpResponse.statusCode() == HttpStatusCode.OK_200) {
JsonObject jsonObject = new JsonObject(httpResponse.body());
JsonObject version = jsonObject.getJsonObject("version");
if (version == null || !version.containsKey("MAJOR_VERSION")) {
String msg = "Invalid format response from Bridge Server. Retry.";
logger.error(msg);
future.fail(msg);
} else {
String serverVersion = version.getString("MAJOR_VERSION");

if (serverVersion.equals(clientVersion)) {
logger.info("Bridge Server connection successful.");
future.complete(client);
} else {
String msg = String.format(
"Bridge client and server versions vary (client:%s - server:%s). They must be the same.",
clientVersion,
serverVersion);
logger.error(msg);
throw new IllegalStateException(msg);
}
}
} else {
String msg = String.format("Invalid Bridge Server response. Retry in %s ms.", retryDuration);
logger.error(msg);
future.fail(msg);
}
} else {
String msg = String.format("Unable to connect to the Bridge Server. Retry in %s ms.", retryDuration);
logger.error(msg);
future.fail(msg);
}
}));
}

private WebClientOptions getWebClientOptions() {
Expand Down
Expand Up @@ -104,6 +104,9 @@ protected void doStart() throws Exception {
bridgeRouter.route().handler(BodyHandler.create());

// Create and associate handler
RootHandler rootHandler = new RootHandler();
bridgeRouter.get("/").handler(rootHandler);

// APIs handler
ApisHandler apisHandler = new ApisHandler();
applicationContext.getAutowireCapableBeanFactory().autowireBean(apisHandler);
Expand Down
@@ -0,0 +1,56 @@
/**
* Copyright (C) 2015 The Gravitee team (http://gravitee.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.gravitee.repository.bridge.server.handler;

import com.fasterxml.jackson.core.JsonProcessingException;
import io.gravitee.common.http.HttpHeaders;
import io.gravitee.common.http.HttpStatusCode;
import io.gravitee.common.http.MediaType;
import io.gravitee.common.util.Version;
import io.vertx.core.Handler;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* @author Nicolas GERAUD (nicolas.geraud at graviteesource.com)
* @author GraviteeSource Team
*/
public class RootHandler implements Handler<RoutingContext> {

private final Logger LOGGER = LoggerFactory.getLogger(RootHandler.class);

@Override
public void handle(RoutingContext ctx) {
HttpServerResponse response = ctx.response();
response.setStatusCode(HttpStatusCode.OK_200);
response.putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
response.setChunked(true);

JsonObject json = new JsonObject();
try {
json.put("version", new JsonObject(Json.prettyMapper.writeValueAsString(Version.RUNTIME_VERSION)));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
response.write(json.toString());

response.end();
}
}

0 comments on commit 8ceddde

Please sign in to comment.