どのようにダボ制御と同時の数を制限しますか?

ExecuteLimitFilter

ExecuteLimitFilter、サービスプロバイダ、によって オープン統一設定項目「を実行」:
各サービスのすべての方法は、要求の並列最大数で行うことができることを意味します。

ExecuteLimitFilterは、並行サービス側の数は、セマフォによって達成されて制御することです。

ExecuteLimitFilter実行プロセス:

  1. まず、すべてのメソッドの並列実行要求の最大数を、各サービスのサービスプロバイダを取りに行きます
  2. 各方法の各サービスを並列に実行することができる場合は要求の最大数がゼロよりも大きい場合、寸法RpcStatusの例示的な方法基づくサービスURL +に基づいて取得
  3. falseを返すtryAcquire RpcStatus例でセマフォを取得し、Ruoguoセマフォ取得呼び出し、例外がスローされます
  4. あなたが例外をスローしない場合は、そう長く静的メソッド寸法法のカウントを開始するRpcStatus beginCount、このURLの+を呼び出します
  5. コールサービス
  6. 静的メソッドの呼び出し回数はRpcStatus endCount、エンド・カウントをコールした後、
  7. セマフォをリリース

ExecuteLimitFilter

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        URL url = invoker.getUrl();
        String methodName = invocation.getMethodName();
        Semaphore executesLimit = null;
        boolean acquireResult = false;
        int max = url.getMethodParameter(methodName, Constants.EXECUTES_KEY, 0);
        if (max > 0) {
            RpcStatus count = RpcStatus.getStatus(url, invocation.getMethodName());
//            if (count.getActive() >= max) {
            /**
             * http://manzhizhen.iteye.com/blog/2386408
             * use semaphore for concurrency control (to limit thread number)
             */
            executesLimit = count.getSemaphore(max);
            if(executesLimit != null && !(acquireResult = executesLimit.tryAcquire())) {
                throw new RpcException("Failed to invoke method " + invocation.getMethodName() + " in provider " + url + ", cause: The service using threads greater than <dubbo:service executes=\"" + max + "\" /> limited.");
            }
        }
        long begin = System.currentTimeMillis();
        boolean isSuccess = true;
        RpcStatus.beginCount(url, methodName);
        try {
            Result result = invoker.invoke(invocation);
            return result;
        } catch (Throwable t) {
            isSuccess = false;
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new RpcException("unexpected exception when ExecuteLimitFilter", t);
            }
        } finally {
            RpcStatus.endCount(url, methodName, System.currentTimeMillis() - begin, isSuccess);
            if(acquireResult) {
                executesLimit.release();
            }
        }
    }

次は、このクラスのRpcStatusを見て

    private static final ConcurrentMap<String, ConcurrentMap<String, RpcStatus>> METHOD_STATISTICS = new ConcurrentHashMap<String, ConcurrentMap<String, RpcStatus>>();
    
    public static RpcStatus getStatus(URL url, String methodName) {
        String uri = url.toIdentityString();
        ConcurrentMap<String, RpcStatus> map = METHOD_STATISTICS.get(uri);
        if (map == null) {
            METHOD_STATISTICS.putIfAbsent(uri, new ConcurrentHashMap<String, RpcStatus>());
            map = METHOD_STATISTICS.get(uri);
        }
        RpcStatus status = map.get(methodName);
        if (status == null) {
            map.putIfAbsent(methodName, new RpcStatus());
            status = map.get(methodName);
        }
        return status;
    }

この方法は、おそらくMETHOD_STATISTICS設定値内の静的プロパティ内のクラスをRpcStatusする、非常に簡単です。外層は、マップのURLである内層をマップ、鍵である鍵と呼ばれる方法です。

    private volatile int executesPermits;
    public Semaphore getSemaphore(int maxThreadNum) {
        if(maxThreadNum <= 0) {
            return null;
        }

        if (executesLimit == null || executesPermits != maxThreadNum) {
            synchronized (this) {
                if (executesLimit == null || executesPermits != maxThreadNum) {
                    executesLimit = new Semaphore(maxThreadNum);
                    executesPermits = maxThreadNum;
                }
            }
        }

        return executesLimit;
    }

空の戻りない場合セマフォが空であり、この場合には、その後、いずれかを追加した場合、この方法は、セマフォを得ることです。

TPSLimiter

サービスプロバイダのフィルタTpsLimitFilter、電流制限機能が提供されます。

構成:

  1. によって 設定項目に追加 若しくは 若しくは 今度は、例えば:
dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoServiceImpl" protocol="injvm" >
    <dubbo:parameter key="tps" value="100" />
</dubbo:service>
  1. によって 設定項目は、TPSサイクルを設定します。

ソースコード解析

TpsLimitFilter

    private final TPSLimiter tpsLimiter = new DefaultTPSLimiter();
    
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {

        if (!tpsLimiter.isAllowable(invoker.getUrl(), invocation)) {
            throw new RpcException(
                    "Failed to invoke service " +
                            invoker.getInterface().getName() +
                            "." +
                            invocation.getMethodName() +
                            " because exceed max service tps.");
        }

        return invoker.invoke(invocation);
    }

この方法は、isAllowableのDefaultTPSLimiterを呼び出して呼び出し、我々は法に見isAllowable

DefaultTPSLimiter

    private final ConcurrentMap<String, StatItem> stats
            = new ConcurrentHashMap<String, StatItem>();
    @Override
    public boolean isAllowable(URL url, Invocation invocation) {
        //获取tps这个参数设置的大小
        int rate = url.getParameter(Constants.TPS_LIMIT_RATE_KEY, -1);
        //获取tps.interval这个参数设置的大小,默认60秒
        long interval = url.getParameter(Constants.TPS_LIMIT_INTERVAL_KEY,
                Constants.DEFAULT_TPS_LIMIT_INTERVAL);
        String serviceKey = url.getServiceKey();
        if (rate > 0) {
            StatItem statItem = stats.get(serviceKey);
            if (statItem == null) {
                stats.putIfAbsent(serviceKey,
                        new StatItem(serviceKey, rate, interval));
                statItem = stats.get(serviceKey);
            }
            return statItem.isAllowable();
        } else {
            StatItem statItem = stats.get(serviceKey);
            if (statItem != null) {
                stats.remove(serviceKey);
            }
        }

        return true;
    }

制限するために、呼び出しを制限するルールを制限するかどうかを決定TPSによれば、StatItem#isAllowable(URL、呼び出し)メソッドを呼び出します。

StatItem

    private long lastResetTime;

    private long interval;

    private AtomicInteger token;

    private int rate;

    public boolean isAllowable() {
        long now = System.currentTimeMillis();
         // 若到达下一个周期,恢复可用种子数,设置最后重置时间。
        if (now > lastResetTime + interval) {
            token.set(rate);// 回复可用种子数
            lastResetTime = now;// 最后重置时间
        }
        // CAS ,直到或得到一个种子,或者没有足够种子
        int value = token.get();
        boolean flag = false;
        while (value > 0 && !flag) {
            flag = token.compareAndSet(value, value - 1);
            value = token.get();
        }

        return flag;
    }

おすすめ

転載: www.cnblogs.com/luozhiyun/p/10960593.html