All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bdware.doip.cluster.client.DoipClusterClient Maven / Gradle / Ivy

There is a newer version: 1.5.4
Show newest version
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();
    }
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy