You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by cr...@apache.org on 2022/06/15 09:40:44 UTC

[dubbo] branch 3.0 updated: [3.0] Add safe foreach in RpcInvocation & Revert synchronizedMap in RpcInvocation (#10160)

This is an automated email from the ASF dual-hosted git repository.

crazyhzm 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 89863e048d [3.0] Add safe foreach in RpcInvocation & Revert synchronizedMap in RpcInvocation (#10160)
89863e048d is described below

commit 89863e048d745b5c655a6b859347b32939b743db
Author: Albumen Kevin <jh...@gmail.com>
AuthorDate: Wed Jun 15 17:40:20 2022 +0800

    [3.0] Add safe foreach in RpcInvocation & Revert synchronizedMap in RpcInvocation (#10160)
    
    * revert
    
    * [3.0] Add safe foreach in RpcInvocation & Revert synchronizedMap in RpcInvocation
    
    * fix ut
---
 .../cluster/router/mock/MockInvokersSelector.java  |   2 +-
 .../rpc/cluster/directory/MockDirInvocation.java   |  11 ++
 .../router/mock/MockInvokersSelectorTest.java      |   6 +-
 .../java/com/alibaba/dubbo/rpc/Invocation.java     |  12 ++
 .../org/apache/dubbo/service/MockInvocation.java   |  11 ++
 .../main/java/org/apache/dubbo/rpc/Invocation.java |   7 +
 .../java/org/apache/dubbo/rpc/RpcInvocation.java   | 216 +++++++++++++++------
 .../dubbo/rpc/filter/ProfilerServerFilter.java     |   6 +-
 .../org/apache/dubbo/rpc/filter/TokenFilter.java   |   5 +-
 .../apache/dubbo/rpc/filter/TokenHeaderFilter.java |   5 +-
 .../apache/dubbo/rpc/protocol/AbstractInvoker.java |   2 +-
 .../org/apache/dubbo/rpc/proxy/InvocationUtil.java |   6 +-
 .../apache/dubbo/rpc/filter/TokenFilterTest.java   |   4 +-
 .../rpc/protocol/dubbo/CallbackServiceCodec.java   |   2 +-
 .../protocol/dubbo/DecodeableRpcInvocation.java    |   8 +-
 .../dubbo/rpc/protocol/dubbo/DubboProtocol.java    |  20 +-
 .../rpc/protocol/dubbo/DubboCountCodecTest.java    |   4 +-
 .../dubbo/rpc/protocol/injvm/InjvmInvoker.java     |   4 +-
 .../dubbo/rpc/protocol/tri/TripleInvoker.java      |  15 +-
 19 files changed, 226 insertions(+), 120 deletions(-)

diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mock/MockInvokersSelector.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mock/MockInvokersSelector.java
index b031c00338..1ea813c598 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mock/MockInvokersSelector.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mock/MockInvokersSelector.java
@@ -65,7 +65,7 @@ public class MockInvokersSelector<T> extends AbstractStateRouter<T> {
             }
             return invokers.and(normalInvokers);
         } else {
-            String value = (String) invocation.getObjectAttachments().get(INVOCATION_NEED_MOCK);
+            String value = (String) invocation.getObjectAttachmentWithoutConvert(INVOCATION_NEED_MOCK);
             if (value == null) {
                 if (needToPrintMessage) {
                     messageHolder.set("invocation.need.mock not set. Return normal Invokers.");
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/directory/MockDirInvocation.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/directory/MockDirInvocation.java
index 75ad1ccc62..246c7bdeec 100644
--- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/directory/MockDirInvocation.java
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/directory/MockDirInvocation.java
@@ -23,6 +23,7 @@ import org.apache.dubbo.rpc.model.ServiceModel;
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.function.Consumer;
 
 import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_VERSION_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
@@ -84,6 +85,16 @@ public class MockDirInvocation implements Invocation {
         return attachments;
     }
 
+    @Override
+    public Map<String, Object> copyObjectAttachments() {
+        return new HashMap<>(attachments);
+    }
+
+    @Override
+    public void foreachAttachment(Consumer<Map.Entry<String, Object>> consumer) {
+        attachments.entrySet().forEach(consumer);
+    }
+
     @Override
     public void setAttachment(String key, String value) {
         setObjectAttachment(key, value);
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mock/MockInvokersSelectorTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mock/MockInvokersSelectorTest.java
index 6798afd4ff..7d6096df1b 100644
--- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mock/MockInvokersSelectorTest.java
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mock/MockInvokersSelectorTest.java
@@ -27,9 +27,7 @@ import org.junit.jupiter.api.Test;
 import org.mockito.Mockito;
 
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 import static org.apache.dubbo.rpc.cluster.Constants.INVOCATION_NEED_MOCK;
 
@@ -59,9 +57,7 @@ public class MockInvokersSelectorTest {
         Assertions.assertTrue(invokers.contains(invoker3));
 
         // rpcInvocation have an attached "invocation.need.mock" parameter, so it will filter out the invoker whose protocol is mock
-        Map<String,Object> attachments = new HashMap<>();
-        attachments.put(INVOCATION_NEED_MOCK,"true");
-        Mockito.when(rpcInvocation.getObjectAttachments()).thenReturn(attachments);
+        Mockito.when(rpcInvocation.getObjectAttachmentWithoutConvert(INVOCATION_NEED_MOCK)).thenReturn("true");
         invokers = selector.route(providers.clone(), consumerURL, rpcInvocation, false, new Holder<>());
         Assertions.assertEquals(invokers.size(),2);
         Assertions.assertTrue(invokers.contains(invoker1));
diff --git a/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/Invocation.java b/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/Invocation.java
index 92e483187a..c016e3b0ea 100644
--- a/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/Invocation.java
+++ b/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/Invocation.java
@@ -20,7 +20,9 @@ package com.alibaba.dubbo.rpc;
 import org.apache.dubbo.rpc.model.ServiceModel;
 
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.Map;
+import java.util.function.Consumer;
 
 @Deprecated
 public interface Invocation extends org.apache.dubbo.rpc.Invocation {
@@ -107,6 +109,16 @@ public interface Invocation extends org.apache.dubbo.rpc.Invocation {
         return Collections.emptyMap();
     }
 
+    @Override
+    default Map<String, Object> copyObjectAttachments() {
+        return new HashMap<>(getObjectAttachments());
+    }
+
+    @Override
+    default void foreachAttachment(Consumer<Map.Entry<String, Object>> consumer) {
+        getObjectAttachments().entrySet().forEach(consumer);
+    }
+
     @Override
     default Object getObjectAttachment(String key) {
         return null;
diff --git a/dubbo-compatible/src/test/java/org/apache/dubbo/service/MockInvocation.java b/dubbo-compatible/src/test/java/org/apache/dubbo/service/MockInvocation.java
index 532f6bde44..233260f3a2 100644
--- a/dubbo-compatible/src/test/java/org/apache/dubbo/service/MockInvocation.java
+++ b/dubbo-compatible/src/test/java/org/apache/dubbo/service/MockInvocation.java
@@ -23,6 +23,7 @@ import org.apache.dubbo.rpc.model.ServiceModel;
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.function.Consumer;
 
 import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_VERSION_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
@@ -88,6 +89,16 @@ public class MockInvocation implements Invocation {
         return attachments;
     }
 
+    @Override
+    public Map<String, Object> copyObjectAttachments() {
+        return new HashMap<>(attachments);
+    }
+
+    @Override
+    public void foreachAttachment(Consumer<Map.Entry<String, Object>> consumer) {
+        attachments.entrySet().forEach(consumer);
+    }
+
     @Override
     public void setAttachment(String key, String value) {
         setObjectAttachment(key, value);
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Invocation.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Invocation.java
index 3a4e21613d..d2d8f64365 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Invocation.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Invocation.java
@@ -22,6 +22,7 @@ import org.apache.dubbo.rpc.model.ScopeModelUtil;
 import org.apache.dubbo.rpc.model.ServiceModel;
 
 import java.util.Map;
+import java.util.function.Consumer;
 import java.util.stream.Stream;
 
 /**
@@ -91,6 +92,12 @@ public interface Invocation {
     @Experimental("Experiment api for supporting Object transmission")
     Map<String, Object> getObjectAttachments();
 
+    @Experimental("Experiment api for supporting Object transmission")
+    Map<String, Object> copyObjectAttachments();
+
+    @Experimental("Experiment api for supporting Object transmission")
+    void foreachAttachment(Consumer<Map.Entry<String, Object>> consumer);
+
     void setAttachment(String key, String value);
 
     @Experimental("Experiment api for supporting Object transmission")
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcInvocation.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcInvocation.java
index 9e4244254c..f5ec9fd064 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcInvocation.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcInvocation.java
@@ -17,7 +17,6 @@
 package org.apache.dubbo.rpc;
 
 import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.common.utils.ReflectUtils;
 import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.rpc.model.FrameworkModel;
@@ -36,6 +35,9 @@ import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.function.Consumer;
 import java.util.stream.Stream;
 
 import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY;
@@ -75,6 +77,8 @@ public class RpcInvocation implements Invocation, Serializable {
      */
     private Map<String, Object> attachments;
 
+    private transient Lock attachmentLock = new ReentrantLock();
+
     /**
      * Only used on the caller side, will not appear on the wire.
      */
@@ -113,9 +117,7 @@ public class RpcInvocation implements Invocation, Serializable {
     public RpcInvocation(Invocation invocation, Invoker<?> invoker) {
         this(invocation.getTargetServiceUniqueName(), invocation.getServiceModel(), invocation.getMethodName(), invocation.getServiceName(),
             invocation.getProtocolServiceKey(), invocation.getParameterTypes(), invocation.getArguments(),
-            CollectionUtils.isEmptyMap(invocation.getObjectAttachments()) ?
-                Collections.synchronizedMap(new HashMap<>()) : Collections.synchronizedMap(invocation.getObjectAttachments()),
-            invocation.getInvoker(), invocation.getAttributes(),
+            invocation.copyObjectAttachments(), invocation.getInvoker(), invocation.getAttributes(),
             invocation instanceof RpcInvocation ? ((RpcInvocation) invocation).getInvokeMode() : null);
         if (invoker != null) {
             URL url = invoker.getUrl();
@@ -243,7 +245,7 @@ public class RpcInvocation implements Invocation, Serializable {
         this.protocolServiceKey = protocolServiceKey;
         this.parameterTypes = parameterTypes == null ? new Class<?>[0] : parameterTypes;
         this.arguments = arguments == null ? new Object[0] : arguments;
-        this.attachments = attachments == null ? Collections.synchronizedMap(new HashMap<>()) : attachments;
+        this.attachments = attachments == null ? new HashMap<>() : attachments;
         this.attributes = attributes == null ? Collections.synchronizedMap(new HashMap<>()) : attributes;
         this.invoker = invoker;
         initParameterDesc();
@@ -380,11 +382,46 @@ public class RpcInvocation implements Invocation, Serializable {
 
     @Override
     public Map<String, Object> getObjectAttachments() {
-        return attachments;
+        try {
+            attachmentLock.lock();
+            return attachments;
+        } finally {
+            attachmentLock.unlock();
+        }
+    }
+
+    @Override
+    public Map<String, Object> copyObjectAttachments() {
+        try {
+            attachmentLock.lock();
+            if (attachments == null) {
+                return new HashMap<>();
+            }
+            return new HashMap<>(attachments);
+        } finally {
+            attachmentLock.unlock();
+        }
+    }
+
+    @Override
+    public void foreachAttachment(Consumer<Map.Entry<String, Object>> consumer) {
+        try {
+            attachmentLock.lock();
+            if (attachments != null) {
+                attachments.entrySet().forEach(consumer);
+            }
+        } finally {
+            attachmentLock.unlock();
+        }
     }
 
     public void setObjectAttachments(Map<String, Object> attachments) {
-        this.attachments = attachments == null ? Collections.synchronizedMap(new HashMap<>()) : attachments;
+        try {
+            attachmentLock.lock();
+            this.attachments = attachments == null ? new HashMap<>() : attachments;
+        } finally {
+            attachmentLock.unlock();
+        }
     }
 
     @Override
@@ -395,12 +432,22 @@ public class RpcInvocation implements Invocation, Serializable {
     @Deprecated
     @Override
     public Map<String, String> getAttachments() {
-        return new AttachmentsAdapter.ObjectToStringMap(attachments);
+        try {
+            attachmentLock.lock();
+            return new AttachmentsAdapter.ObjectToStringMap(attachments);
+        } finally {
+            attachmentLock.unlock();
+        }
     }
 
     @Deprecated
     public void setAttachments(Map<String, String> attachments) {
-        this.attachments = attachments == null ? Collections.synchronizedMap(new HashMap<>()) : new HashMap<>(attachments);
+        try {
+            attachmentLock.lock();
+            this.attachments = attachments == null ? new HashMap<>() : new HashMap<>(attachments);
+        } finally {
+            attachmentLock.unlock();
+        }
     }
 
     @Override
@@ -410,10 +457,15 @@ public class RpcInvocation implements Invocation, Serializable {
 
     @Override
     public void setObjectAttachment(String key, Object value) {
-        if (attachments == null) {
-            attachments = Collections.synchronizedMap(new HashMap<>());
+        try {
+            attachmentLock.lock();
+            if (attachments == null) {
+                attachments = new HashMap<>();
+            }
+            attachments.put(key, value);
+        } finally {
+            attachmentLock.unlock();
         }
-        attachments.put(key, value);
     }
 
     @Override
@@ -428,33 +480,48 @@ public class RpcInvocation implements Invocation, Serializable {
 
     @Override
     public void setObjectAttachmentIfAbsent(String key, Object value) {
-        if (attachments == null) {
-            attachments = Collections.synchronizedMap(new HashMap<>());
-        }
-        if (!attachments.containsKey(key)) {
-            attachments.put(key, value);
+        try {
+            attachmentLock.lock();
+            if (attachments == null) {
+                attachments = new HashMap<>();
+            }
+            if (!attachments.containsKey(key)) {
+                attachments.put(key, value);
+            }
+        } finally {
+            attachmentLock.unlock();
         }
     }
 
     @Deprecated
     public void addAttachments(Map<String, String> attachments) {
-        if (attachments == null) {
-            return;
-        }
-        if (this.attachments == null) {
-            this.attachments = Collections.synchronizedMap(new HashMap<>());
+        try {
+            attachmentLock.lock();
+            if (attachments == null) {
+                return;
+            }
+            if (this.attachments == null) {
+                this.attachments = new HashMap<>();
+            }
+            this.attachments.putAll(attachments);
+        } finally {
+            attachmentLock.unlock();
         }
-        this.attachments.putAll(attachments);
     }
 
     public void addObjectAttachments(Map<String, Object> attachments) {
-        if (attachments == null) {
-            return;
-        }
-        if (this.attachments == null) {
-            this.attachments = Collections.synchronizedMap(new HashMap<>());
+        try {
+            attachmentLock.lock();
+            if (attachments == null) {
+                return;
+            }
+            if (this.attachments == null) {
+                this.attachments = new HashMap<>();
+            }
+            this.attachments.putAll(attachments);
+        } finally {
+            attachmentLock.unlock();
         }
-        this.attachments.putAll(attachments);
     }
 
     @Deprecated
@@ -479,65 +546,90 @@ public class RpcInvocation implements Invocation, Serializable {
     @Override
     @Deprecated
     public String getAttachment(String key) {
-        if (attachments == null) {
+        try {
+            attachmentLock.lock();
+            if (attachments == null) {
+                return null;
+            }
+            Object value = attachments.get(key);
+            if (value instanceof String) {
+                return (String) value;
+            }
             return null;
+        } finally {
+            attachmentLock.unlock();
         }
-        Object value = attachments.get(key);
-        if (value instanceof String) {
-            return (String) value;
-        }
-        return null;
     }
 
     @Override
     public Object getObjectAttachment(String key) {
-        if (attachments == null) {
-            return null;
-        }
-        final Object val = attachments.get(key);
-        if (val != null) {
-            return val;
+        try {
+            attachmentLock.lock();
+            if (attachments == null) {
+                return null;
+            }
+            final Object val = attachments.get(key);
+            if (val != null) {
+                return val;
+            }
+            return attachments.get(key.toLowerCase(Locale.ROOT));
+        } finally {
+            attachmentLock.unlock();
         }
-        return attachments.get(key.toLowerCase(Locale.ROOT));
     }
 
     @Override
     @Deprecated
     public String getAttachment(String key, String defaultValue) {
-        if (attachments == null) {
-            return defaultValue;
-        }
-        Object value = attachments.get(key);
-        if (value instanceof String) {
-            String strValue = (String) value;
-            if (StringUtils.isEmpty(strValue)) {
+        try {
+            attachmentLock.lock();
+            if (attachments == null) {
                 return defaultValue;
-            } else {
-                return strValue;
             }
+            Object value = attachments.get(key);
+            if (value instanceof String) {
+                String strValue = (String) value;
+                if (StringUtils.isEmpty(strValue)) {
+                    return defaultValue;
+                } else {
+                    return strValue;
+                }
+            }
+            return defaultValue;
+        } finally {
+            attachmentLock.unlock();
         }
-        return defaultValue;
     }
 
     @Deprecated
     @Override
     public Object getObjectAttachment(String key, Object defaultValue) {
-        if (attachments == null) {
-            return defaultValue;
-        }
-        Object value = attachments.get(key);
-        if (value == null) {
-            return defaultValue;
+        try {
+            attachmentLock.lock();
+            if (attachments == null) {
+                return defaultValue;
+            }
+            Object value = attachments.get(key);
+            if (value == null) {
+                return defaultValue;
+            }
+            return value;
+        } finally {
+            attachmentLock.unlock();
         }
-        return value;
     }
 
     @Override
     public Object getObjectAttachmentWithoutConvert(String key) {
-        if (attachments == null) {
-            return null;
+        try {
+            attachmentLock.lock();
+            if (attachments == null) {
+                return null;
+            }
+            return attachments.get(key);
+        } finally {
+            attachmentLock.unlock();
         }
-        return attachments.get(key);
     }
 
     public Class<?> getReturnType() {
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
index f1d8d119a3..5596bf30f7 100644
--- 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
@@ -30,8 +30,6 @@ 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;
@@ -92,9 +90,9 @@ public class ProfilerServerFilter implements Filter, BaseFilter.Listener {
         if (((usage / (1000_000L * ProfilerSwitch.getWarnPercent())) > timeout) && timeout != -1) {
 
             StringBuilder attachment = new StringBuilder();
-            for (Map.Entry<String, Object> entry : invocation.getObjectAttachments().entrySet()) {
+            invocation.foreachAttachment((entry) -> {
                 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. Timeout: %dms\n" +
                     "client: %s\n" +
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/TokenFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/TokenFilter.java
index 3e5616b041..056d27e6a1 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/TokenFilter.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/TokenFilter.java
@@ -26,8 +26,6 @@ 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.rpc.Constants.TOKEN_KEY;
 
 /**
@@ -45,8 +43,7 @@ public class TokenFilter implements Filter {
         String token = invoker.getUrl().getParameter(TOKEN_KEY);
         if (ConfigUtils.isNotEmpty(token)) {
             Class<?> serviceType = invoker.getInterface();
-            Map<String, Object> attachments = inv.getObjectAttachments();
-            String remoteToken = (attachments == null ? null : (String) attachments.get(TOKEN_KEY));
+            String remoteToken = (String) inv.getObjectAttachmentWithoutConvert(TOKEN_KEY);
             if (!token.equals(remoteToken)) {
                 throw new RpcException("Invalid token! Forbid invoke remote service " + serviceType + " method " + inv.getMethodName() +
                         "() from consumer " + RpcContext.getServiceContext().getRemoteHost() + " to provider " +
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/TokenHeaderFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/TokenHeaderFilter.java
index 63b00512bc..d72b790a47 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/TokenHeaderFilter.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/TokenHeaderFilter.java
@@ -24,8 +24,6 @@ import org.apache.dubbo.rpc.RpcContext;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.RpcInvocation;
 
-import java.util.Map;
-
 import static org.apache.dubbo.rpc.Constants.TOKEN_KEY;
 import static org.apache.dubbo.rpc.RpcException.FORBIDDEN_EXCEPTION;
 
@@ -36,8 +34,7 @@ public class TokenHeaderFilter implements HeaderFilter {
         String token = invoker.getUrl().getParameter(TOKEN_KEY);
         if (ConfigUtils.isNotEmpty(token)) {
             Class<?> serviceType = invoker.getInterface();
-            Map<String, Object> attachments = invocation.getObjectAttachments();
-            String remoteToken = (attachments == null ? null : (String) attachments.get(TOKEN_KEY));
+            String remoteToken = (String) invocation.getObjectAttachmentWithoutConvert(TOKEN_KEY);
             if (!token.equals(remoteToken)) {
                 throw new RpcException(FORBIDDEN_EXCEPTION, "Forbid invoke remote service " + serviceType + " method " + invocation.getMethodName() +
                     "() from consumer " + RpcContext.getServiceContext().getRemoteHost() + " to provider " +
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 b8c389f289..71247a5523 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
@@ -259,7 +259,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.getObjectAttachment(TIMEOUT_KEY);
+            Object timeout = invocation.getObjectAttachmentWithoutConvert(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/InvocationUtil.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvocationUtil.java
index 2df39f9d3c..9d75590624 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvocationUtil.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvocationUtil.java
@@ -27,8 +27,6 @@ import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.RpcInvocation;
 import org.apache.dubbo.rpc.RpcServiceContext;
 
-import java.util.Map;
-
 import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT;
 import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY;
 
@@ -69,9 +67,9 @@ public class InvocationUtil {
                 long usage = bizProfiler.getEndTime() - bizProfiler.getStartTime();
                 if ((usage / (1000_000L * ProfilerSwitch.getWarnPercent())) > timeout) {
                     StringBuilder attachment = new StringBuilder();
-                    for (Map.Entry<String, Object> entry : rpcInvocation.getObjectAttachments().entrySet()) {
+                    rpcInvocation.foreachAttachment((entry) -> {
                         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. Timeout: %dms\n" + "invocation context:\n%s" + "thread info: \n%s",
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/TokenFilterTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/TokenFilterTest.java
index 1adb7a1727..c31cd67729 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/TokenFilterTest.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/TokenFilterTest.java
@@ -47,10 +47,8 @@ public class TokenFilterTest {
         when(invoker.getUrl()).thenReturn(url);
         when(invoker.invoke(any(Invocation.class))).thenReturn(new AppResponse("result"));
 
-        Map<String, Object> attachments = new HashMap<>();
-        attachments.put(TOKEN_KEY, token);
         Invocation invocation = Mockito.mock(Invocation.class);
-        when(invocation.getObjectAttachments()).thenReturn(attachments);
+        when(invocation.getObjectAttachmentWithoutConvert(TOKEN_KEY)).thenReturn(token);
 
         Result result = tokenFilter.invoke(invoker, invocation);
         Assertions.assertEquals("result", result.getValue());
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/CallbackServiceCodec.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/CallbackServiceCodec.java
index 332dff5cac..38acb87247 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/CallbackServiceCodec.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/CallbackServiceCodec.java
@@ -116,7 +116,7 @@ public class CallbackServiceCodec {
         params.put(IS_SERVER_KEY, Boolean.FALSE.toString());
         // mark it's a callback, for troubleshooting
         params.put(IS_CALLBACK_SERVICE, Boolean.TRUE.toString());
-        String group = (inv == null ? null : (String) inv.getObjectAttachment(GROUP_KEY));
+        String group = (inv == null ? null : (String) inv.getObjectAttachmentWithoutConvert(GROUP_KEY));
         if (group != null && group.length() > 0) {
             params.put(GROUP_KEY, group);
         }
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java
index b63f86cdb8..1f7de2e538 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java
@@ -43,7 +43,6 @@ import org.apache.dubbo.rpc.support.RpcUtils;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -217,12 +216,7 @@ public class DecodeableRpcInvocation extends RpcInvocation implements Codec, Dec
 
             Map<String, Object> map = in.readAttachments();
             if (CollectionUtils.isNotEmptyMap(map)) {
-                Map<String, Object> attachment = getObjectAttachments();
-                if (attachment == null) {
-                    attachment = new HashMap<>(map.size());
-                }
-                attachment.putAll(map);
-                setObjectAttachments(attachment);
+                addObjectAttachments(map);
             }
 
             //decode argument ,may be callback
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java
index 5e01fc42f0..dcff5f4692 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java
@@ -122,7 +122,7 @@ public class DubboProtocol extends AbstractProtocol {
                 Thread.currentThread().setContextClassLoader(invoker.getUrl().getServiceModel().getClassLoader());
             }
             // need to consider backward-compatibility if it's a callback
-            if (Boolean.TRUE.toString().equals(inv.getObjectAttachments().get(IS_CALLBACK_SERVICE_INVOKE))) {
+            if (Boolean.TRUE.toString().equals(inv.getObjectAttachmentWithoutConvert(IS_CALLBACK_SERVICE_INVOKE))) {
                 String methodsStr = invoker.getUrl().getParameters().get("methods");
                 boolean hasMethod = false;
                 if (methodsStr == null || !methodsStr.contains(",")) {
@@ -192,9 +192,9 @@ public class DubboProtocol extends AbstractProtocol {
             } catch (RemotingException e) {
                 String serviceKey = serviceKey(
                     0,
-                    (String) invocation.getObjectAttachments().get(PATH_KEY),
-                    (String) invocation.getObjectAttachments().get(VERSION_KEY),
-                    (String) invocation.getObjectAttachments().get(GROUP_KEY)
+                    (String) invocation.getObjectAttachmentWithoutConvert(PATH_KEY),
+                    (String) invocation.getObjectAttachmentWithoutConvert(VERSION_KEY),
+                    (String) invocation.getObjectAttachmentWithoutConvert(GROUP_KEY)
                 );
                 throw new RemotingException(channel, "The stub service[" + serviceKey + "] is not found, it may not be exported yet");
             }
@@ -260,10 +260,10 @@ public class DubboProtocol extends AbstractProtocol {
         boolean isCallBackServiceInvoke;
         boolean isStubServiceInvoke;
         int port = channel.getLocalAddress().getPort();
-        String path = (String) inv.getObjectAttachments().get(PATH_KEY);
+        String path = (String) inv.getObjectAttachmentWithoutConvert(PATH_KEY);
 
         //if it's stub service on client side(after enable stubevent, usually is set up onconnect or ondisconnect method)
-        isStubServiceInvoke = Boolean.TRUE.toString().equals(inv.getObjectAttachments().get(STUB_EVENT_KEY));
+        isStubServiceInvoke = Boolean.TRUE.toString().equals(inv.getObjectAttachmentWithoutConvert(STUB_EVENT_KEY));
         if (isStubServiceInvoke) {
             //when a stub service export to local, it usually can't be exposed to port
             port = 0;
@@ -272,15 +272,15 @@ public class DubboProtocol extends AbstractProtocol {
         // if it's callback service on client side
         isCallBackServiceInvoke = isClientSide(channel) && !isStubServiceInvoke;
         if (isCallBackServiceInvoke) {
-            path += "." + inv.getObjectAttachments().get(CALLBACK_SERVICE_KEY);
-            inv.getObjectAttachments().put(IS_CALLBACK_SERVICE_INVOKE, Boolean.TRUE.toString());
+            path += "." + inv.getObjectAttachmentWithoutConvert(CALLBACK_SERVICE_KEY);
+            inv.setObjectAttachment(IS_CALLBACK_SERVICE_INVOKE, Boolean.TRUE.toString());
         }
 
         String serviceKey = serviceKey(
                 port,
                 path,
-                (String) inv.getObjectAttachments().get(VERSION_KEY),
-                (String) inv.getObjectAttachments().get(GROUP_KEY)
+                (String) inv.getObjectAttachmentWithoutConvert(VERSION_KEY),
+                (String) inv.getObjectAttachmentWithoutConvert(GROUP_KEY)
         );
         DubboExporter<?> exporter = (DubboExporter<?>) exporterMap.get(serviceKey);
 
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCountCodecTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCountCodecTest.java
index 6ac3acffe6..b531bb1aa0 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCountCodecTest.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCountCodecTest.java
@@ -61,7 +61,7 @@ public class DubboCountCodecTest {
         }
 
         MultiMessage multiMessage = (MultiMessage) dubboCountCodec.decode(channel, buffer);
-        Assertions.assertEquals(multiMessage.size(), 18);
+        Assertions.assertEquals(multiMessage.size(), 20);
         int requestCount = 0;
         int responseCount = 0;
         Iterator iterator = multiMessage.iterator();
@@ -78,7 +78,7 @@ public class DubboCountCodecTest {
             }
         }
         Assertions.assertEquals(requestCount, 10);
-        Assertions.assertEquals(responseCount, 8);
+        Assertions.assertEquals(responseCount, 10);
     }
 
 }
diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmInvoker.java b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmInvoker.java
index 1dcc98806d..719e036d84 100644
--- a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmInvoker.java
+++ b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmInvoker.java
@@ -203,7 +203,7 @@ public class InjvmInvoker<T> extends AbstractInvoker<T> {
             // generic invoke, skip copy arguments
             RpcInvocation copiedInvocation = new RpcInvocation(invocation.getTargetServiceUniqueName(),
                 providerServiceModel, methodName, invocation.getServiceName(), invocation.getProtocolServiceKey(),
-                invocation.getParameterTypes(), invocation.getArguments(), new HashMap<>(invocation.getObjectAttachments()),
+                invocation.getParameterTypes(), invocation.getArguments(), invocation.copyObjectAttachments(),
                 invocation.getInvoker(), new HashMap<>(),
                 invocation instanceof RpcInvocation ? ((RpcInvocation) invocation).getInvokeMode() : null);
             copiedInvocation.setInvoker(invoker);
@@ -233,7 +233,7 @@ public class InjvmInvoker<T> extends AbstractInvoker<T> {
 
                 RpcInvocation copiedInvocation = new RpcInvocation(invocation.getTargetServiceUniqueName(),
                     providerServiceModel, methodName, invocation.getServiceName(), invocation.getProtocolServiceKey(),
-                    pts, realArgument, new HashMap<>(invocation.getObjectAttachments()),
+                    pts, realArgument, invocation.copyObjectAttachments(),
                     invocation.getInvoker(), new HashMap<>(),
                     invocation instanceof RpcInvocation ? ((RpcInvocation) invocation).getInvokeMode() : null);
                 copiedInvocation.setInvoker(invoker);
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 ef0afe55a9..6146fc6232 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
@@ -51,7 +51,6 @@ import org.apache.dubbo.rpc.support.RpcUtils;
 import io.netty.util.AsciiString;
 
 import java.util.Arrays;
-import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.CompletableFuture;
@@ -260,16 +259,12 @@ public class TripleInvoker<T> extends AbstractInvoker<T> {
         if (timeout != null) {
             meta.timeout = timeout + "m";
         }
-        final Map<String, Object> objectAttachments = invocation.getObjectAttachments();
-        if (objectAttachments != null) {
-            String application = (String) objectAttachments.get(CommonConstants.APPLICATION_KEY);
-            if (application == null) {
-                application = (String) objectAttachments.get(
-                    CommonConstants.REMOTE_APPLICATION_KEY);
-            }
-            meta.application = application;
-            meta.attachments = objectAttachments;
+        String application = (String) invocation.getObjectAttachmentWithoutConvert(CommonConstants.APPLICATION_KEY);
+        if (application == null) {
+            application = (String) invocation.getObjectAttachmentWithoutConvert(CommonConstants.REMOTE_APPLICATION_KEY);
         }
+        meta.application = application;
+        meta.attachments = invocation.getObjectAttachments();
         return meta;
     }