From 71649f72b623766ac18d2926c633371dd83e9aa8 Mon Sep 17 00:00:00 2001 From: wuhua3 Date: Wed, 9 Oct 2024 15:48:01 +0800 Subject: [PATCH] fix meta protocol support fix unit test unit test unit test unit test --- endpoint/endpoint.go | 12 ++ lb/weightRoundRobinLb_test.go | 4 +- meta/meta.go | 33 +++-- meta/meta_test.go | 236 ++++++++++++++++++++++++++++++++++ provider/metaProvider.go | 25 +++- provider/metaProvider_test.go | 54 ++++++++ 6 files changed, 349 insertions(+), 15 deletions(-) create mode 100644 meta/meta_test.go create mode 100644 provider/metaProvider_test.go diff --git a/endpoint/endpoint.go b/endpoint/endpoint.go index 45ce1c23..98e4f9d7 100644 --- a/endpoint/endpoint.go +++ b/endpoint/endpoint.go @@ -64,6 +64,7 @@ func GenerateRequestID() uint64 { type MockEndpoint struct { URL *motan.URL MockResponse motan.Response + available atomic.Value } func (m *MockEndpoint) GetRuntimeInfo() map[string]interface{} { @@ -85,9 +86,20 @@ func (m *MockEndpoint) SetURL(url *motan.URL) { } func (m *MockEndpoint) IsAvailable() bool { + if m.available.Load() == nil { + return true + } + b, ok := m.available.Load().(bool) + if ok { + return b + } return true } +func (m *MockEndpoint) SetAvailable(available bool) { + m.available.Store(available) +} + func (m *MockEndpoint) SetProxy(proxy bool) {} func (m *MockEndpoint) SetSerialization(s motan.Serialization) {} diff --git a/lb/weightRoundRobinLb_test.go b/lb/weightRoundRobinLb_test.go index 64093acb..370563d4 100644 --- a/lb/weightRoundRobinLb_test.go +++ b/lb/weightRoundRobinLb_test.go @@ -278,8 +278,8 @@ func processCheck(t *testing.T, lb *WeightRoundRobinLB, typ string, eps []core.E for i := 0; i < int(totalWeight)*round; i++ { lb.Select(nil).Call(nil) } - var maxDelta float64 = 0.0 - var totalDelta float64 = 0.0 + var maxDelta = 0.0 + var totalDelta = 0.0 unavailableCount := 0 for _, ep := range eps { if !ep.IsAvailable() { diff --git a/meta/meta.go b/meta/meta.go index 175cc640..bcab4191 100644 --- a/meta/meta.go +++ b/meta/meta.go @@ -2,16 +2,18 @@ package meta import ( "errors" - "github.com/patrickmn/go-cache" - "github.com/weibocom/motan-go/core" - "github.com/weibocom/motan-go/endpoint" - vlog "github.com/weibocom/motan-go/log" - mpro "github.com/weibocom/motan-go/protocol" "os" "strconv" "strings" "sync" "time" + + "github.com/patrickmn/go-cache" + "github.com/weibocom/motan-go/core" + "github.com/weibocom/motan-go/endpoint" + vlog "github.com/weibocom/motan-go/log" + mpro "github.com/weibocom/motan-go/protocol" + "github.com/weibocom/motan-go/serialize" ) const ( @@ -33,7 +35,7 @@ var ( "grpc-pb-json": true, } supportProtocols = map[string]bool{ - "motan": true, + "motan": false, "motan2": true, } once = sync.Once{} @@ -147,12 +149,17 @@ func getRemoteDynamicMeta(cacheKey string, endpoint core.EndPoint) (map[string]s } return nil, errors.New(resp.GetException().ErrMsg) } - reply := make(map[string]string) - err := resp.ProcessDeserializable(&reply) - if err != nil { - return nil, err + if d, ok := resp.GetValue().(*core.DeserializableValue); ok { + reply := make(map[string]string) + // only support breeze serializer and motan2 protocol + breezeSerialize := &serialize.BreezeSerialization{} + _, err := breezeSerialize.DeSerialize(d.Body, &reply) + if err != nil { + return nil, err + } + return reply, nil } - // multiple serialization might encode empty map into interface{}, not map[string]string + // multiple serialization might encode an empty map into interface{}, not map[string]string // in this case, return a public empty string map if res, ok := resp.GetValue().(map[string]string); ok && res != nil { return res, nil @@ -167,6 +174,10 @@ func getMetaServiceRequest() core.Request { Method: MetaMethodName, Attachment: core.NewStringMap(core.DefaultAttachmentSize), Arguments: []interface{}{}, + RPCContext: &core.RPCContext{ + Serialized: true, + SerializeNum: serialize.BreezeNumber, + }, } req.SetAttachment(mpro.MFrameworkService, "y") return req diff --git a/meta/meta_test.go b/meta/meta_test.go new file mode 100644 index 00000000..93733e63 --- /dev/null +++ b/meta/meta_test.go @@ -0,0 +1,236 @@ +package meta + +import ( + "errors" + "github.com/weibocom/motan-go/endpoint" + "github.com/weibocom/motan-go/serialize" + "testing" + + "github.com/weibocom/motan-go/core" +) + +func TestIsSupport(t *testing.T) { + tests := []struct { + name string + cacheKey string + url *core.URL + want bool + }{ + { + name: "Supported Protocol and Serializer", + cacheKey: "test1", + url: &core.URL{ + Protocol: "motan2", + Parameters: map[string]string{ + core.DynamicMetaKey: "true", + core.SerializationKey: "breeze", + }, + }, + want: true, + }, + { + name: "Unsupported Protocol", + cacheKey: "test2", + url: &core.URL{ + Protocol: "motan", + Parameters: map[string]string{ + core.DynamicMetaKey: "true", + core.SerializationKey: "breeze", + }, + }, + want: false, + }, + { + name: "Unsupported Serializer", + cacheKey: "test3", + url: &core.URL{ + Protocol: "motan2", + Parameters: map[string]string{ + core.DynamicMetaKey: "true", + core.SerializationKey: "protobuf", + }, + }, + want: false, + }, + { + name: "Dynamic Meta Disabled", + cacheKey: "test4", + url: &core.URL{ + Protocol: "motan2", + Parameters: map[string]string{ + core.DynamicMetaKey: "false", + core.SerializationKey: "breeze", + }, + }, + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := isSupport(tt.cacheKey, tt.url); got != tt.want { + t.Errorf("isSupport() = %v, want %v", got, tt.want) + } + notSupportCache.Flush() + }) + } +} + +func TestGetEpStaticMeta(t *testing.T) { + tests := []struct { + name string + endpoint core.EndPoint + want map[string]string + }{ + { + name: "With DefaultMetaPrefix", + endpoint: &endpoint.MockEndpoint{ + URL: &core.URL{ + Parameters: map[string]string{ + core.DefaultMetaPrefix + "key1": "value1", + "otherKey": "value2", + }, + }, + }, + want: map[string]string{ + core.DefaultMetaPrefix + "key1": "value1", + }, + }, + { + name: "With EnvPrefix", + endpoint: &endpoint.MockEndpoint{ + URL: &core.URL{ + Parameters: map[string]string{ + envPrefix + "key2": "value3", + "otherKey": "value4", + }, + }, + }, + want: map[string]string{ + envPrefix + "key2": "value3", + }, + }, + { + name: "No Matching Prefix", + endpoint: &endpoint.MockEndpoint{ + URL: &core.URL{ + Parameters: map[string]string{ + "key3": "value5", + }, + }, + }, + want: map[string]string{}, + }, + { + name: "Nil URL", + endpoint: &endpoint.MockEndpoint{URL: nil}, + want: map[string]string{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := GetEpStaticMeta(tt.endpoint); !equalMaps(got, tt.want) { + t.Errorf("GetEpStaticMeta() = %v, want %v", got, tt.want) + } + }) + } +} + +func equalMaps(a, b map[string]string) bool { + if len(a) != len(b) { + return false + } + for k, v := range a { + if b[k] != v { + return false + } + } + return true +} + +func TestGetRemoteDynamicMeta(t *testing.T) { + breezeSerialization := &serialize.BreezeSerialization{} + resultWant := map[string]string{"key": "value"} + resultBody, _ := breezeSerialization.Serialize(resultWant) + tests := []struct { + name string + cacheKey string + available bool + endpoint *endpoint.MockEndpoint + want map[string]string + wantErr error + }{ + { + name: "Service Not Supported", + cacheKey: "test1", + available: true, + endpoint: &endpoint.MockEndpoint{ + URL: &core.URL{ + Protocol: "motan2", + Parameters: map[string]string{ + core.DynamicMetaKey: "true", + core.SerializationKey: "breeze", + }, + }, + MockResponse: &core.MotanResponse{ + Exception: &core.Exception{ErrMsg: core.ServiceNotSupport}, + }, + }, + want: nil, + wantErr: ServiceNotSupportError, + }, + { + name: "Endpoint Unavailable", + cacheKey: "test2", + available: false, + endpoint: &endpoint.MockEndpoint{ + URL: &core.URL{ + Protocol: "motan2", + Parameters: map[string]string{ + core.DynamicMetaKey: "true", + core.SerializationKey: "breeze", + }, + }, + }, + want: nil, + wantErr: errors.New("endpoint unavailable"), + }, + { + name: "Successful Call", + cacheKey: "test3", + available: true, + endpoint: &endpoint.MockEndpoint{ + URL: &core.URL{ + Protocol: "motan2", + Parameters: map[string]string{ + core.DynamicMetaKey: "true", + core.SerializationKey: "breeze", + }, + }, + MockResponse: &core.MotanResponse{ + Value: &core.DeserializableValue{ + Body: resultBody, + }, + }, + }, + want: resultWant, // expected a deserialized map + wantErr: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.endpoint.SetAvailable(tt.available) + got, err := getRemoteDynamicMeta(tt.cacheKey, tt.endpoint) + if err != nil && err.Error() != tt.wantErr.Error() { + t.Errorf("getRemoteDynamicMeta() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !equalMaps(got, tt.want) { + t.Errorf("getRemoteDynamicMeta() = %v, want %v", got, tt.want) + } + notSupportCache.Flush() + }) + } +} diff --git a/provider/metaProvider.go b/provider/metaProvider.go index bde124b8..38da6d2a 100644 --- a/provider/metaProvider.go +++ b/provider/metaProvider.go @@ -3,6 +3,7 @@ package provider import ( motan "github.com/weibocom/motan-go/core" "github.com/weibocom/motan-go/meta" + "github.com/weibocom/motan-go/serialize" ) type MetaProvider struct{} @@ -10,9 +11,26 @@ type MetaProvider struct{} func (m *MetaProvider) Initialize() {} func (m *MetaProvider) Call(request motan.Request) motan.Response { + // only support motan2 protocol and breeze serialization + if request.GetRPCContext(true).IsMotanV1 { + return &motan.MotanResponse{ + Exception: &motan.Exception{ErrCode: 500, ErrMsg: "meta provider not support motan1 protocol"}, + } + } + serialization := &serialize.BreezeSerialization{} + b, err := serialization.Serialize(meta.GetDynamicMeta()) + if err != nil { + return &motan.MotanResponse{ + Exception: &motan.Exception{ErrCode: 500, ErrMsg: "meta provider serialize fail"}, + } + } resp := &motan.MotanResponse{ RequestID: request.GetRequestID(), - Value: meta.GetDynamicMeta(), + Value: b, + RPCContext: &motan.RPCContext{ + Serialized: true, + SerializeNum: serialize.BreezeNumber, + }, } return resp } @@ -46,5 +64,8 @@ func (m *MetaProvider) IsAvailable() bool { } func (m *MetaProvider) GetRuntimeInfo() map[string]interface{} { - return make(map[string]interface{}) + info := map[string]interface{}{ + motan.RuntimeNameKey: m.GetName(), + } + return info } diff --git a/provider/metaProvider_test.go b/provider/metaProvider_test.go new file mode 100644 index 00000000..2214734f --- /dev/null +++ b/provider/metaProvider_test.go @@ -0,0 +1,54 @@ +package provider + +import ( + "testing" + + "github.com/weibocom/motan-go/core" +) + +func TestMetaProvider_Call(t *testing.T) { + tests := []struct { + name string + request core.Request + wantErr string + }{ + { + name: "Motan1 Protocol Not Supported", + request: &core.MotanRequest{ + RPCContext: &core.RPCContext{IsMotanV1: true}, + }, + wantErr: "meta provider not support motan1 protocol", + }, + { + name: "Successful Serialization", + request: &core.MotanRequest{ + RPCContext: &core.RPCContext{IsMotanV1: false}, + RequestID: 12345, + }, + wantErr: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + provider := &MetaProvider{} + resp := provider.Call(tt.request) + + if tt.wantErr != "" { + if resp.GetException() == nil || resp.GetException().ErrMsg != tt.wantErr { + t.Errorf("MetaProvider.Call() error = %v, wantErr %v", resp.GetException(), tt.wantErr) + } + } else { + if resp.GetException() != nil { + t.Errorf("MetaProvider.Call() unexpected error = %v", resp.GetException()) + } + if resp.GetRequestID() != tt.request.GetRequestID() { + t.Errorf("MetaProvider.Call() requestID = %v, want %v", resp.GetRequestID(), tt.request.GetRequestID()) + } + if !resp.GetRPCContext(true).Serialized { + t.Errorf("MetaProvider.Call() response not serialized") + } + } + }) + } +}