com.danielflower.apprunner.router.mgmt.ClusterQueryingMapManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of app-runner-router Show documentation
Show all versions of app-runner-router Show documentation
A reverse proxy for AppRunner that allows you horizontally scale AppRunner
package com.danielflower.apprunner.router.mgmt;
import com.danielflower.apprunner.router.web.ProxyMap;
import io.muserver.MuRequest;
import io.muserver.murp.ReverseProxy;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpMethod;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
public class ClusterQueryingMapManager implements MapManager {
private static final Logger log = LoggerFactory.getLogger(ClusterQueryingMapManager.class);
private final ExecutorService executorService = Executors.newCachedThreadPool();
private final ProxyMap proxyMap;
private final HttpClient httpClient;
public ClusterQueryingMapManager(ProxyMap proxyMap, HttpClient httpClient) {
this.proxyMap = proxyMap;
this.httpClient = httpClient;
}
@Override
public ConcurrentHashMap getCurrentMapping() {
return proxyMap.getAll();
}
@Override
public Result loadAllApps(MuRequest clientRequest, List runners) throws InterruptedException {
Result result = new Result();
log.info("Looking up app info from " + runners);
List> futures = new ArrayList<>();
for (Runner runner : runners) {
futures.add(executorService.submit(() -> loadRunner(clientRequest, runner)));
}
for (Future future : futures) {
try {
JSONObject appJson = future.get();
result.appsJsonFromEachRunner.add(appJson);
} catch (InterruptedException e) {
throw e;
} catch (Exception e) {
Throwable cause = e instanceof ExecutionException ? e.getCause() : e;
log.error(cause.getMessage());
result.errors.add(cause.getMessage());
}
}
log.info("Got " + result.appsJsonFromEachRunner.size() + " results");
return result;
}
@Override
public JSONObject loadRunner(MuRequest clientRequest, Runner runner) throws Exception {
URI uri = runner.url.resolve("/api/v1/apps");
JSONObject info = getJSONResponse(clientRequest, uri);
List addedNames = new ArrayList<>();
for (Object app : info.getJSONArray("apps")) {
String name = ((JSONObject) app).getString("name");
addedNames.add(name);
proxyMap.add(name, uri.resolve("/" + name));
}
for (Map.Entry entry : proxyMap.getAll().entrySet()) {
if (entry.getValue().getAuthority().equals(runner.url.getAuthority())
&& !addedNames.contains(entry.getKey())) {
log.info("Detected a missing app, so will remove it from the proxy map: " + entry.getKey() + " at " + entry.getValue());
proxyMap.remove(entry.getKey());
}
}
return info;
}
@Override
public JSONObject loadRunnerSystemInfo(MuRequest clientRequest, Runner runner) throws Exception {
URI uri = runner.url.resolve("/api/v1/system");
return getJSONResponse(clientRequest, uri);
}
private JSONObject getJSONResponse(MuRequest clientRequest, URI uri) throws InterruptedException, ExecutionException, TimeoutException {
ContentResponse resp;
try {
Request request = httpClient.newRequest(uri)
.timeout(10, TimeUnit.SECONDS)
.method(HttpMethod.GET);
if (clientRequest != null) {
ReverseProxy.setForwardedHeaders(clientRequest, request, false, true);
}
resp = request
.send();
} catch (TimeoutException e) {
throw new TimeoutException("Timed out calling " + uri);
}
if (resp.getStatus() != 200) {
throw new RuntimeException("Unable to load apps from " + uri + " - message was " + resp.getContentAsString());
}
return new JSONObject(resp.getContentAsString());
}
@Override
public void removeRunner(Runner runner) {
for (Map.Entry entry : proxyMap.getAll().entrySet()) {
if (entry.getValue().getAuthority().equals(runner.url.getAuthority())) {
proxyMap.remove(entry.getKey());
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy