| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * 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 org.apache.dubbo.rpc; |
| |
| import org.apache.dubbo.common.URL; |
| import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; |
| |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.concurrent.ConcurrentMap; |
| import java.util.concurrent.atomic.AtomicInteger; |
| import java.util.concurrent.atomic.AtomicLong; |
| |
| /** |
| * URL statistics. (API, Cached, ThreadSafe) |
| * |
| * @see org.apache.dubbo.rpc.filter.ActiveLimitFilter |
| * @see org.apache.dubbo.rpc.filter.ExecuteLimitFilter |
| * @see org.apache.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance |
| */ |
| public class RpcStatus { |
| |
| private static final ConcurrentMap<String, RpcStatus> SERVICE_STATISTICS = new ConcurrentHashMap<>(); |
| |
| private static final ConcurrentMap<String, ConcurrentMap<String, RpcStatus>> METHOD_STATISTICS = |
| new ConcurrentHashMap<>(); |
| |
| private final ConcurrentMap<String, Object> values = new ConcurrentHashMap<>(); |
| |
| private final AtomicInteger active = new AtomicInteger(); |
| private final AtomicLong total = new AtomicLong(); |
| private final AtomicInteger failed = new AtomicInteger(); |
| private final AtomicLong totalElapsed = new AtomicLong(); |
| private final AtomicLong failedElapsed = new AtomicLong(); |
| private final AtomicLong maxElapsed = new AtomicLong(); |
| private final AtomicLong failedMaxElapsed = new AtomicLong(); |
| private final AtomicLong succeededMaxElapsed = new AtomicLong(); |
| |
| private RpcStatus() {} |
| |
| /** |
| * @param url |
| * @return status |
| */ |
| public static RpcStatus getStatus(URL url) { |
| String uri = url.toIdentityString(); |
| return ConcurrentHashMapUtils.computeIfAbsent(SERVICE_STATISTICS, uri, key -> new RpcStatus()); |
| } |
| |
| /** |
| * @param url |
| */ |
| public static void removeStatus(URL url) { |
| String uri = url.toIdentityString(); |
| SERVICE_STATISTICS.remove(uri); |
| } |
| |
| /** |
| * @param url |
| * @param methodName |
| * @return status |
| */ |
| public static RpcStatus getStatus(URL url, String methodName) { |
| String uri = url.toIdentityString(); |
| ConcurrentMap<String, RpcStatus> map = |
| ConcurrentHashMapUtils.computeIfAbsent(METHOD_STATISTICS, uri, k -> new ConcurrentHashMap<>()); |
| return ConcurrentHashMapUtils.computeIfAbsent(map, methodName, k -> new RpcStatus()); |
| } |
| |
| /** |
| * @param url |
| */ |
| public static void removeStatus(URL url, String methodName) { |
| String uri = url.toIdentityString(); |
| ConcurrentMap<String, RpcStatus> map = METHOD_STATISTICS.get(uri); |
| if (map != null) { |
| map.remove(methodName); |
| } |
| } |
| |
| public static void beginCount(URL url, String methodName) { |
| beginCount(url, methodName, Integer.MAX_VALUE); |
| } |
| |
| /** |
| * @param url |
| */ |
| public static boolean beginCount(URL url, String methodName, int max) { |
| max = (max <= 0) ? Integer.MAX_VALUE : max; |
| RpcStatus appStatus = getStatus(url); |
| RpcStatus methodStatus = getStatus(url, methodName); |
| if (methodStatus.active.get() == Integer.MAX_VALUE) { |
| return false; |
| } |
| for (int i; ; ) { |
| i = methodStatus.active.get(); |
| |
| if (i == Integer.MAX_VALUE || i + 1 > max) { |
| return false; |
| } |
| |
| if (methodStatus.active.compareAndSet(i, i + 1)) { |
| break; |
| } |
| } |
| |
| appStatus.active.incrementAndGet(); |
| |
| return true; |
| } |
| |
| /** |
| * @param url |
| * @param elapsed |
| * @param succeeded |
| */ |
| public static void endCount(URL url, String methodName, long elapsed, boolean succeeded) { |
| endCount(getStatus(url), elapsed, succeeded); |
| endCount(getStatus(url, methodName), elapsed, succeeded); |
| } |
| |
| private static void endCount(RpcStatus status, long elapsed, boolean succeeded) { |
| status.active.decrementAndGet(); |
| status.total.incrementAndGet(); |
| status.totalElapsed.addAndGet(elapsed); |
| |
| if (status.maxElapsed.get() < elapsed) { |
| status.maxElapsed.set(elapsed); |
| } |
| |
| if (succeeded) { |
| if (status.succeededMaxElapsed.get() < elapsed) { |
| status.succeededMaxElapsed.set(elapsed); |
| } |
| |
| } else { |
| status.failed.incrementAndGet(); |
| status.failedElapsed.addAndGet(elapsed); |
| if (status.failedMaxElapsed.get() < elapsed) { |
| status.failedMaxElapsed.set(elapsed); |
| } |
| } |
| } |
| |
| /** |
| * set value. |
| * |
| * @param key |
| * @param value |
| */ |
| public void set(String key, Object value) { |
| values.put(key, value); |
| } |
| |
| /** |
| * get value. |
| * |
| * @param key |
| * @return value |
| */ |
| public Object get(String key) { |
| return values.get(key); |
| } |
| |
| /** |
| * get active. |
| * |
| * @return active |
| */ |
| public int getActive() { |
| return active.get(); |
| } |
| |
| /** |
| * get total. |
| * |
| * @return total |
| */ |
| public long getTotal() { |
| return total.longValue(); |
| } |
| |
| /** |
| * get total elapsed. |
| * |
| * @return total elapsed |
| */ |
| public long getTotalElapsed() { |
| return totalElapsed.get(); |
| } |
| |
| /** |
| * get average elapsed. |
| * |
| * @return average elapsed |
| */ |
| public long getAverageElapsed() { |
| long total = getTotal(); |
| if (total == 0) { |
| return 0; |
| } |
| return getTotalElapsed() / total; |
| } |
| |
| /** |
| * get max elapsed. |
| * |
| * @return max elapsed |
| */ |
| public long getMaxElapsed() { |
| return maxElapsed.get(); |
| } |
| |
| /** |
| * get failed. |
| * |
| * @return failed |
| */ |
| public int getFailed() { |
| return failed.get(); |
| } |
| |
| /** |
| * get failed elapsed. |
| * |
| * @return failed elapsed |
| */ |
| public long getFailedElapsed() { |
| return failedElapsed.get(); |
| } |
| |
| /** |
| * get failed average elapsed. |
| * |
| * @return failed average elapsed |
| */ |
| public long getFailedAverageElapsed() { |
| long failed = getFailed(); |
| if (failed == 0) { |
| return 0; |
| } |
| return getFailedElapsed() / failed; |
| } |
| |
| /** |
| * get failed max elapsed. |
| * |
| * @return failed max elapsed |
| */ |
| public long getFailedMaxElapsed() { |
| return failedMaxElapsed.get(); |
| } |
| |
| /** |
| * get succeeded. |
| * |
| * @return succeeded |
| */ |
| public long getSucceeded() { |
| return getTotal() - getFailed(); |
| } |
| |
| /** |
| * get succeeded elapsed. |
| * |
| * @return succeeded elapsed |
| */ |
| public long getSucceededElapsed() { |
| return getTotalElapsed() - getFailedElapsed(); |
| } |
| |
| /** |
| * get succeeded average elapsed. |
| * |
| * @return succeeded average elapsed |
| */ |
| public long getSucceededAverageElapsed() { |
| long succeeded = getSucceeded(); |
| if (succeeded == 0) { |
| return 0; |
| } |
| return getSucceededElapsed() / succeeded; |
| } |
| |
| /** |
| * get succeeded max elapsed. |
| * |
| * @return succeeded max elapsed. |
| */ |
| public long getSucceededMaxElapsed() { |
| return succeededMaxElapsed.get(); |
| } |
| |
| /** |
| * Calculate average TPS (Transaction per second). |
| * |
| * @return tps |
| */ |
| public long getAverageTps() { |
| if (getTotalElapsed() >= 1000L) { |
| return getTotal() / (getTotalElapsed() / 1000L); |
| } |
| return getTotal(); |
| } |
| } |