feat: 获取app step 3
This commit is contained in:
parent
8f39152834
commit
8431e71fde
@ -76,8 +76,8 @@ public class ApiResult<T> {
|
|||||||
* 如果code > 100000 则认为可能已经带了appId
|
* 如果code > 100000 则认为可能已经带了appId
|
||||||
* 否则拼接当前的appId 到appCode中
|
* 否则拼接当前的appId 到appCode中
|
||||||
*/
|
*/
|
||||||
public Integer getStandardCode(String appId) {
|
public static Integer getStandardCode(String appId, Integer code) {
|
||||||
if (code == null || Strings.isNullOrEmpty(appId) || isSuccess()) {
|
if (code == null || Strings.isNullOrEmpty(appId) || SUCCESS_CODE.equals(code)) {
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
if (code >= 1000000) {
|
if (code >= 1000000) {
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
package cn.axzo.foundation.web.support.apps;
|
package cn.axzo.foundation.web.support.apps;
|
||||||
|
|
||||||
|
import cn.axzo.foundation.page.PageResp;
|
||||||
import cn.axzo.foundation.util.PageUtils;
|
import cn.axzo.foundation.util.PageUtils;
|
||||||
import cn.axzo.foundation.web.support.TimerRefreshCache;
|
import cn.axzo.foundation.web.support.TimerRefreshCache;
|
||||||
import cn.axzo.foundation.web.support.rpc.RequestProxy;
|
import cn.axzo.foundation.web.support.rpc.RequestProxy;
|
||||||
import cn.axzo.foundation.web.support.rpc.RpcClient;
|
import cn.axzo.foundation.web.support.rpc.RpcClient;
|
||||||
import cn.axzo.foundation.web.support.rpc.RpcClientImpl;
|
import cn.axzo.foundation.web.support.rpc.RpcClientImpl;
|
||||||
|
import com.alibaba.fastjson.JSONArray;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
@ -42,20 +44,30 @@ public class AppCenterImpl implements AppCenter {
|
|||||||
|
|
||||||
this.rpcClient = RpcClientImpl.builder().requestProxy(RequestProxy.SIMPLE_PROXY).build();
|
this.rpcClient = RpcClientImpl.builder().requestProxy(RequestProxy.SIMPLE_PROXY).build();
|
||||||
this.appCache = new TimerRefreshCache<>("appCenterCache", INITIAL_DELAY_MILLIS,
|
this.appCache = new TimerRefreshCache<>("appCenterCache", INITIAL_DELAY_MILLIS,
|
||||||
REFRESH_INTERVAL_MILLIS, executor, this::loadAllAppHosts);
|
REFRESH_INTERVAL_MILLIS, executor, this::loadApps);
|
||||||
this.debugHost = debugHost;
|
this.debugHost = debugHost;
|
||||||
this.debugAppRoutes = Optional.ofNullable(debugAppRoutes).orElse(ImmutableMap.of());
|
this.debugAppRoutes = Optional.ofNullable(debugAppRoutes).orElse(ImmutableMap.of());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, App> loadAllAppHosts() {
|
private Map<String, App> loadApps() {
|
||||||
String host = Optional.ofNullable(debugHost).map(e -> e + "/apisix-plat").orElse("http://apisix-plat:8080");
|
String host = Optional.ofNullable(debugHost).map(e -> e + "/apisix-plat").orElse("http://apisix-plat:8080");
|
||||||
List<App> apps = PageUtils.drainAll(page -> rpcClient.request()
|
List<App> apps = PageUtils.drainAll(page -> {
|
||||||
.url(host + "/api/v1/upstream/list")
|
JSONObject result = rpcClient.request()
|
||||||
.content(new JSONObject()
|
.url(host + "/api/v1/upstream/list")
|
||||||
.fluentPut("pageNum", page)
|
.content(new JSONObject()
|
||||||
.fluentPut("pageSize", 50))
|
.fluentPut("pageNum", page)
|
||||||
.clz(App.class)
|
.fluentPut("pageSize", 50))
|
||||||
.postAndGetPage());
|
.clz(JSONObject.class)
|
||||||
|
.post();
|
||||||
|
//结构不一样, 转换为自己的pageResp
|
||||||
|
return PageResp.<App>builder()
|
||||||
|
.total(result.getLong("totalCount"))
|
||||||
|
.current(result.getLong("page"))
|
||||||
|
.size(result.getLong("pageSize"))
|
||||||
|
.data(Optional.ofNullable(result.getJSONArray("list")).orElse(new JSONArray())
|
||||||
|
.toJavaList(App.class))
|
||||||
|
.build();
|
||||||
|
});
|
||||||
|
|
||||||
if (!Strings.isNullOrEmpty(debugHost)) {
|
if (!Strings.isNullOrEmpty(debugHost)) {
|
||||||
apps.forEach(e -> e.setHost(debugHost + "/" + debugAppRoutes.getOrDefault(e.getAppName(), e.getAppName())));
|
apps.forEach(e -> e.setHost(debugHost + "/" + debugAppRoutes.getOrDefault(e.getAppName(), e.getAppName())));
|
||||||
|
|||||||
@ -21,6 +21,6 @@ class ApiResultWrapper<T> extends ApiResult<T> {
|
|||||||
|
|
||||||
this.httpCode = Optional.ofNullable(result.getHttpCode()).orElse(ResultCode.DEFAULT_HTTP_ERROR_CODE);
|
this.httpCode = Optional.ofNullable(result.getHttpCode()).orElse(ResultCode.DEFAULT_HTTP_ERROR_CODE);
|
||||||
|
|
||||||
this.code = result.getStandardCode(appRuntime.getAppId());
|
this.code = getStandardCode(appRuntime.getAppId(), result.getCode());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -84,7 +84,7 @@ public class DefaultWebMvcConfig extends DelegatingWebMvcConfiguration implement
|
|||||||
private Boolean browserCompatible;
|
private Boolean browserCompatible;
|
||||||
|
|
||||||
@Value("${web.debug.host:}")
|
@Value("${web.debug.host:}")
|
||||||
private String contextSupplierHost;
|
private String debugHost;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 自定义对返回的errorMsg进行处理
|
* 自定义对返回的errorMsg进行处理
|
||||||
@ -195,7 +195,7 @@ public class DefaultWebMvcConfig extends DelegatingWebMvcConfiguration implement
|
|||||||
registry.addInterceptor(e);
|
registry.addInterceptor(e);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
registry.addInterceptor(new AxContextInterceptor(appRuntime, contextSupplierHost));
|
registry.addInterceptor(new AxContextInterceptor(appRuntime, debugHost));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -25,10 +25,6 @@ public class RpcClientImpl implements RpcClient {
|
|||||||
@Getter
|
@Getter
|
||||||
protected HttpClient httpClient;
|
protected HttpClient httpClient;
|
||||||
protected RequestProxy requestProxy;
|
protected RequestProxy requestProxy;
|
||||||
|
|
||||||
/** 外部服务的appId */
|
|
||||||
private String appId;
|
|
||||||
|
|
||||||
private static final Set<String> AXZO_HEADERS = ImmutableSet.of("workspaceId", "ouId", "Authorization", "terminal", "userinfo");
|
private static final Set<String> AXZO_HEADERS = ImmutableSet.of("workspaceId", "ouId", "Authorization", "terminal", "userinfo");
|
||||||
// XXX: http/2会把所有Header都转成小写, 历史定义的Header都是大写的,在http/2协议下会透传失败。
|
// XXX: http/2会把所有Header都转成小写, 历史定义的Header都是大写的,在http/2协议下会透传失败。
|
||||||
private static final TreeSet<String> CASE_INSENSITIVE_AXZO_HEADERS = AXZO_HEADERS.stream()
|
private static final TreeSet<String> CASE_INSENSITIVE_AXZO_HEADERS = AXZO_HEADERS.stream()
|
||||||
@ -52,15 +48,13 @@ public class RpcClientImpl implements RpcClient {
|
|||||||
@Builder
|
@Builder
|
||||||
public RpcClientImpl(RequestProxy requestProxy,
|
public RpcClientImpl(RequestProxy requestProxy,
|
||||||
HttpClient.Config config,
|
HttpClient.Config config,
|
||||||
Supplier<Map<String, String>> requestHeaderSupplier,
|
Supplier<Map<String, String>> requestHeaderSupplier) {
|
||||||
String appId) {
|
|
||||||
this.requestProxy = Optional.ofNullable(requestProxy).orElse(RequestProxy.SIMPLE_PROXY);
|
this.requestProxy = Optional.ofNullable(requestProxy).orElse(RequestProxy.SIMPLE_PROXY);
|
||||||
this.httpClient = OkHttpClientImpl.builder()
|
this.httpClient = OkHttpClientImpl.builder()
|
||||||
.config(config)
|
.config(config)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
this.customHeaderSupplier = requestHeaderSupplier;
|
this.customHeaderSupplier = requestHeaderSupplier;
|
||||||
this.appId = appId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -70,8 +64,7 @@ public class RpcClientImpl implements RpcClient {
|
|||||||
Optional<String> resp = requestBySupplier(requestParams, () -> this.getHttpClient().execute(httpMethod, url, requestParams));
|
Optional<String> resp = requestBySupplier(requestParams, () -> this.getHttpClient().execute(httpMethod, url, requestParams));
|
||||||
ApiResult<T> result = converter.apply(resp.orElse(StringUtils.EMPTY));
|
ApiResult<T> result = converter.apply(resp.orElse(StringUtils.EMPTY));
|
||||||
if (!result.isSuccess()) {
|
if (!result.isSuccess()) {
|
||||||
Integer standardCode = result.getStandardCode(appId);
|
throw new BusinessException(result.getCode() + "", result.getMsg());
|
||||||
throw new BusinessException(standardCode + "", result.getMsg());
|
|
||||||
}
|
}
|
||||||
return result.getData();
|
return result.getData();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
package cn.axzo.foundation.web.support.rpc;
|
package cn.axzo.foundation.web.support.rpc;
|
||||||
|
|
||||||
|
import cn.axzo.foundation.exception.BusinessException;
|
||||||
import cn.axzo.foundation.result.ApiResult;
|
import cn.axzo.foundation.result.ApiResult;
|
||||||
import cn.axzo.foundation.web.support.rpc.exception.RpcNetworkException;
|
import cn.axzo.foundation.web.support.apps.App;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.cache.Cache;
|
|
||||||
import com.google.common.cache.CacheBuilder;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@ -12,13 +11,9 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 一个RpcClient的wrapper. 可以方便的在RpcClient和其他Client切换.
|
* 一个RpcClient的wrapper. 可以方便的在RpcClient和其他Client切换.
|
||||||
@ -26,88 +21,54 @@ import java.util.stream.Collectors;
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class RpcClientWrapper implements RpcClient {
|
public class RpcClientWrapper implements RpcClient {
|
||||||
private final Supplier<List<String>> hostsResolver;
|
private final Supplier<App> appResolver;
|
||||||
private Supplier<String> hostResolver;
|
private final RpcClient normalRpcClient;
|
||||||
@Getter
|
@Getter
|
||||||
private volatile RpcClient activeRpcClient;
|
private RpcClient activeRpcClient;
|
||||||
private AtomicInteger roundRobinIndex = new AtomicInteger(0);
|
|
||||||
|
|
||||||
private RpcClient normalRpcClient;
|
|
||||||
|
|
||||||
final Cache<String, String> excludeHostCache = CacheBuilder.
|
|
||||||
newBuilder()
|
|
||||||
.expireAfterWrite(1, TimeUnit.MINUTES).build();
|
|
||||||
|
|
||||||
|
|
||||||
@lombok.Builder
|
@lombok.Builder
|
||||||
public RpcClientWrapper(RpcClient normalRpcClient,
|
public RpcClientWrapper(RpcClient normalRpcClient,
|
||||||
Supplier<String> hostResolver,
|
Supplier<App> appResolver,
|
||||||
Supplier<List<String>> hostsResolver,
|
|
||||||
ClientType defaultClientType) {
|
ClientType defaultClientType) {
|
||||||
Preconditions.checkArgument(normalRpcClient != null, "normalRpcClient不能为空");
|
Preconditions.checkArgument(normalRpcClient != null, "normalRpcClient不能为空");
|
||||||
if (normalRpcClient != null) {
|
Preconditions.checkArgument(appResolver != null, "如果是normalRpcClient, appResolver必须有值");
|
||||||
Preconditions.checkArgument(hostResolver != null || hostsResolver != null, "如果是normalRpcClient, hostResolver必须有值");
|
Preconditions.checkArgument(normalRpcClient instanceof RpcClientImpl, "normalRpcClient必须是RpcClientImpl.class实现");
|
||||||
Preconditions.checkArgument(normalRpcClient instanceof RpcClientImpl, "normalRpcClient必须是RpcClientImpl.class实现");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.normalRpcClient = normalRpcClient;
|
this.normalRpcClient = normalRpcClient;
|
||||||
this.hostResolver = hostResolver;
|
this.appResolver = appResolver;
|
||||||
this.hostsResolver = hostsResolver;
|
|
||||||
activate(Optional.ofNullable(defaultClientType).orElse(ClientType.NORMAL));
|
activate(Optional.ofNullable(defaultClientType).orElse(ClientType.NORMAL));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T execute(HttpClient.HttpMethod httpMethod, String path, RequestParams requestParams, Function<String, ApiResult<T>> converter) {
|
public <T> T execute(HttpClient.HttpMethod httpMethod, String path, RequestParams requestParams, Function<String, ApiResult<T>> converter) {
|
||||||
String host = resolveHost();
|
|
||||||
try {
|
try {
|
||||||
return activeRpcClient.execute(httpMethod, resolvePath(host, path), requestParams, converter);
|
return activeRpcClient.execute(httpMethod, resolvePath(resolveHost(), path), requestParams, converter);
|
||||||
} catch (RpcNetworkException e) {
|
} catch (BusinessException e) {
|
||||||
excludeHostCache.put(host, "1");
|
int code = Integer.parseInt(e.getErrorCode());
|
||||||
log.warn("get network exception, add host {} to invalid pool and cooldown 1 minutes", host);
|
Integer standardCode = ApiResult.getStandardCode(appResolver.get().getAppId(), code);
|
||||||
|
if (standardCode != code) {
|
||||||
|
throw new BusinessException(standardCode + "", e.getErrorMsg());
|
||||||
|
}
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <R> R execute(HttpClient.HttpMethod httpMethod, String path, RequestParams requestParams, BiFunction<byte[], Map<String, List<String>>, R> responder) {
|
public <R> R execute(HttpClient.HttpMethod httpMethod, String path, RequestParams requestParams, BiFunction<byte[], Map<String, List<String>>, R> responder) {
|
||||||
String host = resolveHost();
|
|
||||||
try {
|
try {
|
||||||
return activeRpcClient.execute(httpMethod, resolvePath(host, path), requestParams, responder);
|
return activeRpcClient.execute(httpMethod, resolvePath(resolveHost(), path), requestParams, responder);
|
||||||
} catch (RpcNetworkException e) {
|
} catch (BusinessException e) {
|
||||||
excludeHostCache.put(host, "1");
|
int code = Integer.parseInt(e.getErrorCode());
|
||||||
log.warn("get network exception, add host {} to invalid pool and cooldown 1 minutes", host);
|
Integer standardCode = ApiResult.getStandardCode(appResolver.get().getAppId(), code);
|
||||||
|
if (standardCode != code) {
|
||||||
|
throw new BusinessException(standardCode + "", e.getErrorMsg());
|
||||||
|
}
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String resolveHost() {
|
protected String resolveHost() {
|
||||||
if (hostResolver == null && hostsResolver == null) {
|
return appResolver.get().getHost();
|
||||||
return StringUtils.EMPTY;
|
|
||||||
}
|
|
||||||
// 如果hostResolver不为空, 且path没有包含protocol与host. 则尝试将host与path拼接
|
|
||||||
if (hostResolver != null) {
|
|
||||||
return hostResolver.get();
|
|
||||||
}
|
|
||||||
List<String> hosts = hostsResolver.get();
|
|
||||||
// 如果只有 1 个 host,没必要做选择。
|
|
||||||
if (hosts.size() == 1) {
|
|
||||||
return hosts.get(0);
|
|
||||||
}
|
|
||||||
ConcurrentMap<String, String> excludeHosts = excludeHostCache.asMap();
|
|
||||||
List<String> availables = excludeHosts.size() == 0 ? hosts : hosts.stream().filter(i -> !excludeHosts.containsKey(i)).collect(Collectors.toList());
|
|
||||||
if (availables.size() == 0) {
|
|
||||||
return hosts.get(0);
|
|
||||||
} else if (availables.size() == 1) {
|
|
||||||
return availables.get(0);
|
|
||||||
}
|
|
||||||
// 使用 round robin 算法,选择可用节点。
|
|
||||||
int index = roundRobinIndex.getAndAccumulate(1, (x, y) -> {
|
|
||||||
if ((x + y) >= availables.size()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return x + y;
|
|
||||||
});
|
|
||||||
return availables.get(index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String resolvePath(String host, String path) {
|
protected String resolvePath(String host, String path) {
|
||||||
@ -126,7 +87,7 @@ public class RpcClientWrapper implements RpcClient {
|
|||||||
public RpcClient activate(ClientType type) {
|
public RpcClient activate(ClientType type) {
|
||||||
if (type == ClientType.NORMAL) {
|
if (type == ClientType.NORMAL) {
|
||||||
Preconditions.checkState(normalRpcClient != null);
|
Preconditions.checkState(normalRpcClient != null);
|
||||||
Preconditions.checkState(hostResolver != null || hostsResolver != null);
|
Preconditions.checkState(appResolver != null);
|
||||||
activeRpcClient = normalRpcClient;
|
activeRpcClient = normalRpcClient;
|
||||||
} else {
|
} else {
|
||||||
throw new UnsupportedOperationException("unsupported clientType");
|
throw new UnsupportedOperationException("unsupported clientType");
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user