org.bdware.doip.cluster.client.DoipClusterClient Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of doip-audit-tool Show documentation
Show all versions of doip-audit-tool Show documentation
doip audit tool developed by bdware
package org.bdware.doip.cluster.client;
import com.google.gson.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bdware.doip.audit.EndpointConfig;
import org.bdware.doip.audit.client.AuditIrpClient;
import org.bdware.doip.cluster.entity.DDOEntity;
import org.bdware.doip.cluster.entity.DoipClusterRequestPack;
import org.bdware.doip.cluster.flowcontrol.FlowControl;
import org.bdware.doip.cluster.flowcontrol.core.RateThresholdFlowControl;
import org.bdware.doip.cluster.util.AuditDoipClientCacheUtil;
import org.bdware.doip.cluster.util.DOResolutionUtil;
import org.bdware.doip.cluster.util.RouteJoinUtil;
import org.bdware.doip.codec.JsonDoipMessage;
import org.bdware.doip.codec.doipMessage.DoipMessage;
import org.bdware.doip.codec.doipMessage.DoipMessageFactory;
import org.bdware.doip.codec.doipMessage.DoipMessageSigner;
import org.bdware.doip.codec.doipMessage.DoipResponseCode;
import org.bdware.doip.codec.operations.BasicOperations;
import org.bdware.doip.encrypt.SM2Signer;
import org.bdware.doip.endpoint.client.DoipClientImpl;
import org.bdware.doip.endpoint.client.DoipMessageCallback;
import org.bdware.irp.irplib.exception.IrpConnectException;
import org.bdware.sc.bean.JoinInfo;
import org.bdware.sc.bean.RouteInfo;
import org.bdware.sc.util.JsonUtil;
import org.zz.gmhelper.SM2KeyPair;
import org.zz.gmhelper.SM2Util;
import java.math.BigInteger;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//Client for single bdo running in cluster model.
//Leverage resolved result as input.
//connections between different bdo is reused if possible.
@Deprecated
public class DoipClusterClient extends DoipClientImpl {
private final DoipMessageSigner doipMessageSigner;
protected final EndpointConfig routerConfig;
// all the doipOperation and routeInfo binding relationships
// BDO和DoipClusterClient是一一对应的关系,一个BDO对应一组BDRepo,一个BDRepo对应了一个DoipServer端口,因此这里维护Repo -> 端口的映射
private final static Queue sendMessageTaskQueue = new ConcurrentLinkedDeque<>();
public final AuditIrpClient irsClient;
private final FlowControl flowControl;
Logger Logger = LogManager.getLogger(DoipClusterClient.class);
String curDOID;
private DDOEntity ddoEntity;
// todo optimize thread pool
ExecutorService sendMessagePool = Executors.newFixedThreadPool(8, r -> {
Thread t = new Thread(r);
t.setDaemon(true);
return t;
});
public DoipClusterClient(String dDoId, EndpointConfig routerConfig) {
this.routerConfig = routerConfig;
irsClient = new AuditIrpClient(routerConfig);
new Thread(this::consumeSendMessageTask).start();
try {
irsClient.reconnect();
} catch (IrpConnectException e) {
e.printStackTrace();
}
Logger.info("The repoIrpClient initialization has done");
// initiate doipMessageSigner
if (routerConfig.publicKey != null && routerConfig.privateKey != null) {
doipMessageSigner = new SM2Signer(SM2KeyPair.fromJson(new Gson().toJson(routerConfig)));
} else
doipMessageSigner = new SM2Signer(SM2Util.generateSM2KeyPair());
// todo optimize flowControl
// flowControl = new TokenBucket(10240, 2048);
int threshold = 1000;
if (routerConfig.extraConfig != null && routerConfig.extraConfig.has("rateThresholdFlowControl"))
threshold = routerConfig.extraConfig.get("rateThresholdFlowControl").getAsInt();
flowControl = new RateThresholdFlowControl(threshold);
Logger.info("The DoipClusterClient has been initialized");
parseDOID(dDoId);
}
// TODO 存在并发问题,parse之后就应该立刻进行调用,原子性,不然会出现问题
public void parseDOID(String doid) {
for (int retry = 0; retry < 5; retry++) {
try {
// 如果已经解析过了,就不需要再次进行解析了
JsonObject ddoInfo = DOResolutionUtil.getAndVerifyDDOInfo(irsClient, doid);
ddoEntity = new DDOEntity(ddoInfo, null);
curDOID = doid;
return;
} catch (Exception e) {
e.printStackTrace();
try {
Thread.sleep(retry + (long) retry * (10 << retry));
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
}
}
}
@Override
public void sendMessage(DoipMessage doipMessage, DoipMessageCallback cb) {
try {
BasicOperations op = BasicOperations.getDoOp(doipMessage.header.parameters.operation);
JsonDoipMessage message = JsonDoipMessage.fromDoipMessage(doipMessage);
String[] targetDOIDs = route(message);
// joinParams都来自于callback,joinParams就是所有节点callback到达之后,merge之后的结果
JoinInfo joinInfo = ddoEntity.getDdoJoinInfo().get(op.getName());
DoipClusterRequestPack pack = new DoipClusterRequestPack(doipMessage, joinInfo, targetDOIDs, msg -> {
cb.onResult(msg);
flowControl.maintainFlowControl();
});
produceSendMessageTask(pack);
} catch (Exception e) {
e.printStackTrace();
DoipMessageFactory.DoipMessageBuilder builder = new DoipMessageFactory.DoipMessageBuilder();
builder.createResponse(DoipResponseCode.UnKnownError, doipMessage);
cb.onResult(builder.create());
}
}
public void sendMessageInternal(DoipClusterRequestPack pack) {
try {
RouteJoinUtil.bdoSendMsgAndJoin(
this,
pack.message,
pack.joinInfo,
pack.targetDOIDs,
pack.cb,
doipMessageSigner,
ddoEntity.getEngineUtil(),
routerConfig.publicKey,
ddoEntity.getAppendixes()
);
} catch (Exception e) {
e.printStackTrace();
DoipMessageFactory.DoipMessageBuilder builder = new DoipMessageFactory.DoipMessageBuilder();
builder.createResponse(DoipResponseCode.UnKnownError, pack.message);
pack.cb.onResult(builder.create());
}
}
static Logger LOGGER = LogManager.getLogger(DoipClusterClient.class);
// 根据请求参数,执行RouteInfo,寻找DoipServers
public String[] route(JsonDoipMessage doipParam) {
try {
BasicOperations operations = BasicOperations.getDoOp(doipParam.header.operation);
// If the operation truly has routeInfo
RouteInfo routeInfo = ddoEntity.getDdoRouteInfo().getOrDefault(operations.getName(), null);
String[] BDRepoIDs = ddoEntity.getBDOList();
if (routeInfo != null) {
if (routeInfo.useDefault == null) {
JsonElement requester = routerConfig.publicKey == null ? JsonNull.INSTANCE : new JsonPrimitive(routerConfig.publicKey);
return ddoEntity.getEngineUtil().invokeFunction(routeInfo.funcName, String[].class, JsonUtil.parseObject(doipParam), requester);
}
switch (routeInfo.useDefault) {
case byRequester:
int val = new BigInteger(routerConfig.publicKey, 16).mod(new BigInteger("" + BDRepoIDs.length)).intValue();
while (val < 0 && BDRepoIDs.length > 0) {
val = val + BDRepoIDs.length;
}
return new String[]{BDRepoIDs[val]};
case byArgHash:
val = JsonUtil.toJson(doipParam).hashCode();
val = val % BDRepoIDs.length;
while (val < 0) {
val += BDRepoIDs.length;
}
return new String[]{BDRepoIDs[val]};
case byJsonPropHash:
JsonElement jo = RouteJoinUtil.tryLoadJsonProp(JsonUtil.parseObject(doipParam).getAsJsonObject(), routeInfo.param);
val = jo.toString().hashCode() % BDRepoIDs.length;
while (val < 0) {
val += BDRepoIDs.length;
}
return new String[]{BDRepoIDs[val]};
default:
return BDRepoIDs;
}
// 如果没有RouteInfo,默认随机选择一台机器发送
} else {
Logger.warn("Not RouteInfo in the function");
return BDRepoIDs.length != 0 ? new String[]{BDRepoIDs[(int) (Math.random() * BDRepoIDs.length)]} : BDRepoIDs;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private void produceSendMessageTask(DoipClusterRequestPack pack) {
for (int i = 0; i < 10 && !flowControl.enableRequestPass(); i++) {
try {
LOGGER.info("rate is too fast");
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
}
sendMessageTaskQueue.add(pack);
// todo -> 如果有人在等,我就去synchronize sendMessageTaskQueue 并且 notify,不然性能可能会有瓶颈
synchronized (sendMessageTaskQueue) {
sendMessageTaskQueue.notify();
}
}
public void consumeSendMessageTask() {
while (true) {
try {
DoipClusterRequestPack pack = sendMessageTaskQueue.poll();
if (pack != null) {
sendMessagePool.execute(() -> sendMessageInternal(pack));
} else {
synchronized (sendMessageTaskQueue) {
sendMessageTaskQueue.wait(10000);
}
}
} catch (InterruptedException ignored) {
}
}
}
@Override
public void close() {
AuditDoipClientCacheUtil.closeAll();
}
}