You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by zt...@apache.org on 2021/12/19 06:29:35 UTC

[dubbo-go-pixiu] branch develop updated: using trie enhance dubbo route.go (#310)

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

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


The following commit(s) were added to refs/heads/develop by this push:
     new eebb7f0  using trie enhance dubbo route.go (#310)
eebb7f0 is described below

commit eebb7f002a041d1418adb13c045d06a61e4df142
Author: yqxu <31...@qq.com>
AuthorDate: Sun Dec 19 14:29:28 2021 +0800

    using trie enhance dubbo route.go (#310)
    
    * using trie enhance dubbo route.go
    
    * using trie enhance dubbo route.go
    
    * using trie enhance dubbo route.go
    
    * using trie enhance dubbo route.go
    
    * using trie enhance dubbo route.go
    
    * using trie enhance dubbo route.go
    
    * using trie enhance dubbo route.go
    
    * using trie enhance dubbo route.go
    
    * using trie enhance dubbo route.go
    
    * using trie enhance dubbo route.go
    
    * using trie enhance dubbo route.go
    
    * using trie enhance dubbo route.go
    
    Co-authored-by: Xin.Zh <dr...@foxmail.com>
    Co-authored-by: randy <zt...@gmail.com>
---
 pkg/common/constant/http.go                        |   1 +
 pkg/common/router/trie/trie.go                     |  15 +-
 pkg/common/router/trie/trie_test.go                |  69 ++++++++
 pkg/common/util/stringutil/stringutil.go           |   7 +-
 pkg/filter/http/apiconfig/api/discovery_service.go |   8 +
 .../http/apiconfig/api/discovery_service_test.go   |   5 +-
 pkg/filter/http/apiconfig/api_config.go            |  10 +-
 pkg/router/route.go                                | 187 ++++++++++-----------
 pkg/router/route_test.go                           | 136 +++++++--------
 9 files changed, 255 insertions(+), 183 deletions(-)

diff --git a/pkg/common/constant/http.go b/pkg/common/constant/http.go
index 0a488bc..b8e01c8 100644
--- a/pkg/common/constant/http.go
+++ b/pkg/common/constant/http.go
@@ -31,6 +31,7 @@ const (
 	HeaderValueAll       = "*"
 
 	PathSlash           = "/"
+	ProtocolSlash       = "://"
 	PathParamIdentifier = ":"
 )
 
diff --git a/pkg/common/router/trie/trie.go b/pkg/common/router/trie/trie.go
index d34b84d..f53db34 100644
--- a/pkg/common/router/trie/trie.go
+++ b/pkg/common/router/trie/trie.go
@@ -18,12 +18,13 @@
 package trie
 
 import (
-	"github.com/pkg/errors"
+	"strings"
 )
 
 import (
 	"github.com/apache/dubbo-go-pixiu/pkg/common/util/stringutil"
 	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+	"github.com/pkg/errors"
 )
 
 // Trie
@@ -54,6 +55,10 @@ type Node struct {
 	bizInfo          interface{}      // route info and any other info store here.
 }
 
+func (trie *Trie) Clear() bool {
+	return trie.root.Clear()
+}
+
 //IsEmpty put key and values into trie as map.
 func (trie *Trie) IsEmpty() bool {
 	return trie.root.IsEmpty()
@@ -103,6 +108,7 @@ func (trie Trie) Get(withOutHost string) (*Node, []string, bool, error) {
 //bool is ok
 //error
 func (trie Trie) Match(withOutHost string) (*Node, []string, bool) {
+	withOutHost = strings.Split(withOutHost, "?")[0]
 	parts := stringutil.Split(withOutHost)
 	node, param, ok := trie.root.Match(parts)
 	length := len(param)
@@ -166,6 +172,11 @@ func (node *Node) internalPut(keys []string, bizInfo interface{}) (bool, error)
 
 }
 
+func (node *Node) Clear() bool {
+	*node = Node{}
+	return true
+}
+
 //IsEmpty return true if empty
 func (node *Node) IsEmpty() bool {
 	if node.children == nil && node.matchStr == "" && node.PathVariableNode == nil && node.PathVariablesSet == nil {
@@ -320,7 +331,7 @@ func (node *Node) putNode(matchStr string, isReal bool, bizInfo interface{}) boo
 	if isReal {
 		selfNode.bizInfo = bizInfo
 	}
-	selfNode.endOfPath = selfNode.endOfPath || old.endOfPath
+	selfNode.endOfPath = isReal || old.endOfPath
 	node.children[matchStr] = selfNode
 	return true
 }
diff --git a/pkg/common/router/trie/trie_test.go b/pkg/common/router/trie/trie_test.go
index ee53f11..4d5ea53 100644
--- a/pkg/common/router/trie/trie_test.go
+++ b/pkg/common/router/trie/trie_test.go
@@ -22,6 +22,7 @@ import (
 )
 
 import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/util/stringutil"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -71,6 +72,9 @@ func TestTrie_MatchAndGet(t *testing.T) {
 	result, _, _ := trie.Match("/a/b")
 	assert.Equal(t, result.GetBizInfo(), "ab")
 
+	result, _, _ = trie.Match("/a/b?a=b&c=d")
+	assert.Equal(t, result.GetBizInfo(), "ab")
+
 	_, _ = trie.Put("POST/api/v1/**", "ab")
 	result, _, _ = trie.Match("POST/api/v1")
 	assert.Equal(t, "ab", result.GetBizInfo())
@@ -133,4 +137,69 @@ func TestTrie_MatchAndGet(t *testing.T) {
 	node, _, ok, _ = trie.Get("/path1/:p/path2/:p2")
 	assert.True(t, ok)
 	assert.True(t, node.GetBizInfo() == "test1")
+
+	node, _, ok = trie.Match("/path1/12/path2/12?a=b")
+	assert.True(t, ok)
+	assert.True(t, node.GetBizInfo() == "test1")
+}
+
+func TestTrie_Clear(t *testing.T) {
+	v := "http://baidu.com/aa/bb"
+	v = stringutil.GetTrieKey("PUT", v)
+	assert.Equal(t, "PUT/aa/bb", v)
+
+	trie := NewTrie()
+	ret, _ := trie.Put("/path1/:pathvarible1/path2/:pathvarible2", "")
+	assert.True(t, ret)
+
+	ret, _ = trie.Put("/path1/:pathvarible1/path2/:pathvarible2/**", "")
+	assert.True(t, ret)
+
+	ret, _ = trie.Put("/path2/:pathvarible1/path2/:pathvarible2", "")
+	assert.True(t, ret)
+	ret, _ = trie.Put("/path2/3/path2/:pathvarible2", "")
+	assert.True(t, ret)
+
+	ret, _ = trie.Put("/path2/3/path2/:pathvarible2", "")
+	assert.False(t, ret)
+
+	ret, _ = trie.Put("/path2/3/path2/:pathvarible2/3", "")
+	assert.True(t, ret)
+	ret, _ = trie.Put("/path2/3/path2/:432423/3", "")
+	assert.False(t, ret)
+	ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/d/:fdsa", "")
+	assert.True(t, ret)
+
+	ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/c/:fdsa", "")
+	assert.True(t, ret)
+
+	ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/c/:fdsafdsafsdafsda", "")
+	assert.False(t, ret)
+
+	ret, _ = trie.Put("/path1/:pathvarible1/path2/:pathvarible2/:fdsa", "")
+	assert.True(t, ret)
+
+	ret, _ = trie.Put("/path1/:432/path2/:34", "")
+
+	assert.False(t, ret)
+	assert.False(t, trie.IsEmpty())
+	trie.Clear()
+	assert.True(t, trie.IsEmpty())
+}
+
+func TestTrie_ParamMatch(t *testing.T) {
+	trie := NewTrie()
+	ret, _ := trie.Put("PUT/path1/:pathvarible1/path2/:pathvarible2", "")
+	assert.True(t, ret)
+	str := "https://www.baidu.com/path1/param1/path2/param2?aaaaa=aaaaa"
+
+	node, _, ok := trie.Match(stringutil.GetTrieKey("PUT", str))
+	assert.True(t, ok)
+	assert.Equal(t, "", node.GetBizInfo())
+
+	ret, _ = trie.Put("PUT/path1/:pathvarible1/path2", "")
+	node, _, ok = trie.Match(stringutil.GetTrieKey("PUT", str))
+	assert.True(t, ok)
+	assert.Equal(t, "", node.GetBizInfo())
+	assert.True(t, ret)
 }
diff --git a/pkg/common/util/stringutil/stringutil.go b/pkg/common/util/stringutil/stringutil.go
index 355365f..6b0c9ce 100644
--- a/pkg/common/util/stringutil/stringutil.go
+++ b/pkg/common/util/stringutil/stringutil.go
@@ -72,14 +72,19 @@ func IsMatchAll(key string) bool {
 
 func GetTrieKey(method string, path string) string {
 	ret := ""
+	//"http://localhost:8882/api/v1/test-dubbo/user?name=tc"
+	if strings.Contains(path, constant.ProtocolSlash) {
+		path = path[strings.Index(path, constant.ProtocolSlash)+len(constant.ProtocolSlash):]
+		path = path[strings.Index(path, constant.PathSlash)+1:]
+	}
 	if strings.HasPrefix(path, constant.PathSlash) {
 		ret = method + path
 	} else {
 		ret = method + constant.PathSlash + path
 	}
-
 	if strings.HasSuffix(ret, constant.PathSlash) {
 		ret = ret[0 : len(ret)-1]
 	}
+	ret = strings.Split(ret, "?")[0]
 	return ret
 }
diff --git a/pkg/filter/http/apiconfig/api/discovery_service.go b/pkg/filter/http/apiconfig/api/discovery_service.go
index 818666e..f7f1765 100644
--- a/pkg/filter/http/apiconfig/api/discovery_service.go
+++ b/pkg/filter/http/apiconfig/api/discovery_service.go
@@ -41,6 +41,7 @@ type APIDiscoveryService interface {
 	AddAPI(fr.API) error
 	ClearAPI() error
 	GetAPI(string, config.HTTPVerb) (fr.API, error)
+	MatchAPI(string, config.HTTPVerb) (fr.API, error)
 	RemoveAPIByPath(deleted config.Resource) error
 	RemoveAPIByIntance(api fr.API) error
 	RemoveAPI(fullPath string, method config.Method) error
@@ -72,6 +73,13 @@ func (l *LocalMemoryAPIDiscoveryService) GetAPI(url string, httpVerb config.HTTP
 	return fr.API{}, errors.New("not found")
 }
 
+func (l *LocalMemoryAPIDiscoveryService) MatchAPI(url string, httpVerb config.HTTPVerb) (fr.API, error) {
+	if api, ok := l.router.MatchAPI(url, httpVerb); ok {
+		return *api, nil
+	}
+	return fr.API{}, errors.New("not found")
+}
+
 // ClearAPI clear all api
 func (l *LocalMemoryAPIDiscoveryService) ClearAPI() error {
 	return l.router.ClearAPI()
diff --git a/pkg/filter/http/apiconfig/api/discovery_service_test.go b/pkg/filter/http/apiconfig/api/discovery_service_test.go
index a782c40..d12dda8 100644
--- a/pkg/filter/http/apiconfig/api/discovery_service_test.go
+++ b/pkg/filter/http/apiconfig/api/discovery_service_test.go
@@ -18,6 +18,7 @@
 package api
 
 import (
+	"strings"
 	"testing"
 )
 
@@ -128,7 +129,7 @@ func TestLoadAPIFromResource(t *testing.T) {
 	assert.Equal(t, rsp.URLPattern, "/")
 	rsp, _ = apiDiscSrv.GetAPI("/mock", fc.MethodGet)
 	assert.Equal(t, rsp.URLPattern, "/mock")
-	rsp, _ = apiDiscSrv.GetAPI("/mock2/12345", fc.MethodPut)
+	rsp, _ = apiDiscSrv.MatchAPI("/mock2/12345", fc.MethodPut)
 	assert.Equal(t, rsp.URLPattern, "/mock2/:id")
 
 	tempResources = []fc.Resource{
@@ -181,5 +182,5 @@ func TestLoadAPIFromMethods(t *testing.T) {
 	assert.Equal(t, rsp.URLPattern, "/mock")
 	rsp, _ = apiDiscSrv.GetAPI("/mock", fc.MethodGet)
 	assert.Equal(t, rsp.URLPattern, "/mock")
-	assert.EqualError(t, err, "path: /mock, Method: PUT, error: Method PUT with address localhost:8080 already exists in path /mock")
+	assert.True(t, strings.Contains(err.Error(), "path: /mock, Method: PUT, error: Method PUT with address /mock already exists in path /mock"))
 }
diff --git a/pkg/filter/http/apiconfig/api_config.go b/pkg/filter/http/apiconfig/api_config.go
index 2917077..50518a0 100644
--- a/pkg/filter/http/apiconfig/api_config.go
+++ b/pkg/filter/http/apiconfig/api_config.go
@@ -82,11 +82,11 @@ func (f *Filter) Apply() error {
 		return nil
 	}
 
-	config, err := initApiConfig(f.cfg)
+	cfg, err := initApiConfig(f.cfg)
 	if err != nil {
 		logger.Errorf("Get ApiConfig fail: %v", err)
 	}
-	if err := f.apiService.InitAPIsFromConfig(*config); err != nil {
+	if err := f.apiService.InitAPIsFromConfig(*cfg); err != nil {
 		logger.Errorf("InitAPIsFromConfig fail: %v", err)
 	}
 
@@ -115,7 +115,7 @@ func (f *Filter) PrepareFilterChain(ctx *contexthttp.HttpContext) error {
 
 func (f *Filter) Handle(ctx *contexthttp.HttpContext) {
 	req := ctx.Request
-	api, err := f.apiService.GetAPI(req.URL.Path, fc.HTTPVerb(req.Method))
+	v, err := f.apiService.MatchAPI(req.URL.Path, fc.HTTPVerb(req.Method))
 	if err != nil {
 		if _, err := ctx.WriteWithStatus(http.StatusNotFound, constant.Default404Body); err != nil {
 			logger.Errorf("WriteWithStatus fail: %v", err)
@@ -127,7 +127,7 @@ func (f *Filter) Handle(ctx *contexthttp.HttpContext) {
 		return
 	}
 
-	if !api.Method.Enable {
+	if !v.Method.Enable {
 		if _, err := ctx.WriteWithStatus(http.StatusNotAcceptable, constant.Default406Body); err != nil {
 			logger.Errorf("WriteWithStatus fail: %v", err)
 		}
@@ -138,7 +138,7 @@ func (f *Filter) Handle(ctx *contexthttp.HttpContext) {
 		ctx.Abort()
 		return
 	}
-	ctx.API(api)
+	ctx.API(v)
 	ctx.Next()
 }
 
diff --git a/pkg/router/route.go b/pkg/router/route.go
index 559524f..6961b03 100644
--- a/pkg/router/route.go
+++ b/pkg/router/route.go
@@ -26,131 +26,103 @@ import (
 import (
 	"github.com/dubbogo/dubbo-go-pixiu-filter/pkg/api/config"
 	"github.com/dubbogo/dubbo-go-pixiu-filter/pkg/router"
-
-	"github.com/emirpasic/gods/trees/avltree"
-
 	"github.com/pkg/errors"
 )
 
 import (
 	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/router/trie"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/util/stringutil"
 )
 
 // Node defines the single method of the router configured API
 type Node struct {
 	fullPath string
-	wildcard bool
 	filters  []string
-	methods  map[config.HTTPVerb]*config.Method
+	method   *config.Method
 	headers  map[string]string
 }
 
 // Route defines the tree of router APIs
 type Route struct {
-	lock         sync.RWMutex
-	tree         *avltree.Tree
-	wildcardTree *avltree.Tree
+	lock sync.RWMutex
+	tree trie.Trie
 }
 
 // ClearAPI clear the api
 func (rt *Route) ClearAPI() error {
 	rt.lock.Lock()
 	defer rt.lock.Unlock()
-	rt.wildcardTree.Clear()
 	rt.tree.Clear()
 	return nil
 }
 
 func (r *Route) RemoveAPI(api router.API) {
-	fullPath := api.URLPattern
-	node, ok := r.findNode(fullPath)
-	if !ok {
-		return
-	}
-	if tempMethod, ok := node.methods[api.HTTPVerb]; ok {
-		splitedURLs := strings.Split(tempMethod.IntegrationRequest.HTTPBackendConfig.URL, ",")
-		afterRemoveedURL := make([]string, 0, len(splitedURLs))
-		for _, v := range splitedURLs {
-			if v != api.IntegrationRequest.HTTPBackendConfig.URL {
-				afterRemoveedURL = append(afterRemoveedURL, v)
-			}
-		}
-		if len(afterRemoveedURL) == 0 {
-			delete(node.methods, api.HTTPVerb)
+	r.lock.Lock()
+	defer r.lock.Unlock()
+	lowerCasePath := strings.ToLower(api.URLPattern)
+	key := getTrieKey(api.Method.HTTPVerb, lowerCasePath, false)
+	_, _ = r.tree.Remove(key)
+}
+
+func getTrieKey(method config.HTTPVerb, path string, isPrefix bool) string {
+	if isPrefix {
+		if !strings.HasSuffix(path, constant.PathSlash) {
+			path = path + constant.PathSlash
 		}
-		node.methods[api.HTTPVerb].IntegrationRequest.HTTPBackendConfig.URL = strings.Join(afterRemoveedURL, ",")
-		return
+		path = path + "**"
 	}
+	return stringutil.GetTrieKey(string(method), path)
 }
 
 // PutAPI puts an api into the resource
 func (rt *Route) PutAPI(api router.API) error {
-	fullPath := api.URLPattern
-	node, ok := rt.findNode(fullPath)
-	rt.lock.Lock()
-	defer rt.lock.Unlock()
+	lowerCasePath := strings.ToLower(api.URLPattern)
+	key := getTrieKey(api.Method.HTTPVerb, lowerCasePath, false)
+	node, ok := rt.getNode(key)
 	if !ok {
-		wildcard := strings.Contains(fullPath, constant.PathParamIdentifier)
 		rn := &Node{
-			fullPath: fullPath,
-			methods:  map[config.HTTPVerb]*config.Method{api.Method.HTTPVerb: &api.Method},
-			wildcard: wildcard,
+			fullPath: lowerCasePath,
+			method:   &api.Method,
 			headers:  api.Headers,
 		}
-		if wildcard {
-			rt.wildcardTree.Put(fullPath, rn)
-		}
-		rt.tree.Put(fullPath, rn)
+		rt.lock.Lock()
+		defer rt.lock.Unlock()
+		_, _ = rt.tree.Put(key, rn)
 		return nil
 	}
-	return node.putMethod(api.Method, api.Headers)
+	return errors.Errorf("Method %s with address %s already exists in path %s",
+		api.Method.HTTPVerb, lowerCasePath, node.fullPath)
 }
 
-func (node *Node) putMethod(method config.Method, headers map[string]string) error {
-	// todo lock
-	if tempMethod, ok := node.methods[method.HTTPVerb]; ok {
-		splitedURLs := strings.Split(tempMethod.IntegrationRequest.HTTPBackendConfig.URL, ",")
-		for _, v := range splitedURLs {
-			if v == method.IntegrationRequest.HTTPBackendConfig.URL {
-				return errors.Errorf("Method %s with address %s already exists in path %s",
-					method.HTTPVerb, v, node.fullPath)
-			}
-		}
-		splitedURLs = append(splitedURLs, method.IntegrationRequest.HTTPBackendConfig.URL)
-		node.methods[method.HTTPVerb].IntegrationRequest.HTTPBackendConfig.URL = strings.Join(splitedURLs, ",")
-		node.headers = headers
-		return nil
-	}
-	node.methods[method.HTTPVerb] = &method
-	node.headers = headers
-	return nil
-}
-
-// UpdateAPI update the api method in the existing router node
-func (rt *Route) UpdateAPI(api router.API) error {
-	node, found := rt.findNode(api.URLPattern)
-	if found {
-		if _, ok := node.methods[api.Method.HTTPVerb]; ok {
-			rt.lock.Lock()
-			defer rt.lock.Unlock()
-			node.methods[api.Method.HTTPVerb] = &api.Method
-		}
+// FindAPI return if api has path in trie,or nil
+func (rt *Route) FindAPI(fullPath string, httpverb config.HTTPVerb) (*router.API, bool) {
+	lowerCasePath := strings.ToLower(fullPath)
+	key := getTrieKey(httpverb, lowerCasePath, false)
+	if n, found := rt.getNode(key); found {
+		rt.lock.RLock()
+		defer rt.lock.RUnlock()
+		return &router.API{
+			URLPattern: n.fullPath,
+			Method:     *n.method,
+			Headers:    n.headers,
+		}, found
 	}
-	return nil
+	return nil, false
 }
 
-// FindAPI returns the api that meets the
-func (rt *Route) FindAPI(fullPath string, httpverb config.HTTPVerb) (*router.API, bool) {
-	if n, found := rt.findNode(fullPath); found {
+// MatchAPI FindAPI returns the api that meets the rule
+func (rt *Route) MatchAPI(fullPath string, httpverb config.HTTPVerb) (*router.API, bool) {
+	lowerCasePath := strings.ToLower(fullPath)
+	key := getTrieKey(httpverb, lowerCasePath, false)
+	if n, found := rt.matchNode(key); found {
 		rt.lock.RLock()
 		defer rt.lock.RUnlock()
-		if method, ok := n.methods[httpverb]; ok {
-			return &router.API{
-				URLPattern: n.fullPath,
-				Method:     *method,
-				Headers:    n.headers,
-			}, ok
-		}
+		return &router.API{
+			URLPattern: n.fullPath,
+			Method:     *n.method,
+			Headers:    n.headers,
+		}, found
 	}
 	return nil, false
 }
@@ -159,51 +131,63 @@ func (rt *Route) FindAPI(fullPath string, httpverb config.HTTPVerb) (*router.API
 func (rt *Route) DeleteNode(fullPath string) bool {
 	rt.lock.RLock()
 	defer rt.lock.RUnlock()
-	rt.tree.Remove(fullPath)
+	methodList := [8]config.HTTPVerb{"ANY", "GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"}
+	for _, v := range methodList {
+		key := getTrieKey(v, fullPath, false)
+		_, _ = rt.tree.Remove(key)
+	}
 	return true
 }
 
 // DeleteAPI delete api by fullPath and http verb
 func (rt *Route) DeleteAPI(fullPath string, httpverb config.HTTPVerb) bool {
-	if n, found := rt.findNode(fullPath); found {
+	lowerCasePath := strings.ToLower(fullPath)
+	key := getTrieKey(httpverb, lowerCasePath, false)
+	if _, found := rt.getNode(key); found {
 		rt.lock.RLock()
 		defer rt.lock.RUnlock()
-		delete(n.methods, httpverb)
+		_, _ = rt.tree.Remove(key)
 		return true
 	}
 	return false
 }
 
-func (rt *Route) findNode(fullPath string) (*Node, bool) {
+func (rt *Route) getNode(fullPath string) (*Node, bool) {
 	var n interface{}
 	var found bool
-	if n, found = rt.searchWildcard(fullPath); !found {
-		rt.lock.RLock()
-		defer rt.lock.RUnlock()
-		if n, found = rt.tree.Get(fullPath); !found {
-			return nil, false
-		}
+	rt.lock.RLock()
+	defer rt.lock.RUnlock()
+	trieNode, _, _, _ := rt.tree.Get(fullPath)
+	found = trieNode != nil
+	if !found {
+		return nil, false
+	}
+	n = trieNode.GetBizInfo()
+	if n == nil {
+		return nil, false
 	}
 	return n.(*Node), found
 }
 
-func (rt *Route) searchWildcard(fullPath string) (*Node, bool) {
+func (rt *Route) matchNode(fullPath string) (*Node, bool) {
+	var n interface{}
+	var found bool
 	rt.lock.RLock()
 	defer rt.lock.RUnlock()
-	wildcardPaths := rt.wildcardTree.Keys()
-	for _, p := range wildcardPaths {
-		if wildcardMatch(p.(string), fullPath) != nil {
-			n, ok := rt.wildcardTree.Get(p)
-			return n.(*Node), ok
-		}
+	trieNode, _, _ := rt.tree.Match(fullPath)
+	found = trieNode != nil
+	if !found {
+		return nil, false
 	}
-	return nil, false
+	n = trieNode.GetBizInfo()
+	if n == nil {
+		return nil, false
+	}
+	return n.(*Node), found
 }
 
-// wildcardMatch validate if the checkPath meets the wildcardPath,
-// for example /vought/12345 should match wildcard path /vought/:id;
-// /vought/1234abcd/status should not match /vought/:id;
 func wildcardMatch(wildcardPath string, checkPath string) url.Values {
+
 	cPaths := strings.Split(strings.TrimLeft(checkPath, constant.PathSlash), constant.PathSlash)
 	wPaths := strings.Split(strings.TrimLeft(wildcardPath, constant.PathSlash), constant.PathSlash)
 	result := url.Values{}
@@ -224,7 +208,6 @@ func wildcardMatch(wildcardPath string, checkPath string) url.Values {
 // NewRoute returns an empty router tree
 func NewRoute() *Route {
 	return &Route{
-		tree:         avltree.NewWithStringComparator(),
-		wildcardTree: avltree.NewWithStringComparator(),
+		tree: trie.NewTrie(),
 	}
 }
diff --git a/pkg/router/route_test.go b/pkg/router/route_test.go
index 7275b14..d4e3bca 100644
--- a/pkg/router/route_test.go
+++ b/pkg/router/route_test.go
@@ -20,13 +20,10 @@ package router
 import (
 	"testing"
 )
-
 import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/router/trie"
 	"github.com/dubbogo/dubbo-go-pixiu-filter/pkg/api/config"
 	"github.com/dubbogo/dubbo-go-pixiu-filter/pkg/router"
-
-	"github.com/emirpasic/gods/trees/avltree"
-
 	"github.com/stretchr/testify/assert"
 )
 
@@ -43,12 +40,11 @@ func getMockMethod(verb config.HTTPVerb) config.Method {
 
 func TestPut(t *testing.T) {
 	rt := &Route{
-		tree:         avltree.NewWithStringComparator(),
-		wildcardTree: avltree.NewWithStringComparator(),
+		tree: trie.NewTrie(),
 	}
 	n0 := getMockMethod(config.MethodGet)
-	rt.PutAPI(router.API{URLPattern: "/", Method: n0})
-	_, ok := rt.tree.Get("/")
+	_ = rt.PutAPI(router.API{URLPattern: "/", Method: n0})
+	_, ok := rt.FindAPI("/", n0.HTTPVerb)
 	assert.True(t, ok)
 
 	err := rt.PutAPI(router.API{URLPattern: "/", Method: n0})
@@ -59,36 +55,33 @@ func TestPut(t *testing.T) {
 	assert.Nil(t, err)
 	err = rt.PutAPI(router.API{URLPattern: "/mock", Method: n1})
 	assert.Nil(t, err)
-	mNode, ok := rt.tree.Get("/mock")
+	_, ok = rt.FindAPI("/mock", n0.HTTPVerb)
+	assert.True(t, ok)
+	_, ok = rt.FindAPI("/mock", n1.HTTPVerb)
 	assert.True(t, ok)
-	assert.Equal(t, len(mNode.(*Node).methods), 2)
 
 	err = rt.PutAPI(router.API{URLPattern: "/mock/test", Method: n0})
 	assert.Nil(t, err)
-	_, ok = rt.tree.Get("/mock/test")
+	_, ok = rt.FindAPI("/mock/test", n0.HTTPVerb)
 	assert.True(t, ok)
 
-	rt.PutAPI(router.API{URLPattern: "/test/:id", Method: n0})
-	tNode, ok := rt.tree.Get("/test/:id")
+	_ = rt.PutAPI(router.API{URLPattern: "/test/:id", Method: n0})
+	_, ok = rt.FindAPI("/test/:id", n0.HTTPVerb)
 	assert.True(t, ok)
-	assert.True(t, tNode.(*Node).wildcard)
 
 	err = rt.PutAPI(router.API{URLPattern: "/test/:id", Method: n1})
 	assert.Nil(t, err)
 	err = rt.PutAPI(router.API{URLPattern: "/test/js", Method: n0})
-	assert.Error(t, err, "/test/:id wildcard already exist so that cannot add path /test/js")
-
+	assert.Nil(t, err)
 	err = rt.PutAPI(router.API{URLPattern: "/test/:id/mock", Method: n0})
-	tNode, ok = rt.tree.Get("/test/:id/mock")
+	_, ok = rt.FindAPI("/test/:id/mock", n0.HTTPVerb)
 	assert.True(t, ok)
-	assert.True(t, tNode.(*Node).wildcard)
 	assert.Nil(t, err)
 }
 
-func TestFindMethod(t *testing.T) {
+func TestMatchMethod(t *testing.T) {
 	rt := &Route{
-		tree:         avltree.NewWithStringComparator(),
-		wildcardTree: avltree.NewWithStringComparator(),
+		tree: trie.NewTrie(),
 	}
 	n0 := getMockMethod(config.MethodGet)
 	n1 := getMockMethod(config.MethodPost)
@@ -99,72 +92,73 @@ func TestFindMethod(t *testing.T) {
 	e = rt.PutAPI(router.API{URLPattern: "/vought/:id/supe/:name", Method: n1})
 	assert.Nil(t, e)
 
-	m, ok := rt.FindAPI("/theboys", config.MethodGet)
+	m, ok := rt.MatchAPI("/theboys", config.MethodGet)
 	assert.True(t, ok)
 	assert.NotNil(t, m)
 	assert.Equal(t, m.URLPattern, "/theboys")
 
-	m, ok = rt.FindAPI("/theboys", config.MethodPost)
+	m, ok = rt.MatchAPI("/theboys", config.MethodPost)
 	assert.False(t, ok)
 	assert.Nil(t, m)
 
-	m, ok = rt.FindAPI("/vought/123/supe/startlight", config.MethodPost)
+	m, ok = rt.MatchAPI("/vought/123/supe/startlight", config.MethodPost)
 	assert.True(t, ok)
 	assert.NotNil(t, m)
 	assert.Equal(t, m.URLPattern, "/vought/:id/supe/:name")
 
-	m, ok = rt.FindAPI("/vought/123/supe/startlight", config.MethodPost)
+	m, ok = rt.MatchAPI("/vought/123/supe/startlight", config.MethodPost)
 	assert.True(t, ok)
 	assert.NotNil(t, m)
 	assert.Equal(t, m.URLPattern, "/vought/:id/supe/:name")
 }
 
-func TestUpdateMethod(t *testing.T) {
-	m0 := getMockMethod(config.MethodGet)
-	m1 := getMockMethod(config.MethodGet)
-	m0.DubboBackendConfig.Version = "1.0.0"
-	m1.DubboBackendConfig.Version = "2.0.0"
-
-	rt := NewRoute()
-	rt.PutAPI(router.API{URLPattern: "/marvel", Method: m0})
-	m, _ := rt.FindAPI("/marvel", config.MethodGet)
-	assert.Equal(t, m.DubboBackendConfig.Version, "1.0.0")
-	rt.UpdateAPI(router.API{URLPattern: "/marvel", Method: m1})
-	m, ok := rt.FindAPI("/marvel", config.MethodGet)
-	assert.True(t, ok)
-	assert.Equal(t, m.DubboBackendConfig.Version, "2.0.0")
-
-	rt.PutAPI(router.API{URLPattern: "/theboys/:id", Method: m0})
-	m, _ = rt.FindAPI("/theBoys/12345", config.MethodGet)
-	assert.Equal(t, m.DubboBackendConfig.Version, "1.0.0")
-	rt.UpdateAPI(router.API{URLPattern: "/theBoys/:id", Method: m1})
-	m, ok = rt.FindAPI("/theBoys/12345", config.MethodGet)
-	assert.True(t, ok)
-	assert.Equal(t, m.DubboBackendConfig.Version, "2.0.0")
-}
-
-func TestSearchWildcard(t *testing.T) {
-	rt := &Route{
-		tree:         avltree.NewWithStringComparator(),
-		wildcardTree: avltree.NewWithStringComparator(),
-	}
-	n0 := getMockMethod(config.MethodGet)
-	e := rt.PutAPI(router.API{URLPattern: "/theboys", Method: n0})
-	assert.Nil(t, e)
-	e = rt.PutAPI(router.API{URLPattern: "/theboys/:id", Method: n0})
-	assert.Nil(t, e)
-	e = rt.PutAPI(router.API{URLPattern: "/vought/:id/supe/:name", Method: n0})
-	assert.Nil(t, e)
-
-	_, ok := rt.searchWildcard("/marvel")
-	assert.False(t, ok)
-	_, ok = rt.searchWildcard("/theboys/:id/age")
-	assert.False(t, ok)
-	_, ok = rt.searchWildcard("/theboys/butcher")
-	assert.True(t, ok)
-	_, ok = rt.searchWildcard("/vought/:id/supe/homelander")
-	assert.True(t, ok)
-}
+//
+//func TestUpdateMethod(t *testing.T) {
+//	m0 := getMockMethod(config.MethodGet)
+//	m1 := getMockMethod(config.MethodGet)
+//	m0.DubboBackendConfig.Version = "1.0.0"
+//	m1.DubboBackendConfig.Version = "2.0.0"
+//
+//	rt := NewRoute()
+//	rt.PutAPI(router.API{URLPattern: "/marvel", Method: m0})
+//	m, _ := rt.FindAPI("/marvel", config.MethodGet)
+//	assert.Equal(t, m.DubboBackendConfig.Version, "1.0.0")
+//	rt.UpdateAPI(router.API{URLPattern: "/marvel", Method: m1})
+//	m, ok := rt.FindAPI("/marvel", config.MethodGet)
+//	assert.True(t, ok)
+//	assert.Equal(t, m.DubboBackendConfig.Version, "2.0.0")
+//
+//	rt.PutAPI(router.API{URLPattern: "/theboys/:id", Method: m0})
+//	m, _ = rt.FindAPI("/theBoys/12345", config.MethodGet)
+//	assert.Equal(t, m.DubboBackendConfig.Version, "1.0.0")
+//	rt.UpdateAPI(router.API{URLPattern: "/theBoys/:id", Method: m1})
+//	m, ok = rt.FindAPI("/theBoys/12345", config.MethodGet)
+//	assert.True(t, ok)
+//	assert.Equal(t, m.DubboBackendConfig.Version, "2.0.0")
+//}
+
+//func TestSearchWildcard(t *testing.T) {
+//	rt := &Route{
+//		tree:         avltree.NewWithStringComparator(),
+//		wildcardTree: avltree.NewWithStringComparator(),
+//	}
+//	n0 := getMockMethod(config.MethodGet)
+//	e := rt.PutAPI(router.API{URLPattern: "/theboys", Method: n0})
+//	assert.Nil(t, e)
+//	e = rt.PutAPI(router.API{URLPattern: "/theboys/:id", Method: n0})
+//	assert.Nil(t, e)
+//	e = rt.PutAPI(router.API{URLPattern: "/vought/:id/supe/:name", Method: n0})
+//	assert.Nil(t, e)
+//
+//	_, ok := rt.searchWildcard("/marvel")
+//	assert.False(t, ok)
+//	_, ok = rt.searchWildcard("/theboys/:id/age")
+//	assert.False(t, ok)
+//	_, ok = rt.searchWildcard("/theboys/butcher")
+//	assert.True(t, ok)
+//	_, ok = rt.searchWildcard("/vought/:id/supe/homelander")
+//	assert.True(t, ok)
+//}
 
 func TestWildcardMatch(t *testing.T) {
 	vals := wildcardMatch("/vought/:id", "/vought/12345")