diff --git a/cluster/router/condition_router.go b/cluster/router/condition_router.go
index b63fa093d7dded5d6699c667591db5bc7dd7f9db..45ea0ab890592fb246335c430bd4776483c16401 100644
--- a/cluster/router/condition_router.go
+++ b/cluster/router/condition_router.go
@@ -134,7 +134,6 @@ func (c *ConditionRouter) Route(invokers []protocol.Invoker, url common.URL, inv
 	if len(c.ThenCondition) == 0 {
 		return result
 	}
-	localIP, _ := gxnet.GetLocalIP()
 	for _, invoker := range invokers {
 		isMatchThen, err := c.MatchThen(invoker.GetUrl(), url)
 		if err != nil {
@@ -153,6 +152,7 @@ func (c *ConditionRouter) Route(invokers []protocol.Invoker, url common.URL, inv
 		return result
 	} else if c.Force {
 		rule, _ := url.GetParamAndDecoded(constant.RULE_KEY)
+		localIP, _ := gxnet.GetLocalIP()
 		logger.Warnf("The route result is empty and force execute. consumer: %s, service: %s, router: %s", localIP, url.Service(), rule)
 		return result
 	}
@@ -222,18 +222,18 @@ func parseRule(rule string) (map[string]MatchPair, error) {
 
 //
 func (c *ConditionRouter) MatchWhen(url common.URL, invocation protocol.Invocation) (bool, error) {
-	condition, err := MatchCondition(c.WhenCondition, &url, nil, invocation)
+	condition, err := matchCondition(c.WhenCondition, &url, nil, invocation)
 	return len(c.WhenCondition) == 0 || condition, err
 }
 
 //MatchThen MatchThen
 func (c *ConditionRouter) MatchThen(url common.URL, param common.URL) (bool, error) {
-	condition, err := MatchCondition(c.ThenCondition, &url, &param, nil)
+	condition, err := matchCondition(c.ThenCondition, &url, &param, nil)
 	return len(c.ThenCondition) > 0 && condition, err
 }
 
 //MatchCondition MatchCondition
-func MatchCondition(pairs map[string]MatchPair, url *common.URL, param *common.URL, invocation protocol.Invocation) (bool, error) {
+func matchCondition(pairs map[string]MatchPair, url *common.URL, param *common.URL, invocation protocol.Invocation) (bool, error) {
 	sample := url.ToMap()
 	if sample == nil {
 		return true, perrors.Errorf("url is not allowed be nil")
@@ -292,6 +292,7 @@ func (pair MatchPair) isMatch(value string, param *common.URL) bool {
 		return true
 	}
 	if !pair.Mismatches.Empty() && !pair.Matches.Empty() {
+		//when both mismatches and matches contain the same value, then using mismatches first
 		for mismatch := range pair.Mismatches.Items {
 			if isMatchGlobPattern(mismatch.(string), value, param) {
 				return false
@@ -306,31 +307,3 @@ func (pair MatchPair) isMatch(value string, param *common.URL) bool {
 	}
 	return false
 }
-
-func isMatchGlobPattern(pattern string, value string, param *common.URL) bool {
-	if param != nil && strings.HasPrefix(pattern, "$") {
-		pattern = param.GetRawParam(pattern[1:])
-	}
-	if "*" == pattern {
-		return true
-	}
-	if len(pattern) == 0 && len(value) == 0 {
-		return true
-	}
-	if len(pattern) == 0 || len(value) == 0 {
-		return false
-	}
-	i := strings.LastIndex(pattern, "*")
-	switch i {
-	case -1:
-		return value == pattern
-	case len(pattern) - 1:
-		return strings.HasPrefix(value, pattern[0:i])
-	case 0:
-		return strings.HasSuffix(value, pattern[:i+1])
-	default:
-		prefix := pattern[0:1]
-		suffix := pattern[i+1:]
-		return strings.HasPrefix(value, prefix) && strings.HasSuffix(value, suffix)
-	}
-}
diff --git a/cluster/router/url_utils.go b/cluster/router/url_utils.go
new file mode 100644
index 0000000000000000000000000000000000000000..0adff7503be107f710549f0e99238c140d70af37
--- /dev/null
+++ b/cluster/router/url_utils.go
@@ -0,0 +1,62 @@
+/*
+ * 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 router
+
+import (
+	"strings"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+)
+
+func isMatchGlobPattern(pattern string, value string, param *common.URL) bool {
+	if param != nil && strings.HasPrefix(pattern, "$") {
+		pattern = param.GetRawParam(pattern[1:])
+	}
+	return isMatchInternalPattern(pattern, value)
+}
+
+func isMatchInternalPattern(pattern string, value string) bool {
+	if "*" == pattern {
+		return true
+	}
+	if len(pattern) == 0 && len(value) == 0 {
+		return true
+	}
+	if len(pattern) == 0 || len(value) == 0 {
+		return false
+	}
+	i := strings.LastIndex(pattern, "*")
+	switch i {
+	case -1:
+		// doesn't find "*"
+		return value == pattern
+	case len(pattern) - 1:
+		// "*" is at the end
+		return strings.HasPrefix(value, pattern[0:i])
+	case 0:
+		// "*" is at the beginning
+		return strings.HasSuffix(value, pattern[i+1:])
+	default:
+		// "*" is in the middle
+		prefix := pattern[0:1]
+		suffix := pattern[i+1:]
+		return strings.HasPrefix(value, prefix) && strings.HasSuffix(value, suffix)
+	}
+}
diff --git a/cluster/router/url_utils_test.go b/cluster/router/url_utils_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..caabf35515b934b49c6473b098db9f40ca973cd2
--- /dev/null
+++ b/cluster/router/url_utils_test.go
@@ -0,0 +1,44 @@
+/*
+ * 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 router
+
+import (
+	"context"
+	"testing"
+
+	"github.com/apache/dubbo-go/common"
+)
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+func TestIsMatchInternalPattern(t *testing.T) {
+	assert.Equal(t, true, isMatchInternalPattern("*", "value"))
+	assert.Equal(t, true, isMatchInternalPattern("", ""))
+	assert.Equal(t, false, isMatchInternalPattern("", "value"))
+	assert.Equal(t, true, isMatchInternalPattern("value", "value"))
+	assert.Equal(t, true, isMatchInternalPattern("v*", "value"))
+	assert.Equal(t, true, isMatchInternalPattern("*ue", "value"))
+	assert.Equal(t, true, isMatchInternalPattern("*e", "value"))
+	assert.Equal(t, true, isMatchInternalPattern("v*e", "value"))
+}
+
+func TestIsMatchGlobPattern(t *testing.T) {
+	url, _ := common.NewURL(context.TODO(), "dubbo://localhost:8080/Foo?key=v*e")
+	assert.Equal(t, true, isMatchGlobPattern("$key", "value", &url))
+}
diff --git a/registry/directory/directory.go b/registry/directory/directory.go
index 96ea612a1db5d116bd696019685acdc9a23cd389..044e4ea4da21d0aab6805a3cb992639c593e33de 100644
--- a/registry/directory/directory.go
+++ b/registry/directory/directory.go
@@ -18,7 +18,6 @@
 package directory
 
 import (
-	"fmt"
 	"reflect"
 	"sync"
 	"time"