CloudAccessWebApiServiceImpl中实现了多实例转发:
public OperationResponse<StartLaunchResponse> startLaunch(StartLaunchRequest startLaunchRequest) {
if (log.isDebugEnabled()) {
log.debug("startLaunchRequest is {}", CustomPIIMaskUtil.parseToStr(startLaunchRequest));
}
String siteKey = startLaunchRequest.getSiteKey();
String serverKey = startLaunchRequest.getServerKey();
String email = startLaunchRequest.getEmail();
String username = startLaunchRequest.getUsername();
String userId = startLaunchRequest.getUserId();
String accountId = getAccountIdByUserName(username);
String sessionId = startLaunchRequest.getSessionId();
String tplinkIdToken = startLaunchRequest.getTplinkIdToken();
if (StringUtil.isEmpty(serverKey)) {
log.warn("Invalid codedDeviceId, is empty");
return OperationResponse.error(ResponseCode.INVALID_REQUEST_PARAMS);
}
if (Objects.isNull(accountId)) {
log.error("tenant not fount, userId = {}", userId);
return OperationResponse.error(ResponseCode.TENANT_NOT_EXIST);
}
String deviceId = AESUtils.decrypt(serverKey);
if (log.isDebugEnabled()) {
log.debug("{} start launch to {}", CustomPIIMaskUtil.hash(startLaunchRequest.getEmail()), CustomPIIMaskUtil.encrypt(deviceId));
}
DeviceDTO device;
try {
OperationResponse<DeviceDTO> deviceResponse = getDeviceBasicInfoWithOutCache(accountId, deviceId,
platformCloudProps.getHost());
device = deviceResponse.getResult();
if (deviceResponse.isError() || Objects.isNull(device)) {
log.warn("Failed get deviceInfo of {} for {} , err is {}:{}",
CustomPIIMaskUtil.encrypt(deviceId), CustomPIIMaskUtil.hash(email), deviceResponse.getErrorCode(),
deviceResponse.getMessage());
if (deviceResponse.getErrorCode() == ResponseCode.CLOUD_ACCOUNT_NOT_BOUND.getCode()) {
return OperationResponse.error(ResponseCode.LAUNCH_FAILED_DEVICE_NOT_BOUND);
} else {
return OperationResponse.error(ResponseCode.LAUNCH_FAILED);
}
}
} catch (Exception ex) {
log.error(ex.toString(), ex);
log.warn("Failed get deviceInfo of {} for {}", CustomPIIMaskUtil.encrypt(deviceId),
CustomPIIMaskUtil.hash(email));
return OperationResponse.error(ResponseCode.LAUNCH_FAILED);
}
if (device.getStatus() != Constant.ONLINE) {
log.warn("Launch failed, device {} is offline with {} ", CustomPIIMaskUtil.encrypt(deviceId),
device.getAppServerUrl());
return OperationResponse.error(ResponseCode.CLOUD_DEVICE_OFFLINE);
}
int controllerVerInt;
try {
controllerVerInt = VersionUtils.parseFwVersion(device.getControllerVersion());
if (controllerVerInt < 0) {
log.error("Invalid vms version {}", device.getControllerVersion());
return OperationResponse.error(ResponseCode.INTERNAL_SERVER_ERROR);
}
} catch (Exception ex) {
log.error(ex.toString(), ex);
return OperationResponse.error(ResponseCode.INTERNAL_SERVER_ERROR);
}
if (!proxyHttpServer.isDeviceConnectedLocal(deviceId)) {
log.debug("Device: {} not connected to this server, need to forward.", deviceId);
// 获取可用serverIP列表
List<String> serverIps = proxyHttpServer.findServerIpsByServerKey(ProxyUtils.generateServerKey(deviceId));
if (CollectionUtils.isEmpty(serverIps)) {
log.warn("failed to get serverIps from cache for {}, going to reconnect", CustomPIIMaskUtil.encrypt(deviceId));
return OperationResponse.error(ResponseCode.GENERAL_ERROR);
}
// 构建跨实例请求
InternalForwardLaunchRequest forwardLaunchRequest = InternalForwardLaunchRequest.builder()
.serverKey(serverKey)
.email(email)
.username(username)
.userId(userId)
.sessionId(sessionId)
.tplinkIdToken(tplinkIdToken)
.build();
return httpForwardService.forwardLaunchRequest(serverIps, forwardLaunchRequest);
}
// 检查状态,防止同一用户同时点击多次launch造成并发冲突
synchronized (ProxyClientsCache.getConnectionLock(deviceId)) {
DevLaunchInfo devLaunchStatus = cloudAccessCache.getDevLaunchInfo(deviceId);
if (devLaunchStatus != null
&& Objects.equals(devLaunchStatus.getLaunchStatus(), LaunchStatus.CONNECTING)
&& TimeUtil.isNotTimeoutS(devLaunchStatus.getUpdateTime(), Constant.PROXY_CONNECTING_TIMEOUT_S)) {
if (log.isDebugEnabled()) {
log.debug("Is connecting now, please wait {}.", CustomPIIMaskUtil.hash(email));
}
// 直接返回成功
return OperationResponse.ok();
}
}
return launchToController(serverKey, email, userId, accountId, sessionId, tplinkIdToken, siteKey, device,
controllerVerInt);
}
private OperationResponse launchToController(String serverKey, String email, String userId, String accountId,
String sessionId, String tplinkIdToken, String siteKey, DeviceDTO device, int controllerVerInt) {
try {
launchThreadPool.execute(() -> {
// 检查状态,防止同一用户同时点击多次launch造成并发冲突
synchronized (ProxyClientsCache.getConnectionLock(device.getDeviceId())) {
DevLaunchInfo devLaunchStatus = cloudAccessCache.getDevLaunchInfo(device.getDeviceId());
if (Objects.isNull(devLaunchStatus)) {
devLaunchStatus = new DevLaunchInfo(LaunchStatus.CONNECTING, TimeUtil.getCurrentTimeS(),
controllerVerInt);
} else {
if (Objects.equals(devLaunchStatus.getLaunchStatus(), LaunchStatus.CONNECTING)
&& TimeUtil.isNotTimeoutS(devLaunchStatus.getUpdateTime(), Constant.PROXY_CONNECTING_TIMEOUT_S)) {
if (log.isDebugEnabled()) {
log.debug("Is connecting now, please wait {}.", CustomPIIMaskUtil.hash(email));
}
launchStatusProcessPublisher.publishLaunchStatusProcess(userId, serverKey,
LaunchStatusProgress.builder()
.status(LaunchStatus.CONNECTING.getValue())
.build());
return;
}
devLaunchStatus.setControllerVer(controllerVerInt);
// 不管当前连接是何种状态,均重连
devLaunchStatus.setLaunchStatus(LaunchStatus.CONNECTING);
devLaunchStatus.setUpdateTime(TimeUtil.getCurrentTimeS());
}
cloudAccessCache.saveDevLaunchInfo(device.getDeviceId(), devLaunchStatus);
}
// OperationResponse optRes = createProxyChannel(email, tplinkIdToken, accountId, siteKey, device,
// sessionId);
com.tplink.smb.cloud.common.response.OperationResponse optRes = proxyHttpServer.handleLaunchRequest(tplinkIdToken, accountId, sessionId, device.getDeviceId(), Constant.LAUNCH_TIMEOUT_S);
// 对于LAUNCH_FAILED_CHECK_DEVICE场景,自动重试一次
// 后续会在VMS上增加自动尝试逻辑,增加后应该基本不会返回此错误
if (optRes.getErrorCode() == ResponseCode.LAUNCH_FAILED_CHECK_DEVICE.getCode()) {
log.info("Failed to createProxyChannel to {} as LAUNCH_FAILED_CHECK_DEVICE, try again!",
CustomPIIMaskUtil.encrypt(device.getDeviceId()));
launchStatusProcessPublisher.publishLaunchStatusProcess(userId, serverKey,
LaunchStatusProgress.builder()
.status(LaunchStatus.CONNECTING.getValue())
.build());
// optRes = createProxyChannel(email, tplinkIdToken, accountId, siteKey, device, sessionId);
optRes = proxyHttpServer.handleLaunchRequest(tplinkIdToken, accountId, sessionId, device.getDeviceId(), Constant.LAUNCH_TIMEOUT_S);
}
log.debug("Launch response is {}: {}, resData: {}", optRes.getErrorCode(), optRes.getMsg(), optRes.getResult());
synchronized (ProxyClientsCache.getConnectionLock(device.getDeviceId())) {
DevLaunchInfo devLaunchStatus = cloudAccessCache.getDevLaunchInfo(device.getDeviceId());
if (Objects.isNull(devLaunchStatus)) {
log.error("Failed to find devLaunchStatus {} for {} in session {}", CustomPIIMaskUtil.encrypt(device.getDeviceId()),
CustomPIIMaskUtil.hash(email), sessionId);
launchStatusProcessPublisher.publishLaunchStatusProcess(userId, serverKey,
LaunchStatusProgress.builder()
.status(LaunchStatus.CONNECT_FAILED.getValue())
.failedCode(ResponseCode.GENERAL_ERROR.getCode())
.build());
return;
}
if (optRes.isOk()) {
if (log.isDebugEnabled()) {
log.debug("launch success for {}", CustomPIIMaskUtil.hash(email));
}
Map<String, Object> resultMap = (Map<String, Object>) optRes.getResult();
String serverUrl = (String) resultMap.get(ProxyConstVal.SERVER_URL);
String[] split = serverUrl.split(";");
String vmsCloudUrl;
String internalIP = null;
if (split.length == 2) {
vmsCloudUrl = split[0];
internalIP = split[1];
} else {
vmsCloudUrl = serverUrl;
}
RegionEnum region = RegionUtils.getRegionByServiceUrl(vmsCloudUrl);
log.debug("vmsCloudUrl is {}, internalIP is {}, region is {}", vmsCloudUrl, internalIP, region);
if (Objects.nonNull(region)) {
devLaunchStatus.setLaunchStatus(LaunchStatus.CONNECTED);
devLaunchStatus.setVmsRegion(region.getIndex());
devLaunchStatus.setVmsCloudUrl(vmsCloudUrl);
// 当internalIP不为空时设置internalIP,否则设为空
devLaunchStatus.setInternalIP(StringUtil.isEmpty(internalIP) ? null : internalIP);
devLaunchStatus.setUpdateTime(TimeUtil.getCurrentTimeS());
devLaunchStatus.setErrCode(ResponseCode.OK.getCode());
Region regionEnum = Region.parse(region.getRegion());
Boolean regionPrefix = selfServiceProps.getRegionPrefix().getOrDefault(regionEnum, null);
String webRegionUrl;
if (Boolean.TRUE.equals(regionPrefix)) {
webRegionUrl =
"https://" + region.getRegion() + "-" + selfServiceProps.getDefaultBusinessUrl();
} else {
webRegionUrl = "https://" + selfServiceProps.getDefaultBusinessUrl();
}
launchStatusProcessPublisher.publishLaunchStatusProcess(userId, serverKey,
LaunchStatusProgress.builder()
.status(LaunchStatus.CONNECTED.getValue())
.vmsCloudUrl(vmsCloudUrl)
.webRegionUrl(webRegionUrl)
.build());
} else {
devLaunchStatus.setLaunchStatus(LaunchStatus.CONNECT_FAILED);
devLaunchStatus.setUpdateTime(TimeUtil.getCurrentTimeS());
devLaunchStatus.setErrCode(ResponseCode.INVALID_VMS_CLOUD_URL.getCode());
log.warn("Invalid vms cloud url {} for {}", vmsCloudUrl, CustomPIIMaskUtil.hash(email));
launchStatusProcessPublisher.publishLaunchStatusProcess(userId, serverKey,
LaunchStatusProgress.builder()
.status(LaunchStatus.CONNECT_FAILED.getValue())
.failedCode(ResponseCode.INVALID_VMS_CLOUD_URL.getCode())
.build());
}
} else {
log.warn("{} launch to vms {} with deviceId {} failed, err is {}", CustomPIIMaskUtil.hash(email),
device.getControllerVersion(), CustomPIIMaskUtil.encrypt(device.getDeviceId()), optRes.getMsg());
// 需要判断一下当前LaunchStatus的状态,只有是CONNECTING时才覆盖。
if (Objects.equals(devLaunchStatus.getLaunchStatus(), LaunchStatus.CONNECTING)) {
devLaunchStatus.setLaunchStatus(LaunchStatus.CONNECT_FAILED);
devLaunchStatus.setUpdateTime(TimeUtil.getCurrentTimeS());
devLaunchStatus.setErrCode(optRes.getErrorCode());
}
launchStatusProcessPublisher.publishLaunchStatusProcess(userId, serverKey,
LaunchStatusProgress.builder()
.status(LaunchStatus.CONNECT_FAILED.getValue())
.failedCode(optRes.getErrorCode())
.build());
}
cloudAccessCache.saveDevLaunchInfo(device.getDeviceId(), devLaunchStatus);
}
});
} catch (RejectedExecutionException ex) {
log.error(ex.toString(), ex);
return OperationResponse.error(ResponseCode.LAUNCH_THREAD_INSUFFICIENT);
}
return OperationResponse.ok();
}
public OperationResponse<StartLaunchResponse> handleInternalForwardLaunch(HandleInternalForwardLaunchRequest request) {
if (log.isDebugEnabled()) {
log.debug("HandleInternalForwardLaunchRequest is {}", CustomPIIMaskUtil.parseToStr(request));
}
String siteKey = request.getSiteKey();
String serverKey = request.getServerKey();
String email = request.getEmail();
String username = request.getUsername();
String userId = request.getUserId();
String accountId = getAccountIdByUserName(username);
String sessionId = request.getSessionId();
String tplinkIdToken = request.getTplinkIdToken();
if (StringUtil.isEmpty(serverKey)) {
log.warn("Invalid codedDeviceId, is empty");
return OperationResponse.error(ResponseCode.INVALID_REQUEST_PARAMS);
}
if (Objects.isNull(accountId)) {
log.error("tenant not fount, userId = {}", userId);
return OperationResponse.error(ResponseCode.TENANT_NOT_EXIST);
}
String deviceId = AESUtils.decrypt(serverKey);
if (log.isDebugEnabled()) {
log.debug("{} start launch to {}", CustomPIIMaskUtil.hash(request.getEmail()), CustomPIIMaskUtil.encrypt(deviceId));
}
DeviceDTO device;
try {
OperationResponse<DeviceDTO> deviceResponse = getDeviceBasicInfoWithOutCache(accountId, deviceId,
platformCloudProps.getHost());
device = deviceResponse.getResult();
if (deviceResponse.isError() || Objects.isNull(device)) {
log.warn("Failed get deviceInfo of {} for {} , err is {}:{}",
CustomPIIMaskUtil.encrypt(deviceId), CustomPIIMaskUtil.hash(email), deviceResponse.getErrorCode(),
deviceResponse.getMessage());
if (deviceResponse.getErrorCode() == ResponseCode.CLOUD_ACCOUNT_NOT_BOUND.getCode()) {
return OperationResponse.error(ResponseCode.LAUNCH_FAILED_DEVICE_NOT_BOUND);
} else {
return OperationResponse.error(ResponseCode.LAUNCH_FAILED);
}
}
} catch (Exception ex) {
log.error(ex.toString(), ex);
log.warn("Failed get deviceInfo of {} for {}", CustomPIIMaskUtil.encrypt(deviceId),
CustomPIIMaskUtil.hash(email));
return OperationResponse.error(ResponseCode.LAUNCH_FAILED);
}
if (device.getStatus() != Constant.ONLINE) {
log.warn("Launch failed, device {} is offline with {} ", CustomPIIMaskUtil.encrypt(deviceId),
device.getAppServerUrl());
return OperationResponse.error(ResponseCode.CLOUD_DEVICE_OFFLINE);
}
int controllerVerInt;
try {
controllerVerInt = VersionUtils.parseFwVersion(device.getControllerVersion());
if (controllerVerInt < 0) {
log.error("Invalid vms version {}", device.getControllerVersion());
return OperationResponse.error(ResponseCode.INTERNAL_SERVER_ERROR);
}
} catch (Exception ex) {
log.error(ex.toString(), ex);
return OperationResponse.error(ResponseCode.INTERNAL_SERVER_ERROR);
}
if (proxyHttpServer.isDeviceConnectedLocal(deviceId)) {
synchronized (ProxyClientsCache.getConnectionLock(deviceId)) {
DevLaunchInfo devLaunchStatus = cloudAccessCache.getDevLaunchInfo(deviceId);
if (devLaunchStatus != null
&& Objects.equals(devLaunchStatus.getLaunchStatus(), LaunchStatus.CONNECTING)
&& TimeUtil.isNotTimeoutS(devLaunchStatus.getUpdateTime(), Constant.PROXY_CONNECTING_TIMEOUT_S)) {
if (log.isDebugEnabled()) {
log.debug("Is connecting now, please wait {}.", CustomPIIMaskUtil.hash(email));
}
// 直接返回成功
return OperationResponse.ok();
}
}
return launchToController(serverKey, email, userId, accountId, sessionId,
tplinkIdToken, siteKey, device, controllerVerInt);
} else {
log.debug("device {} belongs to this server, but not connected, please reconnect", deviceId);
// 通知原服务器重连
request.getResponse().setHeader(Constant.ERROR_HEADER_NAME, Constant.NEED_RECONNECT);
return OperationResponse.error(ResponseCode.PROXY_DISCONNECTED_NEED_RECONNECT);
}
}
HttpForwardServiceImpl实现了httpForwardService.forwardLaunchRequest
@Override
public OperationResponse forwardLaunchRequest(List<String> serverIps, InternalForwardLaunchRequest request) {
try {
int internalForwardPort = cloudAccessProps.getPortFilter().getInternalLocalPort();
String url = String.format(Constant.LAUNCH_INTERNAL_FORWARD_URL,
serverIps.get(0),
internalForwardPort,
request.getServerKey(),
request.getEmail(),
request.getUsername(),
request.getUserId(),
request.getSessionId(),
request.getTplinkIdToken());
log.debug("forward launch disconnect request to same region other server : {}", url);
HttpClientExecutor executor = buildLaunchExecutor(url);
executor.addHeaderParam(Constant.HEADER_CLOUD_REQUEST, "true");
return forwardLaunchReq2OtherServer(executor);
} catch (Exception ex) {
log.warn(ex.toString(), ex);
return OperationResponse.error(ResponseCode.GENERAL_ERROR);
}
}
@Nonnull
private HttpClientExecutor buildLaunchExecutor(String urlStr) {
HttpClientExecutor executor = nbuCloudExecutorFactory.getPostExecutor(urlStr);
executor.maxConnectionSeconds(Constant.INTERNAL_FORWARD_MAX_CONN_SECONDS);
executor.maxSocketSeconds(Constant.INTERNAL_FORWARD_MAX_SOCKET_SECONDS);
return executor;
}
private OperationResponse forwardLaunchReq2OtherServer(HttpClientExecutor executor) {
HttpForwardWsResponseHandler responseHandler = new HttpForwardWsResponseHandler();
HttpClientErrCode errCode = executor.execute(responseHandler);
if (!Objects.equals(errCode, HttpClientErrCode.OK)) {
log.warn("execute forwardLaunchReq2OtherServer failed, err is {}", errCode.getMsg());
if (Objects.equals(errCode, HttpClientErrCode.CONNECTION_TIMEOUT)) {
return OperationResponse.error(ResponseCode.LAUNCH_FAILED_NETWORK_TIMEOUT);
} else {
return OperationResponse.error(ResponseCode.LAUNCH_FAILED);
}
}
ResErrorCode errorCode = responseHandler.getResErrorCode();
if (Objects.nonNull(errorCode)) {
log.warn("forwardLaunchReq2OtherServer failed, err is {}", errorCode.getErrMsg());
return OperationResponse.error(errorCode.getErrCode(), errorCode.getErrMsg());
}
return OperationResponse.ok();
}
public static final String LAUNCH_INTERNAL_FORWARD_URL =
"http://%s:%s/v1/internal/internalForwardLaunchReq?serverKey=%s&email=%s&username=%s&userId=%s&sessionId=%s&tplinkIdToken=%s";
然后InternalController中接收请求:
@PostMapping("/v1/internal/internalForwardLaunchReq")
public OperationResponse internalForwardLaunch(HttpServletResponse response, String serverKey,
String email, String username, String userId, String sessionId, String tplinkIdToken) {
HandleInternalForwardLaunchRequest request = HandleInternalForwardLaunchRequest.builder()
.response(response)
.serverKey(serverKey)
.email(email)
.username(username)
.userId(userId)
.sessionId(sessionId)
.tplinkIdToken(tplinkIdToken)
.build();
return cloudAccessWebApiService.handleInternalForwardLaunch(request);
}
ProxyHttpServerImpl中实现了proxyHttpServer.handleLaunchRequest
@Override
public OperationResponse handleLaunchRequest(String tplinkIdToken, String accountId, String sessionId, String deviceId, int timeOutS) {
log.debug("handleLaunchRequest");
if (StringUtils.isEmpty(tplinkIdToken) || StringUtils.isEmpty(accountId) || StringUtils.isEmpty(sessionId) || StringUtils.isEmpty(deviceId)) {
log.warn("Invalid Launch request");
return OperationResponse.error(ResErrorCode.INVALID_REQUEST_PARAMS);
}
String launchReqId = ProxyUtils.generateLaunchReqId();
long currentStage;
try{
synchronized (ProxyClientsCache.getConnectionLock(deviceId)) {
ProxyClient proxyClient = ProxyClientsCache.getProxyConnection(deviceId);
if (Objects.isNull(proxyClient) || !proxyClient.isConnected()) {
// 测试日志
if (Objects.isNull(proxyClient)) {
log.debug("proxyClient {} is null!", deviceId);
} else {
log.debug("The connection status of proxyClient {} is {}", deviceId, proxyClient.isConnected());
}
log.debug(PROXY_CLIENT_DISCONNECTED, deviceId);
return OperationResponse.error(ResErrorCode.PROXY_DISCONNECTED_NEED_RECONNECT);
}
log.debug("current launch request, proxyClient is {}", proxyClient);
ProxyRequestsCache.cacheLaunchReqInfo(launchReqId);
// 构建Launch请求
DefaultFullHttpRequest request = ProxyRequestBuilder.buildRestfulLaunchRequest(tplinkIdToken, accountId, sessionId, deviceId, launchReqId);
ProxyClientsCache.sendProxyMessage(proxyClient.getCtx(), request);
// 更新 proxy 连接的 updateTime
proxyClient.setUpdateTime(TimeUtils.getCurrentTimeS());
ProxyClientsCache.cacheProxyConnection(deviceId, proxyClient);
currentStage = proxyClient.getStage();
}
OperationResponse operationResponse = waitAndGetLaunchResult(deviceId, timeOutS,
currentStage, launchReqId);
ProxyRequestsCache.removeLaunchReqInfo(launchReqId);
log.debug("launch response is {}: {}", operationResponse.getErrorCode(), operationResponse.getMsg());
return operationResponse;
} catch (URISyntaxException e) {
log.error("Build launch request URI failed: {}", e.getMessage());
return OperationResponse.error(ResErrorCode.GENERAL_ERROR);
} catch (Exception e) {
log.error("Launch request failed: {}", e.getMessage());
return OperationResponse.error(ResErrorCode.GENERAL_ERROR);
}
}
private OperationResponse waitAndGetLaunchResult(String deviceId, int timeOutS, long stage, String launchReqId) {
log.debug("waitAndGetLaunchResult");
int waitCount = 0;
// 轮询间隔 2ms
int sleepInternalMs = 2;
int waitCountMax = timeOutS * 500;
ResErrorCode resErrorCode = ResErrorCode.LAUNCH_TIMEOUT;
while(!ProxyRequestsCache.launchReqIsFinished(launchReqId) && waitCount < waitCountMax) {
synchronized (ProxyClientsCache.getConnectionLock(deviceId)) {
ProxyClient proxyClient = ProxyClientsCache.getProxyConnection(deviceId);
if (proxyClient == null || !proxyClient.isConnected() || stage != proxyClient.getStage()) {
log.debug(PROXY_CLIENT_DISCONNECTED, deviceId);
resErrorCode = ResErrorCode.PROXY_DISCONNECTED_NEED_RECONNECT;
break;
}
}
try {
Thread.sleep(sleepInternalMs);
} catch (InterruptedException e) {
log.warn("get an exception while waiting connect result {}", e.toString(), e);
Thread.currentThread().interrupt();
}
waitCount++;
}
if (ProxyRequestsCache.launchReqIsFinished(launchReqId)) {
OperationResponse optResp = ProxyRequestsCache.getLaunchReqResult(launchReqId);
if (optResp == null) {
log.error("Failed to find LaunchReqInfo for {}", deviceId);
return OperationResponse.error(ResErrorCode.GENERAL_ERROR, "Failed to find LaunchReqInfo");
} else if (optResp.isError()) {
log.warn("Failed to launch for {}, err is {}: {}", launchReqId, optResp.getErrorCode(), optResp.getMsg());
}
return optResp;
} else {
ProxyRequestsCache.recordLaunchReqInfo(launchReqId, OperationResponse.error(resErrorCode));
return OperationResponse.error(resErrorCode);
}
}
ProxyUtils静态类用于生成launchReqId:
private static final AtomicLong ATOMIC_LONG_LAUNCH = new AtomicLong(0L);
@Nonnull
public static String generateLaunchReqId() {
return String.valueOf(ATOMIC_LONG_LAUNCH.incrementAndGet());
}
ProxyRequestCache用于缓存launchReqId与对应的信息:
private static final Map<String, LaunchReqInfo> LAUNCH_MAP = new ConcurrentHashMap<>();
public static void cacheLaunchReqInfo(String launchReqId) {
log.debug("cache LaunchReqInfo: {}", launchReqId);
LAUNCH_MAP.put(launchReqId, new LaunchReqInfo());
}
public static void removeLaunchReqInfo(String launchReqId) {
log.debug("remove LaunchReqInfo: {}", launchReqId);
LAUNCH_MAP.remove(launchReqId);
}
public static boolean recordLaunchReqInfo(String launchReqId, OperationResponse operationResponse) {
LaunchReqInfo launchReqInfo = LAUNCH_MAP.get(launchReqId);
if (launchReqInfo != null && !launchReqInfo.isFinished()) {
launchReqInfo.setErrCode(operationResponse.getErrorCode());
launchReqInfo.setErrMsg(operationResponse.getMsg());
launchReqInfo.setResult(operationResponse.getResult());
launchReqInfo.setFinished(true);
return true;
} else {
log.debug("LaunchReqInfo for {} is {}", launchReqId, launchReqInfo);
}
return false;
}
public static boolean launchReqIsFinished(String launchReqId) {
LaunchReqInfo launchReqInfo = LAUNCH_MAP.get(launchReqId);
if (launchReqInfo != null) {
return launchReqInfo.isFinished();
}else {
return true;
}
}
@Nullable
public static OperationResponse getLaunchReqResult(String launchReqId) {
LaunchReqInfo launchReqInfo = LAUNCH_MAP.get(launchReqId);
if (launchReqInfo != null) {
return OperationResponse.error(ResErrorCode.valueOf(launchReqInfo.getErrCode()), launchReqInfo.getResult());
} else {
return null;
}
}
@Getter
@Setter
private static class LaunchReqInfo<T> {
private int errCode;
private String errMsg;
/**
* 请求是否完成(失败或接收到响应)
*/
private boolean finished;
private T result;
public LaunchReqInfo() {
this.finished = false;
this.errCode = 0;
this.errMsg = null;
this.result = null;
}
}
ProxyRequestBuilder构造请求:
/**
* 构造Launch请求
*/
public static DefaultFullHttpRequest buildRestfulLaunchRequest(String tplinkIdToken, String accountId, String sessionId, String deviceId, String launchReqId)
throws URISyntaxException {
JSONObject reqJson = new JSONObject();
reqJson.put(ProxyConstVal.TPLINK_ID_TOKEN, tplinkIdToken);
reqJson.put(ProxyConstVal.ACCOUNT_ID, accountId);
reqJson.put(ProxyConstVal.SESSION_ID, sessionId);
String serverKey = ProxyUtils.generateServerKey(deviceId);
String strUri = String.format(ProxyUriEnum.LAUNCH_URI.getUri(), launchReqId);
URI uri = new URI(strUri);
DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST,
uri.toASCIIString(), Unpooled.wrappedBuffer(reqJson.toString().getBytes(StandardCharsets.UTF_8)));
request.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
request.headers().set(HttpHeaderNames.CONTENT_LENGTH, request.content().readableBytes());
request.headers().set(HttpHeaderNames.COOKIE, ProxyConstVal.DT_TOKEN + serverKey);
log.debug("request is {}, requestBody is {}", request, request.content().toString(CharsetUtil.UTF_8));
return request;
}
目前出现的问题:有两个实例A与B,假设连接在实例A上,实例B能够转发launch请求给A,也能够得到响应,但是会出现重复的launchReqId,比如直接发送到实例A的launch请求的launchReqId为1、2、3、4、5、6,但是由实例B转发给实例A的launch请求的launchReqId却是1、2、6,理想情况launchReqId应该是单调递增的,为什么会这样?