[server] [func.xray] - 完成xray 的对象存储,任务分发全流程 - 1

This commit is contained in:
zeaslity
2023-02-15 15:53:59 +08:00
parent be7f1a9108
commit 864d290fe7
13 changed files with 528 additions and 73 deletions

View File

@@ -14,4 +14,6 @@ public enum OctopusMessageType {
// update or report agent status
STATUS
//
}

View File

@@ -8,6 +8,7 @@ import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.wdd.common.beans.response.R;
import io.wdd.func.oss.config.OctopusObjectSummary;
import io.wdd.func.oss.config.OssConfig;
import io.wdd.func.oss.service.OSSCoreService;
import io.wdd.func.oss.service.OssBackendSelect;
@@ -125,9 +126,29 @@ public class OSSController {
));
}
@PostMapping("/object/simple")
@ApiOperation("[对象] - 简单查询")
public R<OctopusObjectSummary> objectDetailSimple(
@RequestParam(value = "BackendPrefixName", required = true)
@ApiParam(value = "BackendPrefixName") String BackendPrefixName,
@RequestParam(value = "bucketName", required = true)
@ApiParam(value = "bucketName") String bucketName,
@RequestParam(value = "objectName", required = true)
@ApiParam(value = "objectName") String objectName
) {
OssConfig ossConfig = ossBackendSelect.one(BackendPrefixName);
return R.ok(OSSCoreService.getObjectSimple(
ossConfig,
bucketName,
objectName
));
}
@PostMapping("/object/one")
@ApiOperation("[对象] - 查询下载地址")
@ApiOperation("[对象] - 默认查询")
public R<S3Object> objectDetailOne(
@RequestParam(value = "BackendPrefixName", required = true)
@ApiParam(value = "BackendPrefixName") String BackendPrefixName,

View File

@@ -1,25 +1,67 @@
package io.wdd.func.controller;
import io.wdd.func.xray.beans.node.ProxyNode;
import io.wdd.func.xray.service.XrayConfigDistribute;
import io.wdd.func.xray.service.XrayCoreService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import static io.wdd.func.xray.beans.node.ProxyNodeSet.*;
import static io.wdd.func.xray.beans.node.ProxyNodeSet.phoenix2;
@RestController
@RequestMapping("/server/func/xray/")
@RequestMapping("/server/func/xray")
public class XrayController {
@Resource
XrayCoreService xrayCoreService;
@Resource
XrayConfigDistribute xrayConfigDistribute;
@GetMapping("/test")
public void test(){
xrayCoreService.generateXrayJsonFromNodeList(null);
ArrayList<ArrayList<ProxyNode>> allNetworkPathList = new ArrayList<>();
ArrayList<ProxyNode> pathA = new ArrayList<>(
Arrays.asList(
shanghai,
seoul2,
tokyo2,
phoenix2
)
);
/*ArrayList<ProxyNode> pathB = new ArrayList<>(
Arrays.asList(
shanghai,
seoul2,
london2
)
);
ArrayList<ProxyNode> pathC = new ArrayList<>(
Arrays.asList(
seoul2,
phoenix2
)
);*/
allNetworkPathList.add(pathA);
// allNetworkPathList.add(pathB);
// allNetworkPathList.add(pathC);
xrayCoreService.generateXrayJsonFromNodeList(allNetworkPathList);
xrayConfigDistribute.uploadXrayConfigToOSS(allNetworkPathList.get(0));
System.out.println("结束!");

View File

@@ -0,0 +1,74 @@
package io.wdd.func.oss.config;
import com.amazonaws.services.s3.model.Owner;
import io.swagger.annotations.ApiModel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.net.URI;
import java.util.Date;
/**
* 参考 com.amazonaws.services.s3.model.S3ObjectSummary
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder(toBuilder = true)
@ApiModel("Octopus项目使用的对象存储的标准模型")
public class OctopusObjectSummary {
/**
* The name of the bucket in which this object is stored
*/
String bucketName;
/**
* The key under which this object is stored
*/
String key;
/**
* Hex encoded MD5 hash of this object's contents, as computed by Amazon S3
*/
String eTag;
/**
* The size of this object, in bytes
*/
long size;
/**
* The date, according to Amazon S3, when this object was last modified
*/
Date lastModified;
/**
* The class of storage used by Amazon S3 to store this object
*/
String storageClass;
String contentType;
/**
* The owner of this object - can be null if the requester doesn't have
* permission to view object ownership information
*/
Owner owner;
AccessDTO access;
@Data
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder(toBuilder = true)
public static class AccessDTO {
URI originUrl;
String cloudflareUrl;
}
}

View File

@@ -4,6 +4,7 @@ import com.amazonaws.services.s3.model.Bucket;
import com.amazonaws.services.s3.model.ObjectListing;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.S3Object;
import io.wdd.func.oss.config.OctopusObjectSummary;
import io.wdd.func.oss.config.OssConfig;
import org.springframework.web.multipart.MultipartFile;
@@ -93,6 +94,18 @@ public interface OSSCoreService {
* */
/**
* Retrieves an object.
* https://docs.oracle.com/en-us/iaas/api/#/en/s3objectstorage/20160918/Object/GetObject
*
* @param ossConfig
* @param bucketName
* @param objectName
* @return
*/
OctopusObjectSummary getObjectSimple(OssConfig ossConfig, String bucketName, String objectName);
/**
* Retrieves an object.
* https://docs.oracle.com/en-us/iaas/api/#/en/s3objectstorage/20160918/Object/GetObject
@@ -138,6 +151,17 @@ public interface OSSCoreService {
*/
HashMap<String, String> createObject(OssConfig ossConfig, String bucketName, String objectName, MultipartFile file);
/**
* 根据ossConfig查询得到一个随机公开的桶然后上传
*
* @param ossConfig
* @param objectName
* @param file
* @return
*/
OctopusObjectSummary createObject(OssConfig ossConfig, String objectName, File file);
/**
* Creates a new object or overwrites an existing one.
* <p>
@@ -148,7 +172,7 @@ public interface OSSCoreService {
* @param objectName
* @return
*/
ObjectMetadata createObject(OssConfig ossConfig, String bucketName, String objectName, File file);
OctopusObjectSummary createObject(OssConfig ossConfig, String bucketName, String objectName, File file);
/**

View File

@@ -5,9 +5,11 @@ import com.amazonaws.SdkClientException;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.*;
import io.wdd.common.handler.MyRuntimeException;
import io.wdd.func.oss.config.OctopusObjectSummary;
import io.wdd.func.oss.config.OssConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
@@ -102,6 +104,15 @@ public class OSSCoreServiceImpl implements OSSCoreService {
}
@Override
public OctopusObjectSummary getObjectSimple(OssConfig ossConfig, String bucketName, String objectName) {
return buildOctopusObjectSummary(
ossConfig,
bucketName,
objectName
);
}
@Override
public S3Object getObject(OssConfig ossConfig, String bucketName, String objectName) {
@@ -113,7 +124,10 @@ public class OSSCoreServiceImpl implements OSSCoreService {
objectName
);
} catch (SdkClientException e) {
log.error("出查询单个对象 出现错误, 错误原因为 => {}", e.getMessage());
log.error(
"出查询单个对象 出现错误, 错误原因为 => {}",
e.getMessage()
);
throw new MyRuntimeException(e.getMessage());
}
@@ -156,7 +170,10 @@ public class OSSCoreServiceImpl implements OSSCoreService {
);
} catch (Exception e) {
log.error("查询对象的Head信息错误, 错误原因 => {}", e.getMessage());
log.error(
"查询对象的Head信息错误, 错误原因 => {}",
e.getMessage()
);
throw new MyRuntimeException(e.getMessage());
}
@@ -197,29 +214,122 @@ public class OSSCoreServiceImpl implements OSSCoreService {
.getObjectContent()
.getHttpRequest()
.getURI();
map.put("origin",
String.valueOf(uri));
map.put(
"origin",
String.valueOf(uri)
);
return map;
} catch (IOException e) {
log.error("文件-创建一个对象失败,原因为 => {}", e.getMessage());
log.error(
"文件-创建一个对象失败,原因为 => {}",
e.getMessage()
);
throw new RuntimeException(e);
}
}
@Override
public ObjectMetadata createObject(OssConfig ossConfig, String bucketName, String objectName, File file) {
AmazonS3 client = ossConfig.getClient();
public OctopusObjectSummary createObject(OssConfig ossConfig, String objectName, File file) {
PutObjectResult putObjectResult = client.putObject(
bucketName,
AmazonS3 client = ossConfig.getClient();
List<Bucket> bucketList = client.listBuckets(
);
Bucket randomBucket = bucketList
.stream()
// 理论上此处应该增加筛选公开的桶的策略
.findAny()
.get();
Assert.notNull(
randomBucket,
"[对象]-创建失败! 未获取到有效的 bucketName"
);
return createObject(
ossConfig,
randomBucket.getName(),
objectName,
file
);
}
return putObjectResult.getMetadata();
@Override
public OctopusObjectSummary createObject(OssConfig ossConfig, String bucketName, String objectName, File file) {
AmazonS3 client = ossConfig.getClient();
log.info(
"[对象]-创建新的对象OSS后端为 [ {} ], 桶名称为 [ {} ], 文件名称为 [ {} ]",
ossConfig.getName() + "-" + ossConfig.getRegion(),
bucketName,
objectName
);
try {
// 调用sdk上传文件
client.putObject(
bucketName,
objectName,
file
);
} catch (SdkClientException e) {
log.error(
"[对象]-创建对象失败! OSS后端为 [ {} ], 桶名称为 [ {} ], 文件名称为 [ {} ] 原因为 {}",
ossConfig.getName() + "-" + ossConfig.getRegion(),
bucketName,
objectName,
e.getMessage()
);
throw new MyRuntimeException(e);
}
// 查询对象的相关信息返回不一样的内容
return buildOctopusObjectSummary(
ossConfig,
bucketName,
objectName
);
}
private OctopusObjectSummary buildOctopusObjectSummary(OssConfig ossConfig, String bucketName, String objectName) {
S3Object s3Object = this.getObject(
ossConfig,
bucketName,
objectName
);
return convertOOSFromS3Object(s3Object);
}
private OctopusObjectSummary convertOOSFromS3Object(S3Object s3Object) {
S3ObjectInputStream objectContent = s3Object.getObjectContent();
ObjectMetadata objectMetadata = s3Object.getObjectMetadata();
OctopusObjectSummary.AccessDTO accessDTO = OctopusObjectSummary.AccessDTO
.builder()
.originUrl(objectContent
.getHttpRequest()
.getURI())
.build();
return OctopusObjectSummary
.builder()
.key(s3Object.getKey())
.size(objectMetadata.getContentLength())
.lastModified(objectMetadata.getLastModified())
.contentType(objectMetadata.getContentType())
.eTag(objectMetadata.getETag())
.bucketName(s3Object.getBucketName())
.access(accessDTO)
.build();
}
@Override
@@ -234,7 +344,6 @@ public class OSSCoreServiceImpl implements OSSCoreService {
)
);
return restoreObjectResult.getRestoreOutputPath();
}
}

View File

@@ -1,14 +1,19 @@
package io.wdd.func.xray.beans.node;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.io.File;
@Data
@AllArgsConstructor
@NoArgsConstructor
@SuperBuilder(toBuilder = true)
@ApiModel("代理节点的信息")
public class ProxyNode {
/**
@@ -31,8 +36,11 @@ public class ProxyNode {
String publicIPv6;
@ApiModelProperty("保存生成的Xray Config信息")
XrayConfigInfo xrayConfigInfo;
@Override
public String toString() {
return "ProxyNode: [ "+ agentTopicName + " ]";
return "ProxyNode: [ " + agentTopicName + " ]";
}
}

View File

@@ -0,0 +1,33 @@
package io.wdd.func.xray.beans.node;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import io.wdd.func.oss.config.OctopusObjectSummary;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.io.File;
@Data
@AllArgsConstructor
@NoArgsConstructor
@SuperBuilder(toBuilder = true)
@ApiModel("保存ProxyNode对应生成的 Config信息")
public class XrayConfigInfo {
@ApiModelProperty("文件名称为1-<agentTopicName>-<timeString>.json")
String xrayConfigFileName;
/**
* 保存生成的XrayConfig文件的全路径
*/
@ApiModelProperty("保存生成的XrayConfig文件全路径")
File xrayConfigFile;
@ApiModelProperty("xray config file的对象存储的相关信息")
OctopusObjectSummary objectSummary;
}

View File

@@ -1,9 +1,173 @@
package io.wdd.func.xray.service;
import io.wdd.func.oss.config.OctopusObjectSummary;
import io.wdd.func.oss.config.OssConfig;
import io.wdd.func.oss.service.OSSCoreService;
import io.wdd.func.oss.service.OssBackendSelect;
import io.wdd.func.xray.beans.node.ProxyNode;
import io.wdd.func.xray.beans.node.XrayConfigInfo;
import io.wdd.rpc.execute.service.CoreExecutionService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* 需要完成 Tmp目录中Xray文件的上传工作上传至 OSS中
* <p>
* 生成操作命令
* 调用rpc执行命令下发操作等待agent进行相关操作
* <p>
* 检查任务流程的结果,判断是否符合预期
*/
@Service
@Slf4j
public class XrayConfigDistribute {
/**
* 存储更新 Xray的命令行
*/
private static final List<List<String>> updateXrayCommandList = new ArrayList<>();
/**
* 发送至agent的操作类型
*/
private static final String updateCommandType = "OctopusXray";
static {
ArrayList<String> first = new ArrayList<>(
List.of(
"cp",
"/usr/local/etc/xray/config.json",
"/usr/local/etc/xray/config-bak-$(date +%Y-%m-%d-%H-%M-%S)"
)
);
ArrayList<String> second = new ArrayList<>(
List.of(
"wget",
"url",
"-O",
"/usr/local/etc/xray/config.json"
)
);
ArrayList<String> third = new ArrayList<>(
List.of(
"systemctl",
"restart",
"xray"
)
);
ArrayList<String> fourth = new ArrayList<>(
List.of(
"systemctl",
"status",
"xray",
"|",
"grep",
"-c",
"active,(running)"
)
);
updateXrayCommandList.add(first);
updateXrayCommandList.add(second);
updateXrayCommandList.add(third);
updateXrayCommandList.add(fourth);
}
@Resource
OssBackendSelect ossBackendSelect;
@Resource
OSSCoreService ossCoreService;
@Resource
CoreExecutionService executionService;
public void uploadXrayConfigToOSS(ArrayList<ProxyNode> networkPathList) {
// 所有的文件全部存储至一个对象存储的后端中
OssConfig ossConfig = ossBackendSelect.oneRandom();
log.info(
"[Xray] 开始上传所有的xray config 至对象存储中 => [{}]",
ossConfig.getName() + "-" + ossConfig.getRegion()
);
// 依次存储所有的config文件
networkPathList
.stream()
.forEach(
node -> {
XrayConfigInfo xrayConfigInfo = node.getXrayConfigInfo();
// 上传OSS
OctopusObjectSummary octopusObjectSummary = ossCoreService
.createObject(
ossConfig,
xrayConfigInfo.getXrayConfigFileName(),
xrayConfigInfo.getXrayConfigFile()
);
// 设置oss的相关信息
xrayConfigInfo.setObjectSummary(octopusObjectSummary);
}
);
}
public void buildXrayUpdateResult(ArrayList<ProxyNode> networkPathList) {
List<String> resultKeyList = networkPathList
.stream()
.map(
proxyNode -> {
OctopusObjectSummary.AccessDTO access = proxyNode
.getXrayConfigInfo()
.getObjectSummary()
.getAccess();
String realUrl = access
.getOriginUrl()
.getPath();
if (StringUtils.isNotEmpty(access.getCloudflareUrl())) {
// 优先使用 被cloudflare包装过的的下载网址
realUrl = access.getCloudflareUrl();
}
// 修改命令中的下载url
updateXrayCommandList
.get(1)
.set(
1,
realUrl
);
// 向Agent发送命令执行更新操作
String resultKey = executionService.SendCommandToAgent(
proxyNode.getAgentTopicName(),
updateCommandType,
null,
updateXrayCommandList,
null
);
return resultKey;
}
)
.collect(Collectors.toList());
log.debug("发送所有的配置到各个Agent成功 结果查看为 => {}", resultKeyList);
}
}

View File

@@ -11,7 +11,6 @@ import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -29,7 +28,7 @@ public class XrayConfigPersistor {
public static AtomicInteger cleanVersion = new AtomicInteger(0);
public void write(String fileName , String content, int currentVersion) {
public File write(String fileName , String content, int currentVersion) {
System.out.println("currentVersion = " + currentVersion);
@@ -44,7 +43,6 @@ public class XrayConfigPersistor {
File resultFile = getResultFile(fileName);
try {
log.debug("开始写入XrayConfig进入文件中文件名为 => {}",fileName);
FileWriter fileWriter = new FileWriter(
resultFile
@@ -60,12 +58,13 @@ public class XrayConfigPersistor {
bufferedWriter.close();
fileWriter.close();
return resultFile;
} catch (IOException e) {
log.error("打开文件失败写入tmp文件失败 文件为 => {}", resultFile.getName());
throw new MyRuntimeException(e);
}
}
private void clearOldRemainStaff() {

View File

@@ -5,6 +5,7 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.wdd.common.utils.TimeUtils;
import io.wdd.func.xray.beans.node.ProxyNode;
import io.wdd.func.xray.beans.node.XrayConfigInfo;
import io.wdd.func.xray.beans.xray.RoutingObject;
import io.wdd.func.xray.beans.xray.RuleObject;
import io.wdd.func.xray.beans.xray.XrayConfig;
@@ -19,14 +20,17 @@ import org.apache.commons.beanutils.BeanUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import static io.wdd.func.xray.beans.config.InboundVmessHTTPTemplateClass.InboundVmessHTTPTemplate;
import static io.wdd.func.xray.beans.config.InboundVmessHTTPTemplateClass.ListenAddress;
import static io.wdd.func.xray.beans.config.LogTemplateClass.LogTemplate;
import static io.wdd.func.xray.beans.config.OutboundVmessHTTPTemplateClass.*;
import static io.wdd.func.xray.beans.node.ProxyNodeSet.*;
import static io.wdd.func.xray.service.XrayConfigPersistor.cleanVersion;
@Service
@@ -43,38 +47,6 @@ public class XrayCoreServiceImpl implements XrayCoreService {
@Override
public void generateXrayJsonFromNodeList(ArrayList<ArrayList<ProxyNode>> allNetworkPathList) {
allNetworkPathList = new ArrayList<>();
ArrayList<ProxyNode> pathA = new ArrayList<>(
Arrays.asList(
shanghai,
seoul2,
tokyo2,
phoenix2
)
);
/*ArrayList<ProxyNode> pathB = new ArrayList<>(
Arrays.asList(
shanghai,
seoul2,
london2
)
);
ArrayList<ProxyNode> pathC = new ArrayList<>(
Arrays.asList(
seoul2,
phoenix2
)
);*/
allNetworkPathList.add(pathA);
// allNetworkPathList.add(pathB);
// allNetworkPathList.add(pathC);
// 需要根据所有的交叉链路进行计算
// A -> B -> C -> D
// B -> D
@@ -87,12 +59,11 @@ public class XrayCoreServiceImpl implements XrayCoreService {
// 返回每一条的结果至于 Map中 key => shanghai->seoul2->tokyo2
// value => xray配置信息
HashMap<String, String> interfaceMap = new HashMap<>();
// HashMap<String, String> interfaceMap = new HashMap<>();
// 最终Xray Json配置应该放置于 Map中 key => ProxyNode
// value => Xray Config Json
HashMap<ProxyNode, XrayConfig> resultMap = new HashMap<>();
// HashMap<ProxyNode, XrayConfig> resultMap = new HashMap<>();
allNetworkPathList
.stream()
@@ -100,9 +71,7 @@ public class XrayCoreServiceImpl implements XrayCoreService {
networkPathList -> {
// 每一条每一条的执行,依次添加相应的 in-out 连接
generateXrayJsonSinglePath(
networkPathList,
interfaceMap,
resultMap
networkPathList
);
}
);
@@ -110,7 +79,7 @@ public class XrayCoreServiceImpl implements XrayCoreService {
}
private void generateXrayJsonSinglePath(ArrayList<ProxyNode> networkPathList, HashMap<String, String> interfaceMap, HashMap<ProxyNode, XrayConfig> resultMap) {
private void generateXrayJsonSinglePath(ArrayList<ProxyNode> networkPathList) {
int pathLength = networkPathList.size();
if (pathLength == 1) {
log.error("网络路径节点仅为一个");
@@ -146,7 +115,6 @@ public class XrayCoreServiceImpl implements XrayCoreService {
// 清楚tmp目录时候使用的一个锁
int currentVersion = cleanVersion.get();
for (int pos = 0; pos < pathLength; pos++) {
ProxyNode proxyNode = networkPathList.get(pos);
@@ -178,12 +146,19 @@ public class XrayCoreServiceImpl implements XrayCoreService {
);
// 文件持久化!
xrayConfigPersistor.write(
File xrayConfigFile = xrayConfigPersistor.write(
fileName,
resultContent,
currentVersion
);
// 文件写入完成,保存文件信息
XrayConfigInfo xrayConfigInfo = new XrayConfigInfo();
xrayConfigInfo.setXrayConfigFile(xrayConfigFile);
xrayConfigInfo.setXrayConfigFileName(fileName);
proxyNode.setXrayConfigInfo(xrayConfigInfo);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
@@ -376,7 +351,6 @@ public class XrayCoreServiceImpl implements XrayCoreService {
);
// 去掉末尾的 ->
String s = sb.toString();
return s.substring(

View File

@@ -11,6 +11,8 @@ public interface CoreExecutionService {
String SendCommandToAgent(String agentTopicName, List<String> commandList);
/**
* 调用 单行命令脚本的 最底层函数
*
* @param agentTopicName agent唯一表示名
* @param type 任务执行类型
* @param commandList 任务列表内容
@@ -21,10 +23,20 @@ public interface CoreExecutionService {
String SendCommandToAgent(String agentTopicName, String type, List<String> commandList, List<List<String>> commandListComplete);
/**
*
* 调用 完整脚本的 最底层函数
*
* @param agentTopicName
* @param type
* @param commandList
* @param commandListComplete
* @param futureKey
* @return resultKey 本次操作在Redis中记录的结果Key
*/
String SendCommandToAgent(String agentTopicName, String type, List<String> commandList, List<List<String>> commandListComplete, String futureKey);
List<String> SendCommandToAgent(List<String> agentTopicNameList, String type, List<String> command);
/**

View File

@@ -1,7 +0,0 @@
package io.wdd.rpc.openfeign;
import org.springframework.cloud.openfeign.FeignClient;
@FeignClient(url = "")
public interface AgentOperationFeign {
}