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

com.tencent.polaris.ratelimit.client.sync.RemoteSyncTask Maven / Gradle / Ivy

/*
 * Tencent is pleased to support the open source community by making Polaris available.
 *
 * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
 *
 * Licensed under the BSD 3-Clause License (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * https://opensource.org/licenses/BSD-3-Clause
 *
 * Unless required by applicable law or agreed to in writing, software distributed
 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */

package com.tencent.polaris.ratelimit.client.sync;

import com.tencent.polaris.api.plugin.ratelimiter.AmountInfo;
import com.tencent.polaris.api.plugin.ratelimiter.LocalQuotaInfo;
import com.tencent.polaris.api.plugin.ratelimiter.QuotaBucket;
import com.tencent.polaris.api.utils.MapUtils;
import com.tencent.polaris.ratelimit.client.flow.AsyncRateLimitConnector;
import com.tencent.polaris.ratelimit.client.flow.RateLimitWindow;
import com.tencent.polaris.ratelimit.client.flow.ServiceIdentifier;
import com.tencent.polaris.ratelimit.client.flow.StreamCounterSet;
import com.tencent.polaris.ratelimit.client.pb.RateLimitGRPCV2Grpc.RateLimitGRPCV2BlockingStub;
import com.tencent.polaris.ratelimit.client.pb.RatelimitV2.LimitTarget;
import com.tencent.polaris.ratelimit.client.pb.RatelimitV2.QuotaMode;
import com.tencent.polaris.ratelimit.client.pb.RatelimitV2.QuotaSum;
import com.tencent.polaris.ratelimit.client.pb.RatelimitV2.QuotaTotal;
import com.tencent.polaris.ratelimit.client.pb.RatelimitV2.RateLimitCmd;
import com.tencent.polaris.ratelimit.client.pb.RatelimitV2.RateLimitInitRequest;
import com.tencent.polaris.ratelimit.client.pb.RatelimitV2.RateLimitInitRequest.Builder;
import com.tencent.polaris.ratelimit.client.pb.RatelimitV2.RateLimitReportRequest;
import com.tencent.polaris.ratelimit.client.pb.RatelimitV2.RateLimitRequest;
import com.tencent.polaris.ratelimit.client.pb.RatelimitV2.TimeAdjustRequest;
import com.tencent.polaris.ratelimit.client.pb.RatelimitV2.TimeAdjustResponse;
import com.tencent.polaris.ratelimit.client.utils.RateLimitConstants;
import io.grpc.stub.StreamObserver;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 1、首次调用需要上报
 * 2、出现后台server切换需要上报
 * 3、定时上报上一次上报到现在已经超过上报周期
 * 4、额使用完毕后上报。配额=amount/实例数/时间片数。
 * 每次上报完后,server会返回服务端的时间戳,客户端会用来校正上报的时间段
 */
public class RemoteSyncTask implements Runnable {

    private static final Logger LOG = LoggerFactory.getLogger(RemoteSyncTask.class);

    /**
     * 限流窗口
     */
    private final RateLimitWindow window;

    /**
     * 与限流集群通信的连接类
     */
    private final AsyncRateLimitConnector asyncRateLimitConnector;

    /**
     * 被限流的服务信息,一个窗口的唯一标识
     */
    private final ServiceIdentifier serviceIdentifier;

    public RemoteSyncTask(RateLimitWindow window) {
        this.window = window;
        this.asyncRateLimitConnector = window.getWindowSet().getAsyncRateLimitConnector();
        this.serviceIdentifier = new ServiceIdentifier(window.getSvcKey().getService(),
                window.getSvcKey().getNamespace(), window.getLabels());
    }

    public RateLimitWindow getWindow() {
        return window;
    }


    @Override
    public void run() {
        switch (window.getStatus()) {
            case CREATED:
            case DELETED://todo 已经是删除态了,还有必要在轮询吗?
                break;
            case INITIALIZING:
                doRemoteInit();
                break;
            default:
                doRemoteAcquire();
                break;
        }
    }

    /**
     * 发送初始化请求
     */
    private void doRemoteInit() {
        StreamCounterSet streamCounterSet = asyncRateLimitConnector
                .getStreamCounterSet(window.getWindowSet().getRateLimitExtension().getExtensions(),
                        window.getRemoteCluster(), window.getUniqueKey(), serviceIdentifier);
        //拿不到限流集群的实例的时候
        if (streamCounterSet == null) {
            LOG.error("[doRemoteInit] failed, stream counter is null. remote cluster:{},",
                    window.getRemoteCluster());
            return;
        }
        //调整时间
        if (!adjustTime(streamCounterSet)) {
            LOG.error("[doRemoteInit] adjustTime failed.remote cluster:{},svcKey:{}",
                    window.getRemoteCluster(), window.getSvcKey());
            return;
        }
        StreamObserver streamClient = streamCounterSet.preCheckAsync(serviceIdentifier, window);
        if (streamClient == null) {
            LOG.error("[doRemoteInit] failed, stream client is null. remote cluster:{},svcKey:{}",
                    window.getRemoteCluster(), window.getSvcKey());
            return;
        }
        //clientId
        Builder initRequest = RateLimitInitRequest.newBuilder();
        initRequest.setClientId(window.getWindowSet().getClientId());

        //target
        LimitTarget.Builder target = LimitTarget.newBuilder();
        target.setNamespace(window.getSvcKey().getNamespace());
        target.setService(window.getSvcKey().getService());
        target.setLabels(window.getLabels());
        initRequest.setTarget(target);

        //QuotaTotal
        QuotaMode quotaMode = QuotaMode.forNumber(window.getRule().getAmountModeValue());
        QuotaBucket allocatingBucket = window.getAllocatingBucket();
        Map amountInfos = allocatingBucket.getAmountInfo();
        if (MapUtils.isNotEmpty(amountInfos)) {
            for (Map.Entry entry : amountInfos.entrySet()) {
                QuotaTotal.Builder total = QuotaTotal.newBuilder();
                total.setDuration(entry.getKey());
                total.setMode(quotaMode);
                total.setMaxAmount((int) entry.getValue().getMaxAmount());
                initRequest.addTotals(total.build());
            }
        }
        RateLimitRequest rateLimitInitRequest = RateLimitRequest.newBuilder().setCmd(RateLimitCmd.INIT)
                .setRateLimitInitRequest(initRequest).build();

        streamClient.onNext(rateLimitInitRequest);
    }


    /**
     * 上报
     */
    private void doRemoteAcquire() {
        StreamCounterSet streamCounterSet = asyncRateLimitConnector
                .getStreamCounterSet(window.getWindowSet().getRateLimitExtension().getExtensions(),
                        window.getRemoteCluster(), window.getUniqueKey(), serviceIdentifier);
        if (streamCounterSet == null) {
            LOG.error("[doRemoteAcquire] failed, stream counter is null. remote cluster:{},",
                    window.getRemoteCluster());
            return;
        }
        if (!streamCounterSet.hasInit(serviceIdentifier)) {
            LOG.warn("[doRemoteAcquire] has not inited. serviceKey:{}", window.getSvcKey());
            doRemoteInit();
            return;
        }
        //调整时间
        if (!adjustTime(streamCounterSet)) {
            LOG.error("[doRemoteAcquire] adjustTime failed.remote cluster:{},svcKey:{}",
                    window.getRemoteCluster(), window.getSvcKey());
            return;
        }
        StreamObserver streamClient = streamCounterSet.preCheckAsync(serviceIdentifier, window);
        if (streamClient == null) {
            LOG.error("[doRemoteAcquire] failed, stream client is null. remote cluster:{}", window.getRemoteCluster());
            return;
        }
        RateLimitReportRequest.Builder rateLimitReportRequest = RateLimitReportRequest.newBuilder();
        //clientKey
        rateLimitReportRequest.setClientKey(streamCounterSet.getClientKey());
        //timestamp
        long curTimeMilli = System.currentTimeMillis();
        long serverTimeMilli = curTimeMilli + asyncRateLimitConnector.getTimeDiff().get();
        rateLimitReportRequest.setTimestamp(serverTimeMilli);

        //quotaUses
        Map localQuotaInfos = window.getAllocatingBucket().fetchLocalUsage(serverTimeMilli);

        for (Map.Entry entry : localQuotaInfos.entrySet()) {
            QuotaSum.Builder quotaSum = QuotaSum.newBuilder();
            quotaSum.setUsed((int) entry.getValue().getQuotaUsed());
            quotaSum.setLimited((int) entry.getValue().getQuotaLimited());
            quotaSum.setCounterKey(streamCounterSet.getInitRecord().get(serviceIdentifier).getDurationRecord()
                    .get(entry.getKey()));
            rateLimitReportRequest.addQuotaUses(quotaSum.build());
        }

        RateLimitRequest rateLimitRequest = RateLimitRequest.newBuilder().setCmd(RateLimitCmd.ACQUIRE)
                .setRateLimitReportRequest(rateLimitReportRequest).build();
        streamClient.onNext(rateLimitRequest);
    }

    /**
     * 调整时间
     *
     * @param streamCounterSet streamCounterSet
     */
    private boolean adjustTime(StreamCounterSet streamCounterSet) {

        long lastSyncTimeMilli = asyncRateLimitConnector.getLastSyncTimeMilli().get();
        long sendTimeMilli = System.currentTimeMillis();

        //超过间隔时间才需要调整
        if (lastSyncTimeMilli > 0 && sendTimeMilli - lastSyncTimeMilli < RateLimitConstants.STARTUP_DELAY_MS) {
            LOG.info("adjustTime need wait.lastSyncTimeMilli:{},sendTimeMilli:{}", lastSyncTimeMilli, sendTimeMilli);
            return true;
        }

        RateLimitGRPCV2BlockingStub client = streamCounterSet.preCheckSync(serviceIdentifier, window);
        if (client == null) {
            LOG.error("[adjustTime] can not get connection {}", window.getRemoteCluster());
            return false;
        }

        TimeAdjustRequest timeAdjustRequest = TimeAdjustRequest.newBuilder().build();
        TimeAdjustResponse timeAdjustResponse = client.timeAdjust(timeAdjustRequest);

        long receiveClientTimeMilli = System.currentTimeMillis();
        asyncRateLimitConnector.getLastSyncTimeMilli().set(receiveClientTimeMilli);
        //服务端时间
        long serverTimestamp = timeAdjustResponse.getServerTimestamp();

        long latency = receiveClientTimeMilli - sendTimeMilli;

        long timeDiff = serverTimestamp + latency / 2 - receiveClientTimeMilli;
        asyncRateLimitConnector.getTimeDiff().set(timeDiff);
        LOG.info("[RateLimit]adjust time to server time is {}, latency is {},diff is {}", serverTimestamp, latency,
                timeDiff);
        return true;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy