diff --git a/protocol/invocation.go b/protocol/invocation.go
index f32f2c3449ac063ecb89952bd4653312a07a3df4..eedf5f0253c2b76a3e0e1b52a00124d648351cfc 100644
--- a/protocol/invocation.go
+++ b/protocol/invocation.go
@@ -30,5 +30,8 @@ type Invocation interface {
 	Reply() interface{}
 	Attachments() map[string]string
 	AttachmentsByKey(string, string) string
+	// Refer to dubbo 2.7.6.  It is different from attachment. It is used in internal process.
+	Attributes() map[string]interface{}
+	AttributeByKey(string, interface{}) interface{}
 	Invoker() Invoker
 }
diff --git a/protocol/invocation/rpcinvocation.go b/protocol/invocation/rpcinvocation.go
index b207fd0b0cc4eb7de8409a8c46c6fc9ef0baa5c7..10c0efe9cdb6194061fcecfb24c4bf5130dc7fec 100644
--- a/protocol/invocation/rpcinvocation.go
+++ b/protocol/invocation/rpcinvocation.go
@@ -40,8 +40,10 @@ type RPCInvocation struct {
 	reply           interface{}
 	callBack        interface{}
 	attachments     map[string]string
-	invoker         protocol.Invoker
-	lock            sync.RWMutex
+	// Refer to dubbo 2.7.6.  It is different from attachment. It is used in internal process.
+	attributes map[string]interface{}
+	invoker    protocol.Invoker
+	lock       sync.RWMutex
 }
 
 // NewRPCInvocation ...
@@ -111,6 +113,25 @@ func (r *RPCInvocation) AttachmentsByKey(key string, defaultValue string) string
 	return defaultValue
 }
 
+// get attributes
+func (r *RPCInvocation) Attributes() map[string]interface{} {
+	return r.attributes
+}
+
+// get attribute by key. If it is not exist, it will return default value
+func (r *RPCInvocation) AttributeByKey(key string, defaultValue interface{}) interface{} {
+	r.lock.RLock()
+	defer r.lock.RUnlock()
+	if r.attributes == nil {
+		return defaultValue
+	}
+	value, ok := r.attributes[key]
+	if ok {
+		return value
+	}
+	return defaultValue
+}
+
 // SetAttachments ...
 func (r *RPCInvocation) SetAttachments(key string, value string) {
 	r.lock.Lock()