You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by al...@apache.org on 2021/12/27 08:01:50 UTC
[dubbo] branch 3.0 updated: [3.0] Add Dubbo Invocation Profiler (#9373)
This is an automated email from the ASF dual-hosted git repository.
albumenj pushed a commit to branch 3.0
in repository https://gitbox.apache.org/repos/asf/dubbo.git
The following commit(s) were added to refs/heads/3.0 by this push:
new 798840e [3.0] Add Dubbo Invocation Profiler (#9373)
798840e is described below
commit 798840e8577650fa6ad24776b2028f0eb5c7c8a2
Author: Albumen Kevin <jh...@gmail.com>
AuthorDate: Mon Dec 27 16:01:41 2021 +0800
[3.0] Add Dubbo Invocation Profiler (#9373)
* [3.0] Add Profiler
* opt
* add nano
* add qos
* add log
* Add default profiler
* Add default profiler
* Adapt CopyOfFilterChainNode
---
.../rpc/cluster/filter/FilterChainBuilder.java | 11 ++-
.../cluster/support/AbstractClusterInvoker.java | 17 +++-
.../org/apache/dubbo/common/profiler/Profiler.java | 104 +++++++++++++++++++++
.../dubbo/common/profiler/ProfilerEntry.java | 71 ++++++++++++++
.../dubbo/common/profiler/ProfilerSwitch.java | 63 +++++++++++++
.../apache/dubbo/common/profiler/ProfilerTest.java | 97 +++++++++++++++++++
.../apache/dubbo/qos/command/impl/BaseOffline.java | 2 +-
.../apache/dubbo/qos/command/impl/BaseOnline.java | 2 +-
.../qos/command/impl/DisableDetailProfiler.java | 36 +++++++
.../qos/command/impl/DisableSimpleProfiler.java | 36 +++++++
.../qos/command/impl/EnableDetailProfiler.java | 36 +++++++
.../qos/command/impl/EnableSimpleProfiler.java | 36 +++++++
.../qos/command/impl/SetProfilerWarnPercent.java | 39 ++++++++
.../org.apache.dubbo.qos.command.BaseCommand | 5 +
.../dubbo/qos/command/util/CommandHelperTest.java | 11 +++
.../apache/dubbo/rpc/InvocationProfilerUtils.java | 77 +++++++++++++++
.../dubbo/rpc/filter/ProfilerServerFilter.java | 101 ++++++++++++++++++++
.../apache/dubbo/rpc/protocol/AbstractInvoker.java | 2 +-
.../dubbo/rpc/proxy/AbstractProxyInvoker.java | 23 +++++
.../dubbo/rpc/proxy/InvokerInvocationHandler.java | 47 ++++++++++
.../dubbo/internal/org.apache.dubbo.rpc.Filter | 1 +
.../dubbo/rpc/protocol/dubbo/DubboInvoker.java | 2 +-
.../dubbo/rpc/protocol/tri/TripleInvoker.java | 2 +-
23 files changed, 814 insertions(+), 7 deletions(-)
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/FilterChainBuilder.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/FilterChainBuilder.java
index a329959..c01c5b6 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/FilterChainBuilder.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/FilterChainBuilder.java
@@ -24,6 +24,7 @@ import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.rpc.BaseFilter;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.InvocationProfilerUtils;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.ListenableFilter;
import org.apache.dubbo.rpc.Result;
@@ -50,10 +51,11 @@ public interface FilterChainBuilder {
/**
* Works on provider side
+ *
* @param <T>
* @param <TYPE>
*/
- class FilterChainNode<T, TYPE extends Invoker<T>, FILTER extends BaseFilter> implements Invoker<T>{
+ class FilterChainNode<T, TYPE extends Invoker<T>, FILTER extends BaseFilter> implements Invoker<T> {
TYPE originalInvoker;
Invoker<T> nextNode;
FILTER filter;
@@ -87,8 +89,10 @@ public interface FilterChainBuilder {
public Result invoke(Invocation invocation) throws RpcException {
Result asyncResult;
try {
+ InvocationProfilerUtils.enterDetailProfiler(invocation, () -> "Filter " + filter.getClass().getName() + " invoke.");
asyncResult = filter.invoke(nextNode, invocation);
} catch (Exception e) {
+ InvocationProfilerUtils.releaseDetailProfiler(invocation);
if (filter instanceof ListenableFilter) {
ListenableFilter listenableFilter = ((ListenableFilter) filter);
try {
@@ -108,6 +112,7 @@ public interface FilterChainBuilder {
}
return asyncResult.whenCompleteWithContext((r, t) -> {
+ InvocationProfilerUtils.releaseDetailProfiler(invocation);
if (filter instanceof ListenableFilter) {
ListenableFilter listenableFilter = ((ListenableFilter) filter);
Filter.Listener listener = listenableFilter.listener(invocation);
@@ -146,6 +151,7 @@ public interface FilterChainBuilder {
/**
* Works on consumer side
+ *
* @param <T>
* @param <TYPE>
*/
@@ -189,6 +195,7 @@ public interface FilterChainBuilder {
for (int i = filters.size() - 1; i >= 0; i--) {
FILTER filter = filters.get(i);
try {
+ InvocationProfilerUtils.releaseDetailProfiler(invocation);
if (filter instanceof ListenableFilter) {
ListenableFilter listenableFilter = ((ListenableFilter) filter);
Filter.Listener listener = listenableFilter.listener(invocation);
@@ -310,8 +317,10 @@ public interface FilterChainBuilder {
public Result invoke(Invocation invocation) throws RpcException {
Result asyncResult;
try {
+ InvocationProfilerUtils.enterDetailProfiler(invocation, () -> "Filter " + filter.getClass().getName() + " invoke.");
asyncResult = filter.invoke(nextNode, invocation);
} catch (Exception e) {
+ InvocationProfilerUtils.releaseDetailProfiler(invocation);
if (filter instanceof ListenableFilter) {
ListenableFilter listenableFilter = ((ListenableFilter) filter);
try {
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvoker.java
index 42ad94d..5406ac6 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvoker.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvoker.java
@@ -22,10 +22,12 @@ import org.apache.dubbo.common.config.Configuration;
import org.apache.dubbo.common.config.ConfigurationUtils;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.profiler.ProfilerSwitch;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.InvocationProfilerUtils;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcContext;
@@ -326,10 +328,19 @@ public abstract class AbstractClusterInvoker<T> implements ClusterInvoker<T> {
// ((RpcInvocation) invocation).addObjectAttachmentsIfAbsent(contextAttachments);
// }
+ InvocationProfilerUtils.enterDetailProfiler(invocation, () -> "Router route.");
List<Invoker<T>> invokers = list(invocation);
+ InvocationProfilerUtils.releaseDetailProfiler(invocation);
+
LoadBalance loadbalance = initLoadBalance(invokers, invocation);
RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
- return doInvoke(invocation, invokers, loadbalance);
+
+ InvocationProfilerUtils.enterDetailProfiler(invocation, () -> "Cluster " + this.getClass().getName() + " invoke.");
+ try {
+ return doInvoke(invocation, invokers, loadbalance);
+ } finally {
+ InvocationProfilerUtils.releaseDetailProfiler(invocation);
+ }
}
protected void checkWhetherDestroyed() {
@@ -361,9 +372,13 @@ public abstract class AbstractClusterInvoker<T> implements ClusterInvoker<T> {
setContext(invoker);
Result result;
try {
+ if (ProfilerSwitch.isEnableSimpleProfiler()) {
+ InvocationProfilerUtils.enterProfiler(invocation, "Invoker invoke. Target Address: " + invoker.getUrl().getAddress());
+ }
result = invoker.invoke(invocation);
} finally {
clearContext(invoker);
+ InvocationProfilerUtils.releaseSimpleProfiler(invocation);
}
return result;
}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/profiler/Profiler.java b/dubbo-common/src/main/java/org/apache/dubbo/common/profiler/Profiler.java
new file mode 100644
index 0000000..9ef8c91
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/profiler/Profiler.java
@@ -0,0 +1,104 @@
+/*
+ * 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.common.profiler;
+
+import org.apache.dubbo.common.threadlocal.InternalThreadLocal;
+
+import java.util.LinkedList;
+import java.util.List;
+
+public class Profiler {
+ public static final String PROFILER_KEY = "DUBBO_INVOKE_PROFILER";
+
+ private final static InternalThreadLocal<ProfilerEntry> bizProfiler = new InternalThreadLocal<>();
+
+ public static ProfilerEntry start(String message) {
+ return new ProfilerEntry(message);
+ }
+
+ public static ProfilerEntry enter(ProfilerEntry entry, String message) {
+ ProfilerEntry subEntry = new ProfilerEntry(message, entry, entry.getFirst());
+ entry.getSub().add(subEntry);
+ return subEntry;
+ }
+
+ public static ProfilerEntry release(ProfilerEntry entry) {
+ entry.setEndTime(System.nanoTime());
+ ProfilerEntry parent = entry.getParent();
+ if (parent != null) {
+ return parent;
+ } else {
+ return entry;
+ }
+ }
+
+ public static void setToBizProfiler(ProfilerEntry entry) {
+ bizProfiler.set(entry);
+ }
+
+ public static ProfilerEntry getBizProfiler() {
+ return bizProfiler.get();
+ }
+
+ public static void removeBizProfiler() {
+ bizProfiler.remove();
+ }
+
+ public static String buildDetail(ProfilerEntry entry) {
+ ProfilerEntry firstEntry = entry.getFirst();
+ long totalUsageTime = firstEntry.getEndTime() - firstEntry.getStartTime();
+ return "Start time: " + firstEntry.getStartTime() + "\n" +
+ String.join("\n", buildDetail(firstEntry, firstEntry.getStartTime(), totalUsageTime, 0));
+ }
+
+ public static List<String> buildDetail(ProfilerEntry entry, long startTime, long totalUsageTime, int depth) {
+ StringBuilder stringBuilder = new StringBuilder();
+ long usage = entry.getEndTime() - entry.getStartTime();
+ int percent = (int) (((usage) * 100) / totalUsageTime);
+
+ long offset = entry.getStartTime() - startTime;
+ List<String> lines = new LinkedList<>();
+ stringBuilder.append("+-[ Offset: ")
+ .append(offset / 1000_000).append(".").append(String.format("%06d", offset % 1000_000))
+ .append("ms; Usage: ")
+ .append(usage / 1000_000).append(".").append(String.format("%06d", usage % 1000_000))
+ .append("ms, ")
+ .append(percent)
+ .append("% ] ")
+ .append(entry.getMessage());
+ lines.add(stringBuilder.toString());
+ List<ProfilerEntry> entrySub = entry.getSub();
+ for (int i = 0, entrySubSize = entrySub.size(); i < entrySubSize; i++) {
+ ProfilerEntry sub = entrySub.get(i);
+ List<String> subLines = buildDetail(sub, startTime, totalUsageTime, depth + 1);
+ if (i < entrySubSize - 1) {
+ lines.add(" " + subLines.get(0));
+ for (int j = 1, subLinesSize = subLines.size(); j < subLinesSize; j++) {
+ String subLine = subLines.get(j);
+ lines.add(" |" + subLine);
+ }
+ } else {
+ lines.add(" " + subLines.get(0));
+ for (int j = 1, subLinesSize = subLines.size(); j < subLinesSize; j++) {
+ String subLine = subLines.get(j);
+ lines.add(" " + subLine);
+ }
+ }
+ }
+ return lines;
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/profiler/ProfilerEntry.java b/dubbo-common/src/main/java/org/apache/dubbo/common/profiler/ProfilerEntry.java
new file mode 100644
index 0000000..70d4e67
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/profiler/ProfilerEntry.java
@@ -0,0 +1,71 @@
+/*
+ * 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.common.profiler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ProfilerEntry {
+ private final List<ProfilerEntry> sub = new ArrayList<>(4);
+ private final String message;
+ private final ProfilerEntry parent;
+ private final ProfilerEntry first;
+ private final long startTime;
+ private long endTime;
+
+ public ProfilerEntry(String message) {
+ this.message = message;
+ this.parent = null;
+ this.first = this;
+ this.startTime = System.nanoTime();
+ }
+
+ public ProfilerEntry(String message, ProfilerEntry parentEntry, ProfilerEntry firstEntry) {
+ this.message = message;
+ this.parent = parentEntry;
+ this.first = firstEntry;
+ this.startTime = System.nanoTime();
+ }
+
+ public List<ProfilerEntry> getSub() {
+ return sub;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public ProfilerEntry getParent() {
+ return parent;
+ }
+
+ public ProfilerEntry getFirst() {
+ return first;
+ }
+
+ public long getStartTime() {
+ return startTime;
+ }
+
+ public void setEndTime(long endTime) {
+ this.endTime = endTime;
+ }
+
+ public long getEndTime() {
+ return endTime;
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/profiler/ProfilerSwitch.java b/dubbo-common/src/main/java/org/apache/dubbo/common/profiler/ProfilerSwitch.java
new file mode 100644
index 0000000..2025550
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/profiler/ProfilerSwitch.java
@@ -0,0 +1,63 @@
+/*
+ * 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.common.profiler;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * TODO
+ */
+public class ProfilerSwitch {
+ private final static AtomicBoolean enableDetailProfiler = new AtomicBoolean(false);
+
+ private final static AtomicBoolean enableSimpleProfiler = new AtomicBoolean(true);
+
+ private final static AtomicReference<Double> warnPercent = new AtomicReference<>(0.75);
+
+ public static void enableSimpleProfiler() {
+ enableSimpleProfiler.set(true);
+ }
+
+ public static void disableSimpleProfiler() {
+ enableSimpleProfiler.set(false);
+ }
+
+ public static void enableDetailProfiler() {
+ enableDetailProfiler.set(true);
+ }
+
+ public static void disableDetailProfiler() {
+ enableDetailProfiler.set(false);
+ }
+
+ public static boolean isEnableDetailProfiler() {
+ return enableDetailProfiler.get() && enableSimpleProfiler.get();
+ }
+
+ public static boolean isEnableSimpleProfiler() {
+ return enableSimpleProfiler.get();
+ }
+
+ public static double getWarnPercent() {
+ return warnPercent.get();
+ }
+
+ public static void setWarnPercent(double percent) {
+ warnPercent.set(percent);
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/profiler/ProfilerTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/profiler/ProfilerTest.java
new file mode 100644
index 0000000..05f740f
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/profiler/ProfilerTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.common.profiler;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class ProfilerTest {
+
+ @Test
+ public void testProfiler() {
+ ProfilerEntry one = Profiler.start("1");
+ ProfilerEntry two = Profiler.enter(one, "1-2");
+
+ ProfilerEntry three = Profiler.enter(two, "1-2-3");
+
+ ProfilerEntry four = Profiler.enter(three, "1-2-3-4");
+ Assertions.assertEquals(three, Profiler.release(four));
+
+ ProfilerEntry five = Profiler.enter(three, "1-2-3-5");
+ Assertions.assertEquals(three, Profiler.release(five));
+
+ Assertions.assertEquals(two, Profiler.release(three));
+
+ ProfilerEntry six = Profiler.enter(two, "1-2-6");
+ Assertions.assertEquals(two, Profiler.release(six));
+
+ ProfilerEntry seven = Profiler.enter(six, "1-2-6-7");
+ Assertions.assertEquals(six, Profiler.release(seven));
+
+ ProfilerEntry eight = Profiler.enter(six, "1-2-6-8");
+ Assertions.assertEquals(six, Profiler.release(eight));
+
+ Assertions.assertEquals(2, two.getSub().size());
+ Assertions.assertEquals(three, two.getSub().get(0));
+ Assertions.assertEquals(six, two.getSub().get(1));
+
+ Profiler.release(two);
+
+ ProfilerEntry nine = Profiler.enter(one, "1-9");
+ Profiler.release(nine);
+
+ Profiler.release(one);
+
+ /*
+ * Start time: 287395734500659
+ * +-[ Offset: 0.000000ms; Usage: 4.721583ms, 100% ] 1
+ * +-[ Offset: 0.013136ms; Usage: 4.706288ms, 99% ] 1-2
+ * | +-[ Offset: 0.027903ms; Usage: 4.662918ms, 98% ] 1-2-3
+ * | | +-[ Offset: 0.029742ms; Usage: 0.003785ms, 0% ] 1-2-3-4
+ * | | +-[ Offset: 4.688477ms; Usage: 0.001398ms, 0% ] 1-2-3-5
+ * | +-[ Offset: 4.693346ms; Usage: 0.000316ms, 0% ] 1-2-6
+ * | +-[ Offset: 4.695191ms; Usage: 0.000212ms, 0% ] 1-2-6-7
+ * | +-[ Offset: 4.696655ms; Usage: 0.000195ms, 0% ] 1-2-6-8
+ * +-[ Offset: 4.721044ms; Usage: 0.000270ms, 0% ] 1-9
+ */
+ Assertions.assertEquals(Profiler.buildDetail(one), Profiler.buildDetail(two));
+ Assertions.assertEquals(Profiler.buildDetail(one), Profiler.buildDetail(three));
+ Assertions.assertEquals(Profiler.buildDetail(one), Profiler.buildDetail(four));
+ Assertions.assertEquals(Profiler.buildDetail(one), Profiler.buildDetail(five));
+ Assertions.assertEquals(Profiler.buildDetail(one), Profiler.buildDetail(six));
+ Assertions.assertEquals(Profiler.buildDetail(one), Profiler.buildDetail(seven));
+ Assertions.assertEquals(Profiler.buildDetail(one), Profiler.buildDetail(eight));
+ Assertions.assertEquals(Profiler.buildDetail(one), Profiler.buildDetail(nine));
+ }
+
+ @Test
+ public void testBizProfiler() {
+ Assertions.assertNull(Profiler.getBizProfiler());
+
+ ProfilerEntry one = Profiler.start("1");
+
+ Profiler.setToBizProfiler(one);
+
+ Profiler.release(Profiler.enter(Profiler.getBizProfiler(), "1-2"));
+
+ Assertions.assertEquals(one, Profiler.getBizProfiler());
+ Assertions.assertEquals(1, one.getSub().size());
+
+ Profiler.removeBizProfiler();
+ Assertions.assertNull(Profiler.getBizProfiler());
+ }
+}
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/BaseOffline.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/BaseOffline.java
index 425d37f..d7b4baf 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/BaseOffline.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/BaseOffline.java
@@ -32,7 +32,7 @@ import java.util.Collection;
import java.util.List;
public class BaseOffline implements BaseCommand {
- private Logger logger = LoggerFactory.getLogger(OfflineInterface.class);
+ private Logger logger = LoggerFactory.getLogger(BaseOffline.class);
public FrameworkServiceRepository serviceRepository;
public BaseOffline(FrameworkModel frameworkModel) {
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/BaseOnline.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/BaseOnline.java
index 9ee67e5..a060fb8 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/BaseOnline.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/BaseOnline.java
@@ -32,7 +32,7 @@ import java.util.Collection;
import java.util.List;
public class BaseOnline implements BaseCommand {
- private static final Logger logger = LoggerFactory.getLogger(Online.class);
+ private static final Logger logger = LoggerFactory.getLogger(BaseOnline.class);
public FrameworkServiceRepository serviceRepository;
public BaseOnline(FrameworkModel frameworkModel) {
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/DisableDetailProfiler.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/DisableDetailProfiler.java
new file mode 100644
index 0000000..5ac413b
--- /dev/null
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/DisableDetailProfiler.java
@@ -0,0 +1,36 @@
+/*
+ * 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.qos.command.impl;
+
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.profiler.ProfilerSwitch;
+import org.apache.dubbo.qos.command.BaseCommand;
+import org.apache.dubbo.qos.command.CommandContext;
+import org.apache.dubbo.qos.command.annotation.Cmd;
+
+@Cmd(name = "disableDetailProfiler", summary = "Disable Dubbo Invocation Profiler.")
+public class DisableDetailProfiler implements BaseCommand {
+ private final static Logger logger = LoggerFactory.getLogger(DisableDetailProfiler.class);
+
+ @Override
+ public String execute(CommandContext commandContext, String[] args) {
+ ProfilerSwitch.disableDetailProfiler();
+ logger.warn("Dubbo Invocation Profiler has been disabled.");
+ return "OK";
+ }
+}
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/DisableSimpleProfiler.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/DisableSimpleProfiler.java
new file mode 100644
index 0000000..22ffba5
--- /dev/null
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/DisableSimpleProfiler.java
@@ -0,0 +1,36 @@
+/*
+ * 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.qos.command.impl;
+
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.profiler.ProfilerSwitch;
+import org.apache.dubbo.qos.command.BaseCommand;
+import org.apache.dubbo.qos.command.CommandContext;
+import org.apache.dubbo.qos.command.annotation.Cmd;
+
+@Cmd(name = "disableSimpleProfiler", summary = "Disable Dubbo Invocation Profiler.")
+public class DisableSimpleProfiler implements BaseCommand {
+ private final static Logger logger = LoggerFactory.getLogger(DisableSimpleProfiler.class);
+
+ @Override
+ public String execute(CommandContext commandContext, String[] args) {
+ ProfilerSwitch.disableSimpleProfiler();
+ logger.warn("Dubbo Invocation Profiler has been disabled.");
+ return "OK";
+ }
+}
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/EnableDetailProfiler.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/EnableDetailProfiler.java
new file mode 100644
index 0000000..bc1a136
--- /dev/null
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/EnableDetailProfiler.java
@@ -0,0 +1,36 @@
+/*
+ * 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.qos.command.impl;
+
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.profiler.ProfilerSwitch;
+import org.apache.dubbo.qos.command.BaseCommand;
+import org.apache.dubbo.qos.command.CommandContext;
+import org.apache.dubbo.qos.command.annotation.Cmd;
+
+@Cmd(name = "enableDetailProfiler", summary = "Enable Dubbo Invocation Profiler.")
+public class EnableDetailProfiler implements BaseCommand {
+ private final static Logger logger = LoggerFactory.getLogger(EnableDetailProfiler.class);
+
+ @Override
+ public String execute(CommandContext commandContext, String[] args) {
+ ProfilerSwitch.enableDetailProfiler();
+ logger.warn("Dubbo Invocation Profiler has been enabled.");
+ return "OK";
+ }
+}
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/EnableSimpleProfiler.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/EnableSimpleProfiler.java
new file mode 100644
index 0000000..d46df99
--- /dev/null
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/EnableSimpleProfiler.java
@@ -0,0 +1,36 @@
+/*
+ * 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.qos.command.impl;
+
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.profiler.ProfilerSwitch;
+import org.apache.dubbo.qos.command.BaseCommand;
+import org.apache.dubbo.qos.command.CommandContext;
+import org.apache.dubbo.qos.command.annotation.Cmd;
+
+@Cmd(name = "enableSimpleProfiler", summary = "Enable Dubbo Invocation Profiler.")
+public class EnableSimpleProfiler implements BaseCommand {
+ private final static Logger logger = LoggerFactory.getLogger(EnableSimpleProfiler.class);
+
+ @Override
+ public String execute(CommandContext commandContext, String[] args) {
+ ProfilerSwitch.enableSimpleProfiler();
+ logger.warn("Dubbo Invocation Profiler has been enabled.");
+ return "OK";
+ }
+}
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/SetProfilerWarnPercent.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/SetProfilerWarnPercent.java
new file mode 100644
index 0000000..e6014f6
--- /dev/null
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/SetProfilerWarnPercent.java
@@ -0,0 +1,39 @@
+/*
+ * 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.qos.command.impl;
+
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.profiler.ProfilerSwitch;
+import org.apache.dubbo.qos.command.BaseCommand;
+import org.apache.dubbo.qos.command.CommandContext;
+import org.apache.dubbo.qos.command.annotation.Cmd;
+
+@Cmd(name = "setProfilerWarnPercent", example = "setProfilerWarnPercent 0.75", summary = "Disable Dubbo Invocation Profiler.")
+public class SetProfilerWarnPercent implements BaseCommand {
+ private final static Logger logger = LoggerFactory.getLogger(SetProfilerWarnPercent.class);
+
+ @Override
+ public String execute(CommandContext commandContext, String[] args) {
+ if (args == null || args.length != 1) {
+ return "args error. example: setProfilerWarnPercent 0.75";
+ }
+ ProfilerSwitch.setWarnPercent(Double.parseDouble(args[0]));
+ logger.warn("Dubbo Invocation Profiler warn percent has been set to " + args[0]);
+ return "OK";
+ }
+}
diff --git a/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.command.BaseCommand b/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.command.BaseCommand
index 78f5337..d00c59c 100644
--- a/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.command.BaseCommand
+++ b/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.command.BaseCommand
@@ -19,3 +19,8 @@ invoke=org.apache.dubbo.qos.command.impl.InvokeTelnet
select=org.apache.dubbo.qos.command.impl.SelectTelnet
ps=org.apache.dubbo.qos.command.impl.PortTelnet
shutdown=org.apache.dubbo.qos.command.impl.ShutdownTelnet
+enableDetailProfiler=org.apache.dubbo.qos.command.impl.EnableDetailProfiler
+disableDetailProfiler=org.apache.dubbo.qos.command.impl.DisableDetailProfiler
+enableSimpleProfiler=org.apache.dubbo.qos.command.impl.EnableSimpleProfiler
+disableSimpleProfiler=org.apache.dubbo.qos.command.impl.DisableSimpleProfiler
+setProfilerWarnPercent=org.apache.dubbo.qos.command.impl.SetProfilerWarnPercent
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/util/CommandHelperTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/util/CommandHelperTest.java
index 3eb73a2..e765213 100644
--- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/util/CommandHelperTest.java
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/util/CommandHelperTest.java
@@ -19,6 +19,10 @@ package org.apache.dubbo.qos.command.util;
import org.apache.dubbo.qos.command.GreetingCommand;
import org.apache.dubbo.qos.command.impl.ChangeTelnet;
import org.apache.dubbo.qos.command.impl.CountTelnet;
+import org.apache.dubbo.qos.command.impl.DisableDetailProfiler;
+import org.apache.dubbo.qos.command.impl.DisableSimpleProfiler;
+import org.apache.dubbo.qos.command.impl.EnableDetailProfiler;
+import org.apache.dubbo.qos.command.impl.EnableSimpleProfiler;
import org.apache.dubbo.qos.command.impl.Help;
import org.apache.dubbo.qos.command.impl.InvokeTelnet;
import org.apache.dubbo.qos.command.impl.Live;
@@ -35,10 +39,12 @@ import org.apache.dubbo.qos.command.impl.PwdTelnet;
import org.apache.dubbo.qos.command.impl.Quit;
import org.apache.dubbo.qos.command.impl.Ready;
import org.apache.dubbo.qos.command.impl.SelectTelnet;
+import org.apache.dubbo.qos.command.impl.SetProfilerWarnPercent;
import org.apache.dubbo.qos.command.impl.ShutdownTelnet;
import org.apache.dubbo.qos.command.impl.Startup;
import org.apache.dubbo.qos.command.impl.Version;
import org.apache.dubbo.rpc.model.FrameworkModel;
+
import org.junit.jupiter.api.Test;
import java.util.LinkedList;
@@ -88,6 +94,11 @@ public class CommandHelperTest {
expectedClasses.add(PortTelnet.class);
expectedClasses.add(PwdTelnet.class);
expectedClasses.add(ShutdownTelnet.class);
+ expectedClasses.add(EnableDetailProfiler.class);
+ expectedClasses.add(DisableDetailProfiler.class);
+ expectedClasses.add(EnableSimpleProfiler.class);
+ expectedClasses.add(DisableSimpleProfiler.class);
+ expectedClasses.add(SetProfilerWarnPercent.class);
assertThat(classes, containsInAnyOrder(expectedClasses.toArray(new Class<?>[0])));
}
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/InvocationProfilerUtils.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/InvocationProfilerUtils.java
new file mode 100644
index 0000000..0921be0
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/InvocationProfilerUtils.java
@@ -0,0 +1,77 @@
+/*
+ * 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.profiler.Profiler;
+import org.apache.dubbo.common.profiler.ProfilerEntry;
+import org.apache.dubbo.common.profiler.ProfilerSwitch;
+
+import java.util.concurrent.Callable;
+
+public class InvocationProfilerUtils {
+
+ public static void enterSimpleProfiler(Invocation invocation, Callable<String> messageCallable) {
+ if (ProfilerSwitch.isEnableSimpleProfiler()) {
+ enterProfiler(invocation, messageCallable);
+ }
+ }
+
+ public static void releaseSimpleProfiler(Invocation invocation) {
+ if (ProfilerSwitch.isEnableSimpleProfiler()) {
+ releaseProfiler(invocation);
+ }
+ }
+
+ public static void enterDetailProfiler(Invocation invocation, Callable<String> messageCallable) {
+ if (ProfilerSwitch.isEnableDetailProfiler()) {
+ enterProfiler(invocation, messageCallable);
+ }
+ }
+
+ public static void releaseDetailProfiler(Invocation invocation) {
+ if (ProfilerSwitch.isEnableDetailProfiler()) {
+ releaseProfiler(invocation);
+ }
+ }
+
+ public static void enterProfiler(Invocation invocation, String message) {
+ Object fromInvocation = invocation.get(Profiler.PROFILER_KEY);
+ if (fromInvocation instanceof ProfilerEntry) {
+ invocation.put(Profiler.PROFILER_KEY, Profiler.enter((ProfilerEntry) fromInvocation, message));
+ }
+ }
+
+ public static void enterProfiler(Invocation invocation, Callable<String> messageCallable) {
+ Object fromInvocation = invocation.get(Profiler.PROFILER_KEY);
+ if (fromInvocation instanceof ProfilerEntry) {
+ String message = "";
+ try {
+ message = messageCallable.call();
+ } catch (Exception ignore) {
+
+ }
+ invocation.put(Profiler.PROFILER_KEY, Profiler.enter((ProfilerEntry) fromInvocation, message));
+ }
+ }
+
+ public static void releaseProfiler(Invocation invocation) {
+ Object fromInvocation = invocation.get(Profiler.PROFILER_KEY);
+ if (fromInvocation instanceof ProfilerEntry) {
+ invocation.put(Profiler.PROFILER_KEY, Profiler.release((ProfilerEntry) fromInvocation));
+ }
+ }
+}
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ProfilerServerFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ProfilerServerFilter.java
new file mode 100644
index 0000000..c8ad2ed
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ProfilerServerFilter.java
@@ -0,0 +1,101 @@
+/*
+ * 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.filter;
+
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.profiler.Profiler;
+import org.apache.dubbo.common.profiler.ProfilerEntry;
+import org.apache.dubbo.common.profiler.ProfilerSwitch;
+import org.apache.dubbo.rpc.BaseFilter;
+import org.apache.dubbo.rpc.Filter;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Result;
+import org.apache.dubbo.rpc.RpcContext;
+import org.apache.dubbo.rpc.RpcException;
+
+import java.util.Map;
+
+import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT;
+import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER;
+import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY;
+
+@Activate(group = PROVIDER, order = Integer.MIN_VALUE)
+public class ProfilerServerFilter implements Filter, BaseFilter.Listener {
+ private final static String CLIENT_IP_KEY = "client_ip";
+ private final static Logger logger = LoggerFactory.getLogger(ProfilerServerFilter.class);
+
+ @Override
+ public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+ if (ProfilerSwitch.isEnableSimpleProfiler()) {
+ ProfilerEntry bizProfiler = Profiler.start("Receive request. Server invoke begin.");
+ invocation.put(Profiler.PROFILER_KEY, bizProfiler);
+ invocation.put(CLIENT_IP_KEY, RpcContext.getServiceContext().getRemoteAddressString());
+ }
+
+ return invoker.invoke(invocation);
+ }
+
+ @Override
+ public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {
+ afterInvoke(invoker, invocation);
+ }
+
+ @Override
+ public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
+ afterInvoke(invoker, invocation);
+ }
+
+ private void afterInvoke(Invoker<?> invoker, Invocation invocation) {
+ if (ProfilerSwitch.isEnableSimpleProfiler()) {
+ Object fromInvocation = invocation.get(Profiler.PROFILER_KEY);
+ if (fromInvocation instanceof ProfilerEntry) {
+ ProfilerEntry profiler = Profiler.release((ProfilerEntry) fromInvocation);
+ invocation.put(Profiler.PROFILER_KEY, profiler);
+
+ dumpIfNeed(invoker, invocation, profiler);
+ }
+ }
+ }
+
+ private void dumpIfNeed(Invoker<?> invoker, Invocation invocation, ProfilerEntry profiler) {
+ int timeout;
+ Object timeoutKey = invocation.getObjectAttachment(TIMEOUT_KEY);
+ if (timeoutKey instanceof Integer) {
+ timeout = (Integer) timeoutKey;
+ } else {
+ timeout = invoker.getUrl().getParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT);
+ }
+ long usage = profiler.getEndTime() - profiler.getStartTime();
+ if (usage > (timeout * 1000_000 * ProfilerSwitch.getWarnPercent())) {
+
+ StringBuilder attachment = new StringBuilder();
+ for (Map.Entry<String, Object> entry : invocation.getObjectAttachments().entrySet()) {
+ attachment.append(entry.getKey()).append("=").append(entry.getValue()).append(";\n");
+ }
+
+ logger.warn(String.format("[Dubbo-Provider] execute service %s#%s cost %d.%06d ms, this invocation almost (maybe already) timeout\n" +
+ "client: %s\n" +
+ "invocation context:\n%s" +
+ "thread info: \n%s",
+ invocation.getTargetServiceUniqueName(), invocation.getMethodName(), usage / 1000_000, usage % 1000_000,
+ invocation.get(CLIENT_IP_KEY), attachment, Profiler.buildDetail(profiler)));
+ }
+ }
+}
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java
index d865cb1..618d662 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java
@@ -251,7 +251,7 @@ public abstract class AbstractInvoker<T> implements Invoker<T> {
* must call {@link java.util.concurrent.CompletableFuture#get(long, TimeUnit)} because
* {@link java.util.concurrent.CompletableFuture#get()} was proved to have serious performance drop.
*/
- Object timeout = invocation.get(TIMEOUT_KEY);
+ Object timeout = invocation.getObjectAttachment(TIMEOUT_KEY);
if (timeout instanceof Integer) {
asyncResult.get((Integer) timeout, TimeUnit.MILLISECONDS);
} else {
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyInvoker.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyInvoker.java
index 84dfd33..11cafc7 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyInvoker.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyInvoker.java
@@ -19,6 +19,9 @@ package org.apache.dubbo.rpc.proxy;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.profiler.Profiler;
+import org.apache.dubbo.common.profiler.ProfilerEntry;
+import org.apache.dubbo.common.profiler.ProfilerSwitch;
import org.apache.dubbo.rpc.AppResponse;
import org.apache.dubbo.rpc.AsyncContextImpl;
import org.apache.dubbo.rpc.AsyncRpcResult;
@@ -83,7 +86,27 @@ public abstract class AbstractProxyInvoker<T> implements Invoker<T> {
@Override
public Result invoke(Invocation invocation) throws RpcException {
try {
+ if (ProfilerSwitch.isEnableSimpleProfiler()) {
+ Object fromInvocation = invocation.get(Profiler.PROFILER_KEY);
+ if (fromInvocation instanceof ProfilerEntry) {
+ ProfilerEntry profiler = Profiler.enter((ProfilerEntry) fromInvocation, "Receive request. Server biz impl invoke begin.");
+ invocation.put(Profiler.PROFILER_KEY, profiler);
+ // TODO clear after invoke
+ Profiler.setToBizProfiler(profiler);
+ }
+ }
+
Object value = doInvoke(proxy, invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments());
+
+ if (ProfilerSwitch.isEnableSimpleProfiler()) {
+ Object fromInvocation = invocation.get(Profiler.PROFILER_KEY);
+ if (fromInvocation instanceof ProfilerEntry) {
+ ProfilerEntry profiler = Profiler.release((ProfilerEntry) fromInvocation);
+ invocation.put(Profiler.PROFILER_KEY, profiler);
+ }
+ }
+ Profiler.removeBizProfiler();
+
CompletableFuture<Object> future = wrapWithFuture(value, invocation);
CompletableFuture<AppResponse> appResponseFuture = future.handle((obj, t) -> {
AppResponse result = new AppResponse(invocation);
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvokerInvocationHandler.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvokerInvocationHandler.java
index 42125ee..1b440b5 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvokerInvocationHandler.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvokerInvocationHandler.java
@@ -19,6 +19,9 @@ package org.apache.dubbo.rpc.proxy;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.profiler.Profiler;
+import org.apache.dubbo.common.profiler.ProfilerEntry;
+import org.apache.dubbo.common.profiler.ProfilerSwitch;
import org.apache.dubbo.rpc.Constants;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.RpcInvocation;
@@ -28,6 +31,10 @@ import org.apache.dubbo.rpc.model.ServiceModel;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
+import java.util.Map;
+
+import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT;
+import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY;
/**
* InvokerHandler
@@ -77,6 +84,46 @@ public class InvokerInvocationHandler implements InvocationHandler {
rpcInvocation.put(Constants.METHOD_MODEL, ((ConsumerModel) serviceModel).getMethodModel(method));
}
+ if (ProfilerSwitch.isEnableSimpleProfiler()) {
+ ProfilerEntry parentProfiler = Profiler.getBizProfiler();
+ ProfilerEntry bizProfiler;
+ boolean containsBizProfiler = false;
+ if (parentProfiler != null) {
+ containsBizProfiler = true;
+ bizProfiler = Profiler.enter(parentProfiler, "Receive request. Client invoke begin.");
+ } else {
+ bizProfiler = Profiler.start("Receive request. Client invoke begin.");
+ }
+ rpcInvocation.put(Profiler.PROFILER_KEY, bizProfiler);
+ try {
+ return invoker.invoke(rpcInvocation).recreate();
+ } finally {
+ Profiler.release(bizProfiler);
+ if (!containsBizProfiler) {
+ int timeout;
+ Object timeoutKey = rpcInvocation.getObjectAttachment(TIMEOUT_KEY);
+ if (timeoutKey instanceof Integer) {
+ timeout = (Integer) timeoutKey;
+ } else {
+ timeout = url.getParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT);
+ }
+ long usage = bizProfiler.getEndTime() - bizProfiler.getStartTime();
+ if (usage > (timeout * 1000_000 * ProfilerSwitch.getWarnPercent())) {
+ StringBuilder attachment = new StringBuilder();
+ for (Map.Entry<String, Object> entry : rpcInvocation.getObjectAttachments().entrySet()) {
+ attachment.append(entry.getKey()).append("=").append(entry.getValue()).append(";\n");
+ }
+
+ logger.warn(String.format("[Dubbo-Consumer] execute service %s#%s cost %d.%06d ms, this invocation almost (maybe already) timeout\n" +
+ "invocation context:\n%s" +
+ "thread info: \n%s",
+ protocolServiceKey, methodName, usage / 1000_000, usage % 1000_000,
+ attachment, Profiler.buildDetail(bizProfiler)));
+ }
+ }
+ }
+ }
+
return invoker.invoke(rpcInvocation).recreate();
}
}
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter b/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter
index 34d56b4..bed37fa 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter
@@ -12,3 +12,4 @@ deprecated=org.apache.dubbo.rpc.filter.DeprecatedFilter
compatible=org.apache.dubbo.rpc.filter.CompatibleFilter
timeout=org.apache.dubbo.rpc.filter.TimeoutFilter
tps=org.apache.dubbo.rpc.filter.TpsLimitFilter
+profiler-server=org.apache.dubbo.rpc.filter.ProfilerServerFilter
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvoker.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvoker.java
index eb09f5e..e749699 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvoker.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvoker.java
@@ -100,7 +100,7 @@ public class DubboInvoker<T> extends AbstractInvoker<T> {
try {
boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
int timeout = calculateTimeout(invocation, methodName);
- invocation.put(TIMEOUT_KEY, timeout);
+ invocation.setAttachment(TIMEOUT_KEY, timeout);
if (isOneway) {
boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
currentClient.send(inv, isSent);
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleInvoker.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleInvoker.java
index a57f356..1613d06 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleInvoker.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleInvoker.java
@@ -86,7 +86,7 @@ public class TripleInvoker<T> extends AbstractInvoker<T> {
getUrl().getParameter(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION));
try {
int timeout = calculateTimeout(invocation, methodName);
- invocation.put(TIMEOUT_KEY, timeout);
+ invocation.setAttachment(TIMEOUT_KEY, timeout);
ExecutorService executor = getCallbackExecutor(getUrl(), inv);
// create request.
Request req = new Request();