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/25 02:06:28 UTC

[dubbo-go] branch 3.0 updated: test(loadbalance): add unittests for p2c (#1686)

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

alexstocks pushed a commit to branch 3.0
in repository https://gitbox.apache.org/repos/asf/dubbo-go.git


The following commit(s) were added to refs/heads/3.0 by this push:
     new 6710138  test(loadbalance): add unittests for p2c (#1686)
6710138 is described below

commit 671013837b353698c5ef42289265bc270634a265
Author: Xuewei Niu <ju...@apache.org>
AuthorDate: Sat Dec 25 10:06:23 2021 +0800

    test(loadbalance): add unittests for p2c (#1686)
    
    * test(loadbalance): add unittests for p2c
    
    * fix(loadbalance): fix fixed rand seed problem
    
    * style: format  imports
    
    * style: add apache license
---
 cluster/cluster/adaptivesvc/cluster_invoker.go     |   6 +-
 cluster/cluster/available/cluster_invoker.go       |   6 +-
 cluster/cluster/available/cluster_invoker_test.go  |   2 +-
 cluster/loadbalance/p2c/loadbalance.go             |  26 +--
 cluster/loadbalance/p2c/loadbalance_test.go        | 178 +++++++++++++++++++++
 cluster/metrics/local_metrics.go                   |   2 +
 cluster/metrics/mock_metrics.go                    | 144 +++++++++++++++++
 filter/seata/filter_test.go                        |   2 +-
 filter/sentinel/filter.go                          |   2 +-
 .../local/metadata_service_proxy_factory_test.go   |   2 +-
 protocol/invocation/rpcinvocation.go               |   2 +
 protocol/result.go                                 |  12 +-
 12 files changed, 352 insertions(+), 32 deletions(-)

diff --git a/cluster/cluster/adaptivesvc/cluster_invoker.go b/cluster/cluster/adaptivesvc/cluster_invoker.go
index 1fb65e2..30f0772 100644
--- a/cluster/cluster/adaptivesvc/cluster_invoker.go
+++ b/cluster/cluster/adaptivesvc/cluster_invoker.go
@@ -49,13 +49,13 @@ func newAdaptiveServiceClusterInvoker(directory directory.Directory) protocol.In
 func (ivk *adaptiveServiceClusterInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result {
 	invokers := ivk.Directory.List(invocation)
 	if err := ivk.CheckInvokers(invokers, invocation); err != nil {
-		return protocol.NewRPCResult(nil, err)
+		return &protocol.RPCResult{Err: err}
 	}
 
 	// get loadBalance
 	lbKey := invokers[0].GetURL().GetParam(constant.LoadbalanceKey, constant.LoadBalanceKeyP2C)
 	if lbKey != constant.LoadBalanceKeyP2C {
-		return protocol.NewRPCResult(nil, perrors.Errorf("adaptive service not supports %s load balance", lbKey))
+		return &protocol.RPCResult{Err: perrors.Errorf("adaptive service not supports %s load balance", lbKey)}
 	}
 	lb := extension.GetLoadbalance(lbKey)
 
@@ -78,7 +78,7 @@ func (ivk *adaptiveServiceClusterInvoker) Invoke(ctx context.Context, invocation
 		invocation.MethodName(), metrics.HillClimbing, uint64(remaining))
 	if err != nil {
 		logger.Warnf("adaptive service metrics update is failed, err: %v", err)
-		return protocol.NewRPCResult(nil, err)
+		return &protocol.RPCResult{Err: err}
 	}
 
 	return result
diff --git a/cluster/cluster/available/cluster_invoker.go b/cluster/cluster/available/cluster_invoker.go
index 7bf8375..4facd66 100644
--- a/cluster/cluster/available/cluster_invoker.go
+++ b/cluster/cluster/available/cluster_invoker.go
@@ -47,12 +47,12 @@ func (invoker *availableClusterInvoker) Invoke(ctx context.Context, invocation p
 	invokers := invoker.Directory.List(invocation)
 	err := invoker.CheckInvokers(invokers, invocation)
 	if err != nil {
-		return protocol.NewRPCResult(nil, err)
+		return &protocol.RPCResult{Err: err}
 	}
 
 	err = invoker.CheckWhetherDestroyed()
 	if err != nil {
-		return protocol.NewRPCResult(nil, err)
+		return &protocol.RPCResult{Err: err}
 	}
 
 	for _, ivk := range invokers {
@@ -60,5 +60,5 @@ func (invoker *availableClusterInvoker) Invoke(ctx context.Context, invocation p
 			return ivk.Invoke(ctx, invocation)
 		}
 	}
-	return protocol.NewRPCResult(nil, errors.New(fmt.Sprintf("no provider available in %v", invokers)))
+	return &protocol.RPCResult{Err: errors.New(fmt.Sprintf("no provider available in %v", invokers))}
 }
diff --git a/cluster/cluster/available/cluster_invoker_test.go b/cluster/cluster/available/cluster_invoker_test.go
index 730474b..70919e6 100644
--- a/cluster/cluster/available/cluster_invoker_test.go
+++ b/cluster/cluster/available/cluster_invoker_test.go
@@ -65,7 +65,7 @@ func TestAvailableClusterInvokerSuccess(t *testing.T) {
 	invoker := mock.NewMockInvoker(ctrl)
 	clusterInvoker := registerAvailable(invoker)
 
-	mockResult := protocol.NewRPCResult(clusterpkg.Rest{Tried: 0, Success: true}, nil)
+	mockResult := &protocol.RPCResult{Rest: clusterpkg.Rest{Tried: 0, Success: true}}
 	invoker.EXPECT().IsAvailable().Return(true)
 	invoker.EXPECT().Invoke(gomock.Any()).Return(mockResult)
 
diff --git a/cluster/loadbalance/p2c/loadbalance.go b/cluster/loadbalance/p2c/loadbalance.go
index 2adeb61..f673cbb 100644
--- a/cluster/loadbalance/p2c/loadbalance.go
+++ b/cluster/loadbalance/p2c/loadbalance.go
@@ -34,6 +34,12 @@ import (
 	"dubbo.apache.org/dubbo-go/v3/protocol"
 )
 
+var (
+	randSeed = func() int64 {
+		return time.Now().Unix()
+	}
+)
+
 func init() {
 	extension.SetLoadbalance(constant.LoadBalanceKeyP2C, newP2CLoadBalance)
 }
@@ -69,25 +75,23 @@ func (l *p2cLoadBalance) Select(invokers []protocol.Invoker, invocation protocol
 	if len(invokers) == 2 {
 		i, j = 0, 1
 	} else {
-		rand.Seed(time.Now().Unix())
+		rand.Seed(randSeed())
 		i = rand.Intn(len(invokers))
 		j = i
 		for i == j {
 			j = rand.Intn(len(invokers))
 		}
 	}
-	logger.Debugf("[P2C select] Two invokers were selected, i: %d, j: %d, invoker[i]: %s, invoker[j]: %s.",
-		i, j, invokers[i], invokers[j])
+	logger.Debugf("[P2C select] Two invokers were selected, invoker[%d]: %s, invoker[%d]: %s.",
+		i, invokers[i], j, invokers[j])
 
-	// TODO(justxuewei): please consider how to get the real method name from $invoke,
-	// 	see also [#1511](https://github.com/apache/dubbo-go/issues/1511)
-	methodName := invocation.MethodName()
+	methodName := invocation.ActualMethodName()
 	// remainingIIface, remainingJIface means remaining capacity of node i and node j.
 	// If one of the metrics is empty, invoke the invocation to that node directly.
 	remainingIIface, err := m.GetMethodMetrics(invokers[i].GetURL(), methodName, metrics.HillClimbing)
 	if err != nil {
 		if errors.Is(err, metrics.ErrMetricsNotFound) {
-			logger.Debugf("[P2C select] The invoker[i] was selected, because it hasn't been selected before.")
+			logger.Debugf("[P2C select] The invoker[%d] was selected, because it hasn't been selected before.", i)
 			return invokers[i]
 		}
 		logger.Warnf("get method metrics err: %v", err)
@@ -97,7 +101,7 @@ func (l *p2cLoadBalance) Select(invokers []protocol.Invoker, invocation protocol
 	remainingJIface, err := m.GetMethodMetrics(invokers[j].GetURL(), methodName, metrics.HillClimbing)
 	if err != nil {
 		if errors.Is(err, metrics.ErrMetricsNotFound) {
-			logger.Debugf("[P2C select] The invoker[j] was selected, because it hasn't been selected before.")
+			logger.Debugf("[P2C select] The invoker[%d] was selected, because it hasn't been selected before.", j)
 			return invokers[j]
 		}
 		logger.Warnf("get method metrics err: %v", err)
@@ -116,14 +120,14 @@ func (l *p2cLoadBalance) Select(invokers []protocol.Invoker, invocation protocol
 		panic(fmt.Sprintf("the type of %s expects to be uint64, but gets %T", metrics.HillClimbing, remainingJIface))
 	}
 
-	logger.Debugf("[P2C select] The invoker[i] remaining is %d, and the invoker[j] is %d.", remainingI, remainingJ)
+	logger.Debugf("[P2C select] The invoker[%d] remaining is %d, and the invoker[%d] is %d.", i, remainingI, j, remainingJ)
 
 	// For the remaining capacity, the bigger, the better.
 	if remainingI > remainingJ {
-		logger.Debugf("[P2C select] The invoker[i] was selected.")
+		logger.Debugf("[P2C select] The invoker[%d] was selected.", i)
 		return invokers[i]
 	}
 
-	logger.Debugf("[P2C select] The invoker[j] was selected.")
+	logger.Debugf("[P2C select] The invoker[%d] was selected.", j)
 	return invokers[j]
 }
diff --git a/cluster/loadbalance/p2c/loadbalance_test.go b/cluster/loadbalance/p2c/loadbalance_test.go
new file mode 100644
index 0000000..17092fb
--- /dev/null
+++ b/cluster/loadbalance/p2c/loadbalance_test.go
@@ -0,0 +1,178 @@
+/*
+ * 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 p2c
+
+import (
+	"testing"
+)
+
+import (
+	"github.com/golang/mock/gomock"
+
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"dubbo.apache.org/dubbo-go/v3/cluster/metrics"
+	"dubbo.apache.org/dubbo-go/v3/common"
+	"dubbo.apache.org/dubbo-go/v3/protocol"
+	protoinvoc "dubbo.apache.org/dubbo-go/v3/protocol/invocation"
+)
+
+func TestLoadBalance(t *testing.T) {
+	lb := newP2CLoadBalance()
+	invocation := protoinvoc.NewRPCInvocation("TestMethod", []interface{}{}, nil)
+	randSeed = func() int64 {
+		return 0
+	}
+
+	t.Run("no invokers", func(t *testing.T) {
+		ivk := lb.Select([]protocol.Invoker{}, invocation)
+		assert.Nil(t, ivk)
+	})
+
+	t.Run("one invoker", func(t *testing.T) {
+		url0, _ := common.NewURL("dubbo://192.168.1.0:20000/com.ikurento.user.UserProvider")
+
+		ivkArr := []protocol.Invoker{
+			protocol.NewBaseInvoker(url0),
+		}
+		ivk := lb.Select(ivkArr, invocation)
+		assert.Equal(t, ivkArr[0].GetURL().String(), ivk.GetURL().String())
+	})
+
+	t.Run("two invokers", func(t *testing.T) {
+		ctrl := gomock.NewController(t)
+		defer ctrl.Finish()
+
+		m := metrics.NewMockMetrics(ctrl)
+		metrics.LocalMetrics = m
+
+		url0, _ := common.NewURL("dubbo://192.168.1.0:20000/com.ikurento.user.UserProvider")
+		url1, _ := common.NewURL("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider")
+
+		m.EXPECT().
+			GetMethodMetrics(gomock.Eq(url0), gomock.Eq(invocation.MethodName()), gomock.Eq(metrics.HillClimbing)).
+			Times(1).
+			Return(uint64(10), nil)
+		m.EXPECT().
+			GetMethodMetrics(gomock.Eq(url1), gomock.Eq(invocation.MethodName()), gomock.Eq(metrics.HillClimbing)).
+			Times(1).
+			Return(uint64(5), nil)
+
+		ivkArr := []protocol.Invoker{
+			protocol.NewBaseInvoker(url0),
+			protocol.NewBaseInvoker(url1),
+		}
+
+		ivk := lb.Select(ivkArr, invocation)
+
+		assert.Equal(t, ivkArr[0].GetURL().String(), ivk.GetURL().String())
+	})
+
+	t.Run("multiple invokers", func(t *testing.T) {
+		ctrl := gomock.NewController(t)
+		defer ctrl.Finish()
+
+		m := metrics.NewMockMetrics(ctrl)
+		metrics.LocalMetrics = m
+
+		url0, _ := common.NewURL("dubbo://192.168.1.0:20000/com.ikurento.user.UserProvider")
+		url1, _ := common.NewURL("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider")
+		url2, _ := common.NewURL("dubbo://192.168.1.2:20000/com.ikurento.user.UserProvider")
+
+		m.EXPECT().
+			GetMethodMetrics(gomock.Eq(url0), gomock.Eq(invocation.MethodName()), gomock.Eq(metrics.HillClimbing)).
+			Times(1).
+			Return(uint64(10), nil)
+		m.EXPECT().
+			GetMethodMetrics(gomock.Eq(url1), gomock.Eq(invocation.MethodName()), gomock.Eq(metrics.HillClimbing)).
+			Times(1).
+			Return(uint64(5), nil)
+
+		ivkArr := []protocol.Invoker{
+			protocol.NewBaseInvoker(url0),
+			protocol.NewBaseInvoker(url1),
+			protocol.NewBaseInvoker(url2),
+		}
+
+		ivk := lb.Select(ivkArr, invocation)
+
+		assert.Equal(t, ivkArr[0].GetURL().String(), ivk.GetURL().String())
+	})
+
+	t.Run("metrics i not found", func(t *testing.T) {
+		ctrl := gomock.NewController(t)
+		defer ctrl.Finish()
+
+		m := metrics.NewMockMetrics(ctrl)
+		metrics.LocalMetrics = m
+
+		url0, _ := common.NewURL("dubbo://192.168.1.0:20000/com.ikurento.user.UserProvider")
+		url1, _ := common.NewURL("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider")
+		url2, _ := common.NewURL("dubbo://192.168.1.2:20000/com.ikurento.user.UserProvider")
+
+		m.EXPECT().
+			GetMethodMetrics(gomock.Eq(url0), gomock.Eq(invocation.MethodName()), gomock.Eq(metrics.HillClimbing)).
+			Times(1).
+			Return(0, metrics.ErrMetricsNotFound)
+
+		ivkArr := []protocol.Invoker{
+			protocol.NewBaseInvoker(url0),
+			protocol.NewBaseInvoker(url1),
+			protocol.NewBaseInvoker(url2),
+		}
+
+		ivk := lb.Select(ivkArr, invocation)
+
+		assert.Equal(t, ivkArr[0].GetURL().String(), ivk.GetURL().String())
+	})
+
+	t.Run("metrics j not found", func(t *testing.T) {
+		ctrl := gomock.NewController(t)
+		defer ctrl.Finish()
+
+		m := metrics.NewMockMetrics(ctrl)
+		metrics.LocalMetrics = m
+
+		url0, _ := common.NewURL("dubbo://192.168.1.0:20000/com.ikurento.user.UserProvider")
+		url1, _ := common.NewURL("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider")
+		url2, _ := common.NewURL("dubbo://192.168.1.2:20000/com.ikurento.user.UserProvider")
+
+		m.EXPECT().
+			GetMethodMetrics(gomock.Eq(url0), gomock.Eq(invocation.MethodName()), gomock.Eq(metrics.HillClimbing)).
+			Times(1).
+			Return(uint64(0), nil)
+
+		m.EXPECT().
+			GetMethodMetrics(gomock.Eq(url1), gomock.Eq(invocation.MethodName()), gomock.Eq(metrics.HillClimbing)).
+			Times(1).
+			Return(uint64(0), metrics.ErrMetricsNotFound)
+
+		ivkArr := []protocol.Invoker{
+			protocol.NewBaseInvoker(url0),
+			protocol.NewBaseInvoker(url1),
+			protocol.NewBaseInvoker(url2),
+		}
+
+		ivk := lb.Select(ivkArr, invocation)
+
+		assert.Equal(t, ivkArr[1].GetURL().String(), ivk.GetURL().String())
+	})
+
+}
diff --git a/cluster/metrics/local_metrics.go b/cluster/metrics/local_metrics.go
index e9f976a..16c094c 100644
--- a/cluster/metrics/local_metrics.go
+++ b/cluster/metrics/local_metrics.go
@@ -32,6 +32,8 @@ func init() {
 	LocalMetrics = newLocalMetrics()
 }
 
+var _ Metrics = (*localMetrics)(nil)
+
 type localMetrics struct {
 	// protect metrics
 	lock    *sync.RWMutex
diff --git a/cluster/metrics/mock_metrics.go b/cluster/metrics/mock_metrics.go
new file mode 100644
index 0000000..dbfb895
--- /dev/null
+++ b/cluster/metrics/mock_metrics.go
@@ -0,0 +1,144 @@
+/*
+ * 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.
+ */
+
+// Code generated by MockGen. DO NOT EDIT.
+// Source: metrics.go
+
+// Package mock_metrics is a generated GoMock package.
+package metrics
+
+import (
+	reflect "reflect"
+)
+
+import (
+	gomock "github.com/golang/mock/gomock"
+)
+
+import (
+	common "dubbo.apache.org/dubbo-go/v3/common"
+)
+
+// MockMetrics is a mock of Metrics interface.
+type MockMetrics struct {
+	ctrl     *gomock.Controller
+	recorder *MockMetricsMockRecorder
+}
+
+// MockMetricsMockRecorder is the mock recorder for MockMetrics.
+type MockMetricsMockRecorder struct {
+	mock *MockMetrics
+}
+
+// NewMockMetrics creates a new mock instance.
+func NewMockMetrics(ctrl *gomock.Controller) *MockMetrics {
+	mock := &MockMetrics{ctrl: ctrl}
+	mock.recorder = &MockMetricsMockRecorder{mock}
+	return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockMetrics) EXPECT() *MockMetricsMockRecorder {
+	return m.recorder
+}
+
+// GetInstanceMetrics mocks base method.
+func (m *MockMetrics) GetInstanceMetrics(url *common.URL, key string) (interface{}, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetInstanceMetrics", url, key)
+	ret0, _ := ret[0].(interface{})
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// GetInstanceMetrics indicates an expected call of GetInstanceMetrics.
+func (mr *MockMetricsMockRecorder) GetInstanceMetrics(url, key interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInstanceMetrics", reflect.TypeOf((*MockMetrics)(nil).GetInstanceMetrics), url, key)
+}
+
+// GetInvokerMetrics mocks base method.
+func (m *MockMetrics) GetInvokerMetrics(url *common.URL, key string) (interface{}, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetInvokerMetrics", url, key)
+	ret0, _ := ret[0].(interface{})
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// GetInvokerMetrics indicates an expected call of GetInvokerMetrics.
+func (mr *MockMetricsMockRecorder) GetInvokerMetrics(url, key interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInvokerMetrics", reflect.TypeOf((*MockMetrics)(nil).GetInvokerMetrics), url, key)
+}
+
+// GetMethodMetrics mocks base method.
+func (m *MockMetrics) GetMethodMetrics(url *common.URL, methodName, key string) (interface{}, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetMethodMetrics", url, methodName, key)
+	ret0, _ := ret[0].(interface{})
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// GetMethodMetrics indicates an expected call of GetMethodMetrics.
+func (mr *MockMetricsMockRecorder) GetMethodMetrics(url, methodName, key interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMethodMetrics", reflect.TypeOf((*MockMetrics)(nil).GetMethodMetrics), url, methodName, key)
+}
+
+// SetInstanceMetrics mocks base method.
+func (m *MockMetrics) SetInstanceMetrics(url *common.URL, key string, value interface{}) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "SetInstanceMetrics", url, key, value)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// SetInstanceMetrics indicates an expected call of SetInstanceMetrics.
+func (mr *MockMetricsMockRecorder) SetInstanceMetrics(url, key, value interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetInstanceMetrics", reflect.TypeOf((*MockMetrics)(nil).SetInstanceMetrics), url, key, value)
+}
+
+// SetInvokerMetrics mocks base method.
+func (m *MockMetrics) SetInvokerMetrics(url *common.URL, key string, value interface{}) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "SetInvokerMetrics", url, key, value)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// SetInvokerMetrics indicates an expected call of SetInvokerMetrics.
+func (mr *MockMetricsMockRecorder) SetInvokerMetrics(url, key, value interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetInvokerMetrics", reflect.TypeOf((*MockMetrics)(nil).SetInvokerMetrics), url, key, value)
+}
+
+// SetMethodMetrics mocks base method.
+func (m *MockMetrics) SetMethodMetrics(url *common.URL, methodName, key string, value interface{}) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "SetMethodMetrics", url, methodName, key, value)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// SetMethodMetrics indicates an expected call of SetMethodMetrics.
+func (mr *MockMetricsMockRecorder) SetMethodMetrics(url, methodName, key, value interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetMethodMetrics", reflect.TypeOf((*MockMetrics)(nil).SetMethodMetrics), url, methodName, key, value)
+}
diff --git a/filter/seata/filter_test.go b/filter/seata/filter_test.go
index 4db50b0..cdbf0a0 100644
--- a/filter/seata/filter_test.go
+++ b/filter/seata/filter_test.go
@@ -43,7 +43,7 @@ func (iv *testMockSeataInvoker) Invoke(ctx context.Context, _ protocol.Invocatio
 			return &protocol.RPCResult{Rest: xid}
 		}
 	}
-	return protocol.NewRPCResult(nil, nil)
+	return &protocol.RPCResult{}
 }
 
 func TestSeataFilter_Invoke(t *testing.T) {
diff --git a/filter/sentinel/filter.go b/filter/sentinel/filter.go
index 4b08cfa..161e2fd 100644
--- a/filter/sentinel/filter.go
+++ b/filter/sentinel/filter.go
@@ -212,7 +212,7 @@ func SetDubboProviderFallback(f DubboFallback) {
 
 func getDefaultDubboFallback() DubboFallback {
 	return func(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation, blockError *base.BlockError) protocol.Result {
-		return protocol.NewRPCResult(nil, blockError)
+		return &protocol.RPCResult{Err: blockError}
 	}
 }
 
diff --git a/metadata/service/local/metadata_service_proxy_factory_test.go b/metadata/service/local/metadata_service_proxy_factory_test.go
index f38d63c..98aafce 100644
--- a/metadata/service/local/metadata_service_proxy_factory_test.go
+++ b/metadata/service/local/metadata_service_proxy_factory_test.go
@@ -91,5 +91,5 @@ func (m *mockInvoker) Destroy() {
 }
 
 func (m *mockInvoker) Invoke(context.Context, protocol.Invocation) protocol.Result {
-	return protocol.NewRPCResult([]string{"dubbo://localhost"}, nil)
+	return &protocol.RPCResult{Rest: []string{"dubbo://localhost"}}
 }
diff --git a/protocol/invocation/rpcinvocation.go b/protocol/invocation/rpcinvocation.go
index a70da17..aa1f0d9 100644
--- a/protocol/invocation/rpcinvocation.go
+++ b/protocol/invocation/rpcinvocation.go
@@ -29,6 +29,8 @@ import (
 	"dubbo.apache.org/dubbo-go/v3/protocol"
 )
 
+var _ protocol.Invocation = (*RPCInvocation)(nil)
+
 // todo: is it necessary to separate fields of consumer(provider) from RPCInvocation
 // nolint
 type RPCInvocation struct {
diff --git a/protocol/result.go b/protocol/result.go
index 34de34c..a2169a1 100644
--- a/protocol/result.go
+++ b/protocol/result.go
@@ -42,9 +42,7 @@ type Result interface {
 	Attachment(string, interface{}) interface{}
 }
 
-/////////////////////////////
-// Result Implement of RPC
-/////////////////////////////
+var _ Result = (*RPCResult)(nil)
 
 // RPCResult is default RPC result.
 type RPCResult struct {
@@ -53,14 +51,6 @@ type RPCResult struct {
 	Rest  interface{}
 }
 
-func NewRPCResult(result interface{}, err error) *RPCResult {
-	return &RPCResult{
-		Rest:  result,
-		Err:   err,
-		Attrs: make(map[string]interface{}),
-	}
-}
-
 // SetError sets error.
 func (r *RPCResult) SetError(err error) {
 	r.Err = err