Repository: go-kratos/kratos
Pull request #2281 (2022-08-16 07:23:51)

fix grammar problems in the Readme file
Found some grammar mistakes in the Readme.md file along with some sentences that don't feel smooth to read. This pull request fixed them.


Patch:
diff --git a/README.md b/README.md
index 0508d5c6..f4023812 100644
--- a/README.md
+++ b/README.md
@@ -17,21 +17,21 @@
 
 ## About Kratos
   
-> The name is inspired by the game God of War which is based on Greek myths, tells the adventures of Kratos who became a god of war from a mortal and launched a god-killing slaughter.
+> The name is inspired by the Greek-mythology-based game "God of War". It tells the adventures of Kratos becoming a god of war from a mortal and launching a god-killing slaughter.
 
 Kratos is a microservice-oriented governance framework implemented by golang, which offers convenient capabilities to help you quickly build a bulletproof application from scratch, such as:
 
 - The [communication protocol](https://go-kratos.dev/en/docs/component/api) is based on the HTTP/gRPC through the definition of Protobuf.
-- Abstract [transport](https://go-kratos.dev/en/docs/component/transport/overview) layer are support: [HTTP](https://go-kratos.dev/en/docs/component/transport/http) / [gRPC](https://go-kratos.dev/en/docs/component/transport/grpc).
-- Powerful [middleware](https://go-kratos.dev/en/docs/component/middleware/overview) design, support: [Tracing (OpenTelemetry)](https://go-kratos.dev/en/docs/component/middleware/tracing)、[Metrics (Prometheus is default)](https://go-kratos.dev/en/docs/component/middleware/metrics)、[Recovery](https://go-kratos.dev/en/docs/component/middleware/recovery) and more.
+- Abstract [transport](https://go-kratos.dev/en/docs/component/transport/overview) layer support: [HTTP](https://go-kratos.dev/en/docs/component/transport/http) / [gRPC](https://go-kratos.dev/en/docs/component/transport/grpc).
+- Powerful [middleware](https://go-kratos.dev/en/docs/component/middleware/overview) design, support: [Tracing (OpenTelemetry)](https://go-kratos.dev/en/docs/component/middleware/tracing), [Metrics (Prometheus is default)](https://go-kratos.dev/en/docs/component/middleware/metrics), [Recovery](https://go-kratos.dev/en/docs/component/middleware/recovery) and more.
 - [Registry](https://go-kratos.dev/en/docs/component/registry) interface able to be connected with various other centralized registries through plug-ins.
-- The [standard log interfaces](https://go-kratos.dev/en/docs/component/log) ease the integration of the third-party log libs and logs are collected through the *Fluentd*.
-- The selection of the content [encoding](https://go-kratos.dev/en/docs/component/encoding) is automatically supported by Accept and Content-Type.
+- The [standard log interfaces](https://go-kratos.dev/en/docs/component/log) ease the integration of the third-party log libs with logs collected through the *Fluentd*.
+- Automatically support the selection of the content [encoding](https://go-kratos.dev/en/docs/component/encoding) with Accept and Content-Type.
 - Multiple data sources are supported for [configurations](https://go-kratos.dev/en/docs/component/config) and dynamic configurations (use atomic operations).
-- In the protocol of HTTP/gRPC, use uniform [metadata](https://go-kratos.dev/en/docs/component/metadata) transfer method.
+- In the protocol of HTTP/gRPC, use the uniform [metadata](https://go-kratos.dev/en/docs/component/metadata) transfer method.
 - You can define [errors](https://go-kratos.dev/en/docs/component/errors/) in protos and generate enums with protoc-gen-go.
-- You can define [verification rules](https://go-kratos.dev/en/docs/component/middleware/validate) in Protobuf which is supported by HTTP/gRPC service.
-- [Swagger API](https://go-kratos.dev/en/docs/guide/openapi) generated Automatically and embed Swagger UI endpoint can be started by adding [Swagger plugin](https://github.com/go-kratos/swagger-api).
+- You can define [verification rules](https://go-kratos.dev/en/docs/component/middleware/validate) in Protobuf supported by the HTTP/gRPC service.
+- [Swagger API](https://go-kratos.dev/en/docs/guide/openapi) is generated Automatically and embed Swagger UI endpoint can be started by adding [Swagger plugin](https://github.com/go-kratos/swagger-api).
 
 Kratos is accessible, powerful, and provides tools required for large, robust applications.
 
@@ -39,7 +39,7 @@ Kratos is accessible, powerful, and provides tools required for large, robust ap
 
 Kratos has the most extensive and thorough [documentation](https://go-kratos.dev/en/docs/getting-started/start) and [example](https://github.com/go-kratos/examples) library of all modern web application frameworks, making it a breeze to get started with the framework.
 
-We also provide a [modern template](https://github.com/go-kratos/kratos-layout), This template should help reduce the work required to setup up a modern project.
+We also provide a [modern template](https://github.com/go-kratos/kratos-layout). This template should help reduce the work required to setup up modern projects.
 
 ### Goals
 
@@ -47,13 +47,13 @@ Kratos boosts your productivity. With the integration of excellent resources and
 
 ### Principles
 
-* **Simple**: Appropriate design, plain and easy code.
+* **Simple**: Appropriate design with plain and easy code.
 * **General**: Cover the various utilities for business development.
 * **Highly efficient**: Speeding up the efficiency of businesses upgrading.
-* **Stable**: The base libs validated in the production environment which have the characters of the high testability, high coverage as well as high security and reliability.
+* **Stable**: The base libs validated in the production environment have the characteristics of high testability, high coverage as well as high security and reliability.
 * **Robust**: Eliminating misusing through high quality of the base libs.
 * **High-performance**: Optimal performance excluding the optimization of hacking in case of *unsafe*. 
-* **Expandability**: Properly designed interfaces, you can expand utilities such as base libs to meet your further requirements.
+* **Expandability**: Properly designed interfaces where you can expand utilities such as base libs to meet your further requirements.
 * **Fault-tolerance**: Designed against failure, enhance the understanding and exercising of SRE within Kratos to achieve more robustness.
 * **Toolchain**: Includes an extensive toolchain, such as the code generation of cache, the lint tool, and so forth.
 
diff --git a/cmd/kratos/version.go b/cmd/kratos/version.go
index 50708a15..9d205cbc 100644
--- a/cmd/kratos/version.go
+++ b/cmd/kratos/version.go
@@ -1,4 +1,4 @@
 package main
 
 // release is the current kratos tool version.
-const release = "v2.4.1"
+const release = "v2.5.0"
diff --git a/cmd/protoc-gen-go-errors/version.go b/cmd/protoc-gen-go-errors/version.go
index 14ef3ee0..71fced3e 100644
--- a/cmd/protoc-gen-go-errors/version.go
+++ b/cmd/protoc-gen-go-errors/version.go
@@ -1,4 +1,4 @@
 package main
 
 // release is the current protoc-gen-go-errors version.
-const release = "v2.4.1"
+const release = "v2.5.0"
diff --git a/cmd/protoc-gen-go-http/version.go b/cmd/protoc-gen-go-http/version.go
index 36a83f84..065e7dd5 100644
--- a/cmd/protoc-gen-go-http/version.go
+++ b/cmd/protoc-gen-go-http/version.go
@@ -1,4 +1,4 @@
 package main
 
 // release is the current protoc-gen-go-http version.
-const release = "v2.4.1"
+const release = "v2.5.0"
diff --git a/contrib/config/apollo/apollo.go b/contrib/config/apollo/apollo.go
index a0a38b02..5f868bab 100644
--- a/contrib/config/apollo/apollo.go
+++ b/contrib/config/apollo/apollo.go
@@ -7,7 +7,10 @@ import (
 	"github.com/go-kratos/kratos/v2/log"
 
 	"github.com/apolloconfig/agollo/v4"
+	"github.com/apolloconfig/agollo/v4/constant"
 	apolloConfig "github.com/apolloconfig/agollo/v4/env/config"
+	"github.com/apolloconfig/agollo/v4/extension"
+	"github.com/go-kratos/kratos/v2/encoding"
 )
 
 type apollo struct {
@@ -15,6 +18,13 @@ type apollo struct {
 	opt    *options
 }
 
+const (
+	yaml       = "yaml"
+	yml        = "yml"
+	json       = "json"
+	properties = "properties"
+)
+
 // Option is apollo option
 type Option func(*options)
 
@@ -26,6 +36,7 @@ type options struct {
 	namespace      string
 	isBackupConfig bool
 	backupPath     string
+	originConfig   bool
 }
 
 // WithAppID with apollo config app id
@@ -84,6 +95,16 @@ func WithBackupPath(backupPath string) Option {
 	}
 }
 
+// WithOriginalConfig use the original configuration file without parse processing
+func WithOriginalConfig() Option {
+	return func(o *options) {
+		extension.AddFormatParser(constant.JSON, &jsonExtParser{})
+		extension.AddFormatParser(constant.YAML, &yamlExtParser{})
+		extension.AddFormatParser(constant.YML, &yamlExtParser{})
+		o.originConfig = true
+	}
+}
+
 func NewSource(opts ...Option) config.Source {
 	op := options{}
 	for _, o := range opts {
@@ -108,31 +129,78 @@ func NewSource(opts ...Option) config.Source {
 
 func format(ns string) string {
 	arr := strings.Split(ns, ".")
-	if len(arr) <= 1 {
-		return "json"
+	if len(arr) <= 1 || arr[len(arr)-1] == properties {
+		return json
 	}
-
 	return arr[len(arr)-1]
 }
 
 func (e *apollo) load() []*config.KeyValue {
-	kv := make([]*config.KeyValue, 0)
+	kvs := make([]*config.KeyValue, 0)
 	namespaces := strings.Split(e.opt.namespace, ",")
 
 	for _, ns := range namespaces {
-		value, err := e.client.GetConfigCache(ns).Get("content")
-		if err != nil {
-			log.Warnw("apollo get config failed", "err", err)
+		if !e.opt.originConfig {
+			kv, err := e.getConfig(ns)
+			if err != nil {
+				log.Errorf("apollo get config failed，err:%v", err)
+				continue
+			}
+			kvs = append(kvs, kv)
+			continue
 		}
-		// serialize the namespace content KeyValue into bytes.
-		kv = append(kv, &config.KeyValue{
-			Key:    ns,
-			Value:  []byte(value.(string)),
-			Format: format(ns),
-		})
+		if strings.Contains(ns, ".") && !strings.Contains(ns, properties) &&
+			(format(ns) == yaml || format(ns) == yml || format(ns) == json) {
+			kv, err := e.getOriginConfig(ns)
+			if err != nil {
+				log.Errorf("apollo get config failed，err:%v", err)
+				continue
+			}
+			kvs = append(kvs, kv)
+			continue
+		} else {
+			kv, err := e.getConfig(ns)
+			if err != nil {
+				log.Errorf("apollo get config failed，err:%v", err)
+				continue
+			}
+			kvs = append(kvs, kv)
+		}
+	}
+	return kvs
+}
+
+func (e *apollo) getConfig(ns string) (*config.KeyValue, error) {
+	next := map[string]interface{}{}
+	e.client.GetConfigCache(ns).Range(func(key, value interface{}) bool {
+		// all values are out properties format
+		resolve(genKey(ns, key.(string)), value, next)
+		return true
+	})
+	f := format(ns)
+	codec := encoding.GetCodec(f)
+	val, err := codec.Marshal(next)
+	if err != nil {
+		return nil, err
 	}
+	return &config.KeyValue{
+		Key:    ns,
+		Value:  val,
+		Format: f,
+	}, nil
+}
 
-	return kv
+func (e apollo) getOriginConfig(ns string) (*config.KeyValue, error) {
+	value, err := e.client.GetConfigCache(ns).Get("content")
+	if err != nil {
+		return nil, err
+	}
+	// serialize the namespace content KeyValue into bytes.
+	return &config.KeyValue{
+		Key:    ns,
+		Value:  []byte(value.(string)),
+		Format: format(ns),
+	}, nil
 }
 
 func (e *apollo) Load() (kv []*config.KeyValue, err error) {
@@ -146,3 +214,54 @@ func (e *apollo) Watch() (config.Watcher, error) {
 	}
 	return w, nil
 }
+
+// resolve convert kv pair into one map[string]interface{} by split key into different
+// map level. such as: app.name = "application" => map[app][name] = "application"
+func resolve(key string, value interface{}, target map[string]interface{}) {
+	// expand key "aaa.bbb" into map[aaa]map[bbb]interface{}
+	keys := strings.Split(key, ".")
+	last := len(keys) - 1
+	cursor := target
+
+	for i, k := range keys {
+		if i == last {
+			cursor[k] = value
+			break
+		}
+
+		// not the last key, be deeper
+		v, ok := cursor[k]
+		if !ok {
+			// create a new map
+			deeper := make(map[string]interface{})
+			cursor[k] = deeper
+			cursor = deeper
+			continue
+		}
+
+		// current exists, then check existing value type, if it's not map
+		// that means duplicate keys, and at least one is not map instance.
+		if cursor, ok = v.(map[string]interface{}); !ok {
+			log.Warnf("duplicate key: %v\n", strings.Join(keys[:i+1], "."))
+			break
+		}
+	}
+}
+
+// genKey got the key of config.KeyValue pair.
+// eg: namespace.ext with subKey got namespace.subKey
+func genKey(ns, sub string) string {
+	arr := strings.Split(ns, ".")
+	if len(arr) < 1 {
+		return sub
+	}
+
+	if len(arr) == 1 {
+		if ns == "" {
+			return sub
+		}
+		return ns + "." + sub
+	}
+
+	return strings.Join(arr[:len(arr)-1], ".") + "." + sub
+}
diff --git a/contrib/config/apollo/parser.go b/contrib/config/apollo/parser.go
index a449256f..2b1abb99 100644
--- a/contrib/config/apollo/parser.go
+++ b/contrib/config/apollo/parser.go
@@ -1,10 +1,5 @@
 package apollo
 
-import (
-	"github.com/apolloconfig/agollo/v4/constant"
-	"github.com/apolloconfig/agollo/v4/extension"
-)
-
 type jsonExtParser struct{}
 
 func (parser jsonExtParser) Parse(configContent interface{}) (map[string]interface{}, error) {
@@ -13,13 +8,6 @@ func (parser jsonExtParser) Parse(configContent interface{}) (map[string]interfa
 
 type yamlExtParser struct{}
 
-func (parser yamlExtParser) Parse(configContent interface{}) (out map[string]interface{}, err error) {
+func (parser yamlExtParser) Parse(configContent interface{}) (map[string]interface{}, error) {
 	return map[string]interface{}{"content": configContent}, nil
 }
-
-func init() {
-	// add json/yaml/yml format
-	extension.AddFormatParser(constant.JSON, &jsonExtParser{})
-	extension.AddFormatParser(constant.YAML, &yamlExtParser{})
-	extension.AddFormatParser(constant.YML, &yamlExtParser{})
-}
diff --git a/contrib/config/apollo/watcher.go b/contrib/config/apollo/watcher.go
index 8c039005..670139a2 100644
--- a/contrib/config/apollo/watcher.go
+++ b/contrib/config/apollo/watcher.go
@@ -2,6 +2,9 @@ package apollo
 
 import (
 	"context"
+	"strings"
+
+	"github.com/go-kratos/kratos/v2/encoding"
 
 	"github.com/go-kratos/kratos/v2/config"
 	"github.com/go-kratos/kratos/v2/log"
@@ -23,14 +26,38 @@ type customChangeListener struct {
 
 func (c *customChangeListener) onChange(namespace string, changes map[string]*storage.ConfigChange) []*config.KeyValue {
 	kv := make([]*config.KeyValue, 0, 2)
-	value, err := c.apollo.client.GetConfigCache(namespace).Get("content")
+	if strings.Contains(namespace, ".") && !strings.Contains(namespace, properties) &&
+		(format(namespace) == yaml || format(namespace) == yml || format(namespace) == json) {
+		value, err := c.apollo.client.GetConfigCache(namespace).Get("content")
+		if err != nil {
+			log.Warnw("apollo get config failed", "err", err)
+		}
+		kv = append(kv, &config.KeyValue{
+			Key:    namespace,
+			Value:  []byte(value.(string)),
+			Format: format(namespace),
+		})
+
+		return kv
+	}
+
+	next := make(map[string]interface{})
+
+	for key, change := range changes {
+		resolve(genKey(namespace, key), change.NewValue, next)
+	}
+
+	f := format(namespace)
+	codec := encoding.GetCodec(f)
+	val, err := codec.Marshal(next)
 	if err != nil {
-		log.Warnw("apollo get config failed", "err", err)
+		log.Warnf("apollo could not handle namespace %s: %v", namespace, err)
+		return nil
 	}
 	kv = append(kv, &config.KeyValue{
 		Key:    namespace,
-		Value:  []byte(value.(string)),
-		Format: format(namespace),
+		Value:  val,
+		Format: f,
 	})
 
 	return kv
diff --git a/contrib/encoding/msgpack/go.mod b/contrib/encoding/msgpack/go.mod
index 9e02ad81..5d78c21b 100644
--- a/contrib/encoding/msgpack/go.mod
+++ b/contrib/encoding/msgpack/go.mod
@@ -3,7 +3,7 @@ module github.com/go-kratos/kratos/contrib/encoding/msgpack/v2
 go 1.16
 
 require (
-	github.com/go-kratos/kratos/v2 v2.4.1
+	github.com/go-kratos/kratos/v2 v2.5.0
 	github.com/vmihailenco/msgpack/v5 v5.3.5
 )
 
diff --git a/contrib/log/aliyun/aliyun.go b/contrib/log/aliyun/aliyun.go
index 0c1e1987..53fdefde 100644
--- a/contrib/log/aliyun/aliyun.go
+++ b/contrib/log/aliyun/aliyun.go
@@ -7,13 +7,13 @@ import (
 
 	sls "github.com/aliyun/aliyun-log-go-sdk"
 	"github.com/aliyun/aliyun-log-go-sdk/producer"
-	klog "github.com/go-kratos/kratos/v2/log"
+	log "github.com/go-kratos/kratos/v2/log"
 	"google.golang.org/protobuf/proto"
 )
 
 // Log see more detail https://github.com/aliyun/aliyun-log-go-sdk
-type Log interface {
-	klog.Logger
+type Logger interface {
+	log.Logger
 	GetProducer() *producer.Producer
 	Close() error
 }
@@ -78,7 +78,7 @@ func (a *aliyunLog) Close() error {
 	return a.producer.Close(5000)
 }
 
-func (a *aliyunLog) Log(level klog.Level, keyvals ...interface{}) error {
+func (a *aliyunLog) Log(level log.Level, keyvals ...interface{}) error {
 	buf := level.String()
 	levelTitle := "level"
 
@@ -108,7 +108,7 @@ func (a *aliyunLog) Log(level klog.Level, keyvals ...interface{}) error {
 }
 
 // NewAliyunLog new a aliyun logger with options.
-func NewAliyunLog(options ...Option) Log {
+func NewAliyunLog(options ...Option) Logger {
 	opts := defaultOptions()
 	for _, o := range options {
 		o(opts)
diff --git a/contrib/log/aliyun/go.mod b/contrib/log/aliyun/go.mod
index 83b139ea..efe1b827 100644
--- a/contrib/log/aliyun/go.mod
+++ b/contrib/log/aliyun/go.mod
@@ -4,7 +4,7 @@ go 1.16
 
 require (
 	github.com/aliyun/aliyun-log-go-sdk v0.1.37
-	github.com/go-kratos/kratos/v2 v2.4.1
+	github.com/go-kratos/kratos/v2 v2.5.0
 	google.golang.org/protobuf v1.28.1
 )
 
diff --git a/contrib/log/fluent/go.mod b/contrib/log/fluent/go.mod
index b8477bde..b316425a 100644
--- a/contrib/log/fluent/go.mod
+++ b/contrib/log/fluent/go.mod
@@ -5,7 +5,7 @@ go 1.16
 require (
 	github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
 	github.com/fluent/fluent-logger-golang v1.9.0
-	github.com/go-kratos/kratos/v2 v2.4.1
+	github.com/go-kratos/kratos/v2 v2.5.0
 	github.com/kr/pretty v0.3.0 // indirect
 	github.com/tinylib/msgp v1.1.6 // indirect
 )
diff --git a/contrib/log/logrus/go.mod b/contrib/log/logrus/go.mod
index fa3989f3..aa1f5946 100644
--- a/contrib/log/logrus/go.mod
+++ b/contrib/log/logrus/go.mod
@@ -3,7 +3,7 @@ module github.com/go-kratos/kratos/contrib/log/logrus/v2
 go 1.16
 
 require (
-	github.com/go-kratos/kratos/v2 v2.4.1
+	github.com/go-kratos/kratos/v2 v2.5.0
 	github.com/sirupsen/logrus v1.8.1
 	github.com/stretchr/testify v1.7.1
 )
diff --git a/contrib/log/tencent/go.mod b/contrib/log/tencent/go.mod
index feb39f17..2d3fa7dc 100644
--- a/contrib/log/tencent/go.mod
+++ b/contrib/log/tencent/go.mod
@@ -3,7 +3,9 @@ module github.com/go-kratos/kratos/contrib/log/tencent/v2
 go 1.16
 
 require (
-	github.com/go-kratos/kratos/v2 v2.4.1
+	github.com/go-kratos/kratos/v2 v2.5.0
 	github.com/tencentcloud/tencentcloud-cls-sdk-go v1.0.2
 	google.golang.org/protobuf v1.28.0
 )
+
+replace github.com/go-kratos/kratos/v2 => ../../../
diff --git a/contrib/log/tencent/go.sum b/contrib/log/tencent/go.sum
index b4b87442..993b044f 100644
--- a/contrib/log/tencent/go.sum
+++ b/contrib/log/tencent/go.sum
@@ -27,8 +27,6 @@ github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09
 github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/go-kratos/aegis v0.1.2/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw=
-github.com/go-kratos/kratos/v2 v2.4.1 h1:NFQy8Ha4Xu6T3Q40JlKzspvlMa5IGvIHhJw5+sqyV4c=
-github.com/go-kratos/kratos/v2 v2.4.1/go.mod h1:5acyLj4EgY428AJnZl2EwCrMV1OVlttQFBum+SghMiA=
 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
diff --git a/contrib/log/zap/go.mod b/contrib/log/zap/go.mod
index 8d5d2c7e..ecb315e1 100644
--- a/contrib/log/zap/go.mod
+++ b/contrib/log/zap/go.mod
@@ -3,7 +3,7 @@ module github.com/go-kratos/kratos/contrib/log/zap/v2
 go 1.16
 
 require (
-	github.com/go-kratos/kratos/v2 v2.4.1
+	github.com/go-kratos/kratos/v2 v2.5.0
 	go.uber.org/zap v1.21.0
 )
 
diff --git a/contrib/metrics/datadog/go.mod b/contrib/metrics/datadog/go.mod
index 4d06c42c..206ccf4e 100644
--- a/contrib/metrics/datadog/go.mod
+++ b/contrib/metrics/datadog/go.mod
@@ -5,7 +5,7 @@ go 1.16
 require (
 	github.com/DataDog/datadog-go v4.8.3+incompatible
 	github.com/Microsoft/go-winio v0.5.2 // indirect
-	github.com/go-kratos/kratos/v2 v2.4.1
+	github.com/go-kratos/kratos/v2 v2.5.0
 )
 
 replace github.com/go-kratos/kratos/v2 => ../../../
diff --git a/contrib/metrics/prometheus/go.mod b/contrib/metrics/prometheus/go.mod
index ff2f8af2..c6a1b5df 100644
--- a/contrib/metrics/prometheus/go.mod
+++ b/contrib/metrics/prometheus/go.mod
@@ -3,7 +3,7 @@ module github.com/go-kratos/kratos/contrib/metrics/prometheus/v2
 go 1.16
 
 require (
-	github.com/go-kratos/kratos/v2 v2.4.1
+	github.com/go-kratos/kratos/v2 v2.5.0
 	github.com/prometheus/client_golang v1.12.2
 	github.com/prometheus/common v0.37.0
 )
diff --git a/contrib/opensergo/go.mod b/contrib/opensergo/go.mod
index fa64c2ca..beba8f7f 100644
--- a/contrib/opensergo/go.mod
+++ b/contrib/opensergo/go.mod
@@ -3,7 +3,7 @@ module github.com/go-kratos/kratos/contrib/opensergo/v2
 go 1.17
 
 require (
-	github.com/go-kratos/kratos/v2 v2.4.1
+	github.com/go-kratos/kratos/v2 v2.5.0
 	github.com/opensergo/opensergo-go v0.0.0-20220331070310-e5b01fee4d1c
 	golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2
 	google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd
diff --git a/contrib/registry/consul/go.mod b/contrib/registry/consul/go.mod
index 03ed997f..368308ec 100644
--- a/contrib/registry/consul/go.mod
+++ b/contrib/registry/consul/go.mod
@@ -3,7 +3,7 @@ module github.com/go-kratos/kratos/contrib/registry/consul/v2
 go 1.16
 
 require (
-	github.com/go-kratos/kratos/v2 v2.4.1
+	github.com/go-kratos/kratos/v2 v2.5.0
 	github.com/hashicorp/consul/api v1.13.1
 )
 
diff --git a/contrib/registry/discovery/go.mod b/contrib/registry/discovery/go.mod
index f03c2f15..407af47a 100644
--- a/contrib/registry/discovery/go.mod
+++ b/contrib/registry/discovery/go.mod
@@ -3,7 +3,7 @@ module github.com/go-kratos/kratos/contrib/registry/discovery/v2
 go 1.16
 
 require (
-	github.com/go-kratos/kratos/v2 v2.4.1
+	github.com/go-kratos/kratos/v2 v2.5.0
 	github.com/go-resty/resty/v2 v2.7.0
 	github.com/pkg/errors v0.9.1
 )
diff --git a/contrib/registry/etcd/go.mod b/contrib/registry/etcd/go.mod
index 6634eac7..952e8a55 100644
--- a/contrib/registry/etcd/go.mod
+++ b/contrib/registry/etcd/go.mod
@@ -3,7 +3,7 @@ module github.com/go-kratos/kratos/contrib/registry/etcd/v2
 go 1.16
 
 require (
-	github.com/go-kratos/kratos/v2 v2.4.1
+	github.com/go-kratos/kratos/v2 v2.5.0
 	go.etcd.io/etcd/client/v3 v3.5.4
 	google.golang.org/grpc v1.46.2
 )
diff --git a/contrib/registry/kubernetes/go.mod b/contrib/registry/kubernetes/go.mod
index 67dd4504..cad07df0 100644
--- a/contrib/registry/kubernetes/go.mod
+++ b/contrib/registry/kubernetes/go.mod
@@ -3,7 +3,7 @@ module github.com/go-kratos/kratos/contrib/registry/kubernetes/v2
 go 1.16
 
 require (
-	github.com/go-kratos/kratos/v2 v2.4.1
+	github.com/go-kratos/kratos/v2 v2.5.0
 	github.com/json-iterator/go v1.1.12
 	k8s.io/api v0.24.3
 	k8s.io/apimachinery v0.24.3
diff --git a/contrib/registry/nacos/go.mod b/contrib/registry/nacos/go.mod
index a4665a10..bdfd037e 100644
--- a/contrib/registry/nacos/go.mod
+++ b/contrib/registry/nacos/go.mod
@@ -3,7 +3,7 @@ module github.com/go-kratos/kratos/contrib/registry/nacos/v2
 go 1.16
 
 require (
-	github.com/go-kratos/kratos/v2 v2.4.1
+	github.com/go-kratos/kratos/v2 v2.5.0
 	github.com/nacos-group/nacos-sdk-go v1.0.9
 )
 
diff --git a/contrib/registry/polaris/go.mod b/contrib/registry/polaris/go.mod
index e399d9dd..2789a991 100644
--- a/contrib/registry/polaris/go.mod
+++ b/contrib/registry/polaris/go.mod
@@ -3,7 +3,7 @@ module github.com/go-kratos/kratos/contrib/registry/polaris/v2
 go 1.16
 
 require (
-	github.com/go-kratos/kratos/v2 v2.4.1
+	github.com/go-kratos/kratos/v2 v2.5.0
 	github.com/polarismesh/polaris-go v1.1.0
 )
 
diff --git a/contrib/registry/zookeeper/go.mod b/contrib/registry/zookeeper/go.mod
index d8d58818..a149d20a 100644
--- a/contrib/registry/zookeeper/go.mod
+++ b/contrib/registry/zookeeper/go.mod
@@ -3,7 +3,7 @@ module github.com/go-kratos/kratos/contrib/registry/zookeeper/v2
 go 1.16
 
 require (
-	github.com/go-kratos/kratos/v2 v2.4.1
+	github.com/go-kratos/kratos/v2 v2.5.0
 	github.com/go-zookeeper/zk v1.0.3
 	golang.org/x/sync v0.0.0-20220513210516-0976fa681c29
 )
diff --git a/encoding/form/proto_decode.go b/encoding/form/proto_decode.go
index 9e31e3ba..2856bef5 100644
--- a/encoding/form/proto_decode.go
+++ b/encoding/form/proto_decode.go
@@ -12,11 +12,11 @@ import (
 	"google.golang.org/protobuf/encoding/protojson"
 	"google.golang.org/protobuf/types/known/structpb"
 
-	"google.golang.org/genproto/protobuf/field_mask"
 	"google.golang.org/protobuf/proto"
 	"google.golang.org/protobuf/reflect/protoreflect"
 	"google.golang.org/protobuf/reflect/protoregistry"
 	"google.golang.org/protobuf/types/known/durationpb"
+	"google.golang.org/protobuf/types/known/fieldmaskpb"
 	"google.golang.org/protobuf/types/known/timestamppb"
 	"google.golang.org/protobuf/types/known/wrapperspb"
 )
@@ -289,7 +289,7 @@ func parseMessage(md protoreflect.MessageDescriptor, value string) (protoreflect
 		}
 		msg = wrapperspb.Bytes(v)
 	case "google.protobuf.FieldMask":
-		fm := &field_mask.FieldMask{}
+		fm := &fieldmaskpb.FieldMask{}
 		for _, fv := range strings.Split(value, ",") {
 			fm.Paths = append(fm.Paths, jsonSnakeCase(fv))
 		}
diff --git a/internal/endpoint/endpoint.go b/internal/endpoint/endpoint.go
index e4c9ef96..160b694f 100644
--- a/internal/endpoint/endpoint.go
+++ b/internal/endpoint/endpoint.go
@@ -2,7 +2,6 @@ package endpoint
 
 import (
 	"net/url"
-	"strconv"
 )
 
 // NewEndpoint new an Endpoint URL.
@@ -18,10 +17,6 @@ func ParseEndpoint(endpoints []string, scheme string) (string, error) {
 			return "", err
 		}
 
-		// TODO: Compatibility processing
-		// Function is to convert grpc:/127.0.0.1/?isSecure=true into grpcs:/127.0.0.1/
-		// It will be deleted in about a month
-		u = legacyURLToNew(u)
 		if u.Scheme == scheme {
 			return u.Host, nil
 		}
@@ -29,27 +24,6 @@ func ParseEndpoint(endpoints []string, scheme string) (string, error) {
 	return "", nil
 }
 
-func legacyURLToNew(u *url.URL) *url.URL {
-	if u.Scheme == "https" || u.Scheme == "grpcs" {
-		return u
-	}
-	if IsSecure(u) {
-		return &url.URL{Scheme: u.Scheme + "s", Host: u.Host}
-	}
-	return u
-}
-
-// IsSecure parses isSecure for Endpoint URL.
-// Note: It will be deleted after some time,
-// unified use grpcs://127.0.0.1:8080 instead of grpc://127.0.0.1:8080?isSecure=true
-func IsSecure(u *url.URL) bool {
-	ok, err := strconv.ParseBool(u.Query().Get("isSecure"))
-	if err != nil {
-		return false
-	}
-	return ok
-}
-
 // Scheme is the scheme of endpoint url.
 // examples: scheme="http",isSecure=true get "https"
 func Scheme(scheme string, isSecure bool) string {
diff --git a/log/global.go b/log/global.go
index 624fccb3..f1984fc3 100644
--- a/log/global.go
+++ b/log/global.go
@@ -27,10 +27,6 @@ func (a *loggerAppliance) SetLogger(in Logger) {
 	a.Logger = in
 }
 
-func (a *loggerAppliance) Log(level Level, keyvals ...interface{}) error {
-	return a.Logger.Log(level, keyvals...)
-}
-
 // SetLogger should be called before any other log call.
 // And it is NOT THREAD SAFE.
 func SetLogger(logger Logger) {
diff --git a/selector/default_selector.go b/selector/default_selector.go
index 0e99249e..247af2e9 100644
--- a/selector/default_selector.go
+++ b/selector/default_selector.go
@@ -9,7 +9,6 @@ import (
 type Default struct {
 	NodeBuilder WeightedNodeBuilder
 	Balancer    Balancer
-	Filters     []Filter
 
 	nodes atomic.Value
 }
@@ -27,16 +26,13 @@ func (d *Default) Select(ctx context.Context, opts ...SelectOption) (selected No
 	for _, o := range opts {
 		o(&options)
 	}
-	if len(d.Filters) > 0 || len(options.Filters) > 0 {
+	if len(options.NodeFilters) > 0 {
 		newNodes := make([]Node, len(nodes))
 		for i, wc := range nodes {
 			newNodes[i] = wc
 		}
-		for _, f := range d.Filters {
-			newNodes = f(ctx, newNodes)
-		}
-		for _, f := range options.Filters {
-			newNodes = f(ctx, newNodes)
+		for _, filter := range options.NodeFilters {
+			newNodes = filter(ctx, newNodes)
 		}
 		candidates = make([]WeightedNode, len(newNodes))
 		for i, n := range newNodes {
@@ -74,7 +70,6 @@ func (d *Default) Apply(nodes []Node) {
 type DefaultBuilder struct {
 	Node     WeightedNodeBuilder
 	Balancer BalancerBuilder
-	Filters  []Filter
 }
 
 // Build create builder
@@ -82,6 +77,5 @@ func (db *DefaultBuilder) Build() Selector {
 	return &Default{
 		NodeBuilder: db.Node,
 		Balancer:    db.Balancer.Build(),
-		Filters:     db.Filters,
 	}
 }
diff --git a/selector/filter.go b/selector/filter.go
index e88a52da..c8b630d2 100644
--- a/selector/filter.go
+++ b/selector/filter.go
@@ -2,5 +2,5 @@ package selector
 
 import "context"
 
-// Filter is select filter.
-type Filter func(context.Context, []Node) []Node
+// NodeFilter is select filter.
+type NodeFilter func(context.Context, []Node) []Node
diff --git a/selector/filter/version.go b/selector/filter/version.go
index 815d3fb7..41d56fbd 100644
--- a/selector/filter/version.go
+++ b/selector/filter/version.go
@@ -7,7 +7,7 @@ import (
 )
 
 // Version is version filter.
-func Version(version string) selector.Filter {
+func Version(version string) selector.NodeFilter {
 	return func(_ context.Context, nodes []selector.Node) []selector.Node {
 		newNodes := nodes[:0]
 		for _, n := range nodes {
diff --git a/selector/global.go b/selector/global.go
new file mode 100644
index 00000000..2f951212
--- /dev/null
+++ b/selector/global.go
@@ -0,0 +1,13 @@
+package selector
+
+var globalSelector Builder
+
+// GlobalSelector returns global selector builder.
+func GlobalSelector() Builder {
+	return globalSelector
+}
+
+// SetGlobalSelector set global selector builder.
+func SetGlobalSelector(builder Builder) {
+	globalSelector = builder
+}
diff --git a/selector/options.go b/selector/options.go
index c9129fb3..14b441d7 100644
--- a/selector/options.go
+++ b/selector/options.go
@@ -2,15 +2,15 @@ package selector
 
 // SelectOptions is Select Options.
 type SelectOptions struct {
-	Filters []Filter
+	NodeFilters []NodeFilter
 }
 
 // SelectOption is Selector option.
 type SelectOption func(*SelectOptions)
 
-// WithFilter with filter options
-func WithFilter(fn ...Filter) SelectOption {
+// WithNodeFilter with filter options
+func WithNodeFilter(fn ...NodeFilter) SelectOption {
 	return func(opts *SelectOptions) {
-		opts.Filters = fn
+		opts.NodeFilters = fn
 	}
 }
diff --git a/selector/p2c/p2c.go b/selector/p2c/p2c.go
index 21a2199b..6f8d5a3f 100644
--- a/selector/p2c/p2c.go
+++ b/selector/p2c/p2c.go
@@ -19,20 +19,11 @@ const (
 
 var _ selector.Balancer = &Balancer{}
 
-// WithFilter with select filters
-func WithFilter(filters ...selector.Filter) Option {
-	return func(o *options) {
-		o.filters = filters
-	}
-}
-
 // Option is random builder option.
 type Option func(o *options)
 
 // options is random builder options
-type options struct {
-	filters []selector.Filter
-}
+type options struct{}
 
 // New creates a p2c selector.
 func New(opts ...Option) selector.Selector {
@@ -95,7 +86,6 @@ func NewBuilder(opts ...Option) selector.Builder {
 		opt(&option)
 	}
 	return &selector.DefaultBuilder{
-		Filters:  option.filters,
 		Balancer: &Builder{},
 		Node:     &ewma.Builder{},
 	}
diff --git a/selector/random/random.go b/selector/random/random.go
index 824fb883..cf59534d 100644
--- a/selector/random/random.go
+++ b/selector/random/random.go
@@ -15,20 +15,11 @@ const (
 
 var _ selector.Balancer = &Balancer{} // Name is balancer name
 
-// WithFilter with select filters
-func WithFilter(filters ...selector.Filter) Option {
-	return func(o *options) {
-		o.filters = filters
-	}
-}
-
 // Option is random builder option.
 type Option func(o *options)
 
 // options is random builder options
-type options struct {
-	filters []selector.Filter
-}
+type options struct{}
 
 // Balancer is a random balancer.
 type Balancer struct{}
@@ -56,7 +47,6 @@ func NewBuilder(opts ...Option) selector.Builder {
 		opt(&option)
 	}
 	return &selector.DefaultBuilder{
-		Filters:  option.filters,
 		Balancer: &Builder{},
 		Node:     &direct.Builder{},
 	}
diff --git a/selector/selector.go b/selector/selector.go
index 79ecdf78..e346f6c3 100644
--- a/selector/selector.go
+++ b/selector/selector.go
@@ -57,7 +57,7 @@ type DoneInfo struct {
 	// Response Error
 	Err error
 	// Response Metadata
-	ReplyMeta ReplyMeta
+	ReplyMD ReplyMD
 
 	// BytesSent indicates if any bytes have been sent to the server.
 	BytesSent bool
@@ -65,8 +65,8 @@ type DoneInfo struct {
 	BytesReceived bool
 }
 
-// ReplyMeta is Reply Metadata.
-type ReplyMeta interface {
+// ReplyMD is Reply Metadata.
+type ReplyMD interface {
 	Get(key string) string
 }
 
diff --git a/selector/wrr/wrr.go b/selector/wrr/wrr.go
index c3405915..0b95e04f 100644
--- a/selector/wrr/wrr.go
+++ b/selector/wrr/wrr.go
@@ -15,20 +15,11 @@ const (
 
 var _ selector.Balancer = &Balancer{} // Name is balancer name
 
-// WithFilter with select filters
-func WithFilter(filters ...selector.Filter) Option {
-	return func(o *options) {
-		o.filters = filters
-	}
-}
-
 // Option is random builder option.
 type Option func(o *options)
 
 // options is random builder options
-type options struct {
-	filters []selector.Filter
-}
+type options struct{}
 
 // Balancer is a random balancer.
 type Balancer struct {
@@ -77,7 +68,6 @@ func NewBuilder(opts ...Option) selector.Builder {
 		opt(&option)
 	}
 	return &selector.DefaultBuilder{
-		Filters:  option.filters,
 		Balancer: &Builder{},
 		Node:     &direct.Builder{},
 	}
diff --git a/transport/grpc/balancer.go b/transport/grpc/balancer.go
index 2a27c05a..bd50f098 100644
--- a/transport/grpc/balancer.go
+++ b/transport/grpc/balancer.go
@@ -1,59 +1,45 @@
 package grpc
 
 import (
-	"sync"
-
 	"github.com/go-kratos/kratos/v2/registry"
 	"github.com/go-kratos/kratos/v2/selector"
-	"github.com/go-kratos/kratos/v2/selector/p2c"
-	"github.com/go-kratos/kratos/v2/selector/random"
-	"github.com/go-kratos/kratos/v2/selector/wrr"
 	"github.com/go-kratos/kratos/v2/transport"
 
-	gBalancer "google.golang.org/grpc/balancer"
+	"google.golang.org/grpc/balancer"
 	"google.golang.org/grpc/balancer/base"
 	"google.golang.org/grpc/metadata"
 )
 
-var (
-	_ base.PickerBuilder = &Builder{}
-	_ gBalancer.Picker   = &Picker{}
+const (
+	balancerName = "selector"
+)
 
-	mu sync.Mutex
+var (
+	_ base.PickerBuilder = &balancerBuilder{}
+	_ balancer.Picker    = &balancerPicker{}
 )
 
 func init() {
-	// inject global grpc balancer
-	SetGlobalBalancer(random.Name, random.NewBuilder())
-	SetGlobalBalancer(wrr.Name, wrr.NewBuilder())
-	SetGlobalBalancer(p2c.Name, p2c.NewBuilder())
-}
-
-// SetGlobalBalancer set grpc balancer with scheme.
-func SetGlobalBalancer(scheme string, builder selector.Builder) {
-	mu.Lock()
-	defer mu.Unlock()
-
 	b := base.NewBalancerBuilder(
-		scheme,
-		&Builder{builder: builder},
+		balancerName,
+		&balancerBuilder{
+			builder: selector.GlobalSelector(),
+		},
 		base.Config{HealthCheck: true},
 	)
-	gBalancer.Register(b)
+	balancer.Register(b)
 }
 
-// Builder is grpc balancer builder.
-type Builder struct {
+type balancerBuilder struct {
 	builder selector.Builder
 }
 
 // Build creates a grpc Picker.
-func (b *Builder) Build(info base.PickerBuildInfo) gBalancer.Picker {
+func (b *balancerBuilder) Build(info base.PickerBuildInfo) balancer.Picker {
 	if len(info.ReadySCs) == 0 {
 		// Block the RPC until a new picker is available via UpdateState().
-		return base.NewErrPicker(gBalancer.ErrNoSubConnAvailable)
+		return base.NewErrPicker(balancer.ErrNoSubConnAvailable)
 	}
-
 	nodes := make([]selector.Node, 0)
 	for conn, info := range info.ReadySCs {
 		ins, _ := info.Address.Attributes.Value("rawServiceInstance").(*registry.ServiceInstance)
@@ -62,40 +48,40 @@ func (b *Builder) Build(info base.PickerBuildInfo) gBalancer.Picker {
 			subConn: conn,
 		})
 	}
-	p := &Picker{
+	p := &balancerPicker{
 		selector: b.builder.Build(),
 	}
 	p.selector.Apply(nodes)
 	return p
 }
 
-// Picker is a grpc picker.
-type Picker struct {
+// balancerPicker is a grpc picker.
+type balancerPicker struct {
 	selector selector.Selector
 }
 
 // Pick pick instances.
-func (p *Picker) Pick(info gBalancer.PickInfo) (gBalancer.PickResult, error) {
-	var filters []selector.Filter
+func (p *balancerPicker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
+	var filters []selector.NodeFilter
 	if tr, ok := transport.FromClientContext(info.Ctx); ok {
 		if gtr, ok := tr.(*Transport); ok {
-			filters = gtr.SelectFilters()
+			filters = gtr.NodeFilters()
 		}
 	}
 
-	n, done, err := p.selector.Select(info.Ctx, selector.WithFilter(filters...))
+	n, done, err := p.selector.Select(info.Ctx, selector.WithNodeFilter(filters...))
 	if err != nil {
-		return gBalancer.PickResult{}, err
+		return balancer.PickResult{}, err
 	}
 
-	return gBalancer.PickResult{
+	return balancer.PickResult{
 		SubConn: n.(*grpcNode).subConn,
-		Done: func(di gBalancer.DoneInfo) {
+		Done: func(di balancer.DoneInfo) {
 			done(info.Ctx, selector.DoneInfo{
 				Err:           di.Err,
 				BytesSent:     di.BytesSent,
 				BytesReceived: di.BytesReceived,
-				ReplyMeta:     Trailer(di.Trailer),
+				ReplyMD:       Trailer(di.Trailer),
 			})
 		},
 	}, nil
@@ -115,5 +101,5 @@ func (t Trailer) Get(k string) string {
 
 type grpcNode struct {
 	selector.Node
-	subConn gBalancer.SubConn
+	subConn balancer.SubConn
 }
diff --git a/transport/grpc/client.go b/transport/grpc/client.go
index 923e5267..a1424d20 100644
--- a/transport/grpc/client.go
+++ b/transport/grpc/client.go
@@ -10,7 +10,7 @@ import (
 	"github.com/go-kratos/kratos/v2/middleware"
 	"github.com/go-kratos/kratos/v2/registry"
 	"github.com/go-kratos/kratos/v2/selector"
-	"github.com/go-kratos/kratos/v2/selector/wrr"
+	"github.com/go-kratos/kratos/v2/selector/p2c"
 	"github.com/go-kratos/kratos/v2/transport"
 	"github.com/go-kratos/kratos/v2/transport/grpc/resolver/discovery"
 
@@ -23,6 +23,12 @@ import (
 	grpcmd "google.golang.org/grpc/metadata"
 )
 
+func init() {
+	if selector.GlobalSelector() == nil {
+		selector.SetGlobalSelector(p2c.NewBuilder())
+	}
+}
+
 // ClientOption is gRPC client option.
 type ClientOption func(o *clientOptions)
 
@@ -75,15 +81,8 @@ func WithOptions(opts ...grpc.DialOption) ClientOption {
 	}
 }
 
-// WithBalancerName with balancer name
-func WithBalancerName(name string) ClientOption {
-	return func(o *clientOptions) {
-		o.balancerName = name
-	}
-}
-
-// WithFilter with select filters
-func WithFilter(filters ...selector.Filter) ClientOption {
+// WithNodeFilter with select filters
+func WithNodeFilter(filters ...selector.NodeFilter) ClientOption {
 	return func(o *clientOptions) {
 		o.filters = filters
 	}
@@ -105,7 +104,7 @@ type clientOptions struct {
 	ints         []grpc.UnaryClientInterceptor
 	grpcOpts     []grpc.DialOption
 	balancerName string
-	filters      []selector.Filter
+	filters      []selector.NodeFilter
 }
 
 // Dial returns a GRPC connection.
@@ -121,7 +120,7 @@ func DialInsecure(ctx context.Context, opts ...ClientOption) (*grpc.ClientConn,
 func dial(ctx context.Context, insecure bool, opts ...ClientOption) (*grpc.ClientConn, error) {
 	options := clientOptions{
 		timeout:      2000 * time.Millisecond,
-		balancerName: wrr.Name,
+		balancerName: balancerName,
 	}
 	for _, o := range opts {
 		o(&options)
@@ -156,13 +155,13 @@ func dial(ctx context.Context, insecure bool, opts ...ClientOption) (*grpc.Clien
 	return grpc.DialContext(ctx, options.endpoint, grpcOpts...)
 }
 
-func unaryClientInterceptor(ms []middleware.Middleware, timeout time.Duration, filters []selector.Filter) grpc.UnaryClientInterceptor {
+func unaryClientInterceptor(ms []middleware.Middleware, timeout time.Duration, filters []selector.NodeFilter) grpc.UnaryClientInterceptor {
 	return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
 		ctx = transport.NewClientContext(ctx, &Transport{
-			endpoint:  cc.Target(),
-			operation: method,
-			reqHeader: headerCarrier{},
-			filters:   filters,
+			endpoint:    cc.Target(),
+			operation:   method,
+			reqHeader:   headerCarrier{},
+			nodeFilters: filters,
 		})
 		if timeout > 0 {
 			var cancel context.CancelFunc
diff --git a/transport/grpc/transport.go b/transport/grpc/transport.go
index efa70e74..a11a271a 100644
--- a/transport/grpc/transport.go
+++ b/transport/grpc/transport.go
@@ -14,7 +14,7 @@ type Transport struct {
 	operation   string
 	reqHeader   headerCarrier
 	replyHeader headerCarrier
-	filters     []selector.Filter
+	nodeFilters []selector.NodeFilter
 }
 
 // Kind returns the transport kind.
@@ -42,9 +42,9 @@ func (tr *Transport) ReplyHeader() transport.Header {
 	return tr.replyHeader
 }
 
-// SelectFilters returns the client select filters.
-func (tr *Transport) SelectFilters() []selector.Filter {
-	return tr.filters
+// NodeFilters returns the client select filters.
+func (tr *Transport) NodeFilters() []selector.NodeFilter {
+	return tr.nodeFilters
 }
 
 type headerCarrier metadata.MD
diff --git a/transport/http/client.go b/transport/http/client.go
index f1c21359..79ac4f91 100644
--- a/transport/http/client.go
+++ b/transport/http/client.go
@@ -16,10 +16,16 @@ import (
 	"github.com/go-kratos/kratos/v2/middleware"
 	"github.com/go-kratos/kratos/v2/registry"
 	"github.com/go-kratos/kratos/v2/selector"
-	"github.com/go-kratos/kratos/v2/selector/wrr"
+	"github.com/go-kratos/kratos/v2/selector/p2c"
 	"github.com/go-kratos/kratos/v2/transport"
 )
 
+func init() {
+	if selector.GlobalSelector() == nil {
+		selector.SetGlobalSelector(p2c.NewBuilder())
+	}
+}
+
 // DecodeErrorFunc is decode error func.
 type DecodeErrorFunc func(ctx context.Context, res *http.Response) error
 
@@ -43,7 +49,7 @@ type clientOptions struct {
 	decoder      DecodeResponseFunc
 	errorDecoder DecodeErrorFunc
 	transport    http.RoundTripper
-	selector     selector.Selector
+	nodeFilters  []selector.NodeFilter
 	discovery    registry.Discovery
 	middleware   []middleware.Middleware
 	block        bool
@@ -112,10 +118,10 @@ func WithDiscovery(d registry.Discovery) ClientOption {
 	}
 }
 
-// WithSelector with client selector.
-func WithSelector(selector selector.Selector) ClientOption {
+// WithNodeFilter with select filters
+func WithNodeFilter(filters ...selector.NodeFilter) ClientOption {
 	return func(o *clientOptions) {
-		o.selector = selector
+		o.nodeFilters = filters
 	}
 }
 
@@ -140,6 +146,7 @@ type Client struct {
 	r        *resolver
 	cc       *http.Client
 	insecure bool
+	selector selector.Selector
 }
 
 // NewClient returns an HTTP client.
@@ -151,7 +158,6 @@ func NewClient(ctx context.Context, opts ...ClientOption) (*Client, error) {
 		decoder:      DefaultResponseDecoder,
 		errorDecoder: DefaultErrorDecoder,
 		transport:    http.DefaultTransport,
-		selector:     wrr.New(),
 	}
 	for _, o := range opts {
 		o(&options)
@@ -166,10 +172,11 @@ func NewClient(ctx context.Context, opts ...ClientOption) (*Client, error) {
 	if err != nil {
 		return nil, err
 	}
+	selector := selector.GlobalSelector().Build()
 	var r *resolver
 	if options.discovery != nil {
 		if target.Scheme == "discovery" {
-			if r, err = newResolver(ctx, options.discovery, target, options.selector, options.block, insecure); err != nil {
+			if r, err = newResolver(ctx, options.discovery, target, selector, options.block, insecure); err != nil {
 				return nil, fmt.Errorf("[http client] new resolver failed!err: %v", options.endpoint)
 			}
 		} else if _, _, err := host.ExtractHostPort(options.endpoint); err != nil {
@@ -185,6 +192,7 @@ func NewClient(ctx context.Context, opts ...ClientOption) (*Client, error) {
 			Timeout:   options.timeout,
 			Transport: options.transport,
 		},
+		selector: selector,
 	}, nil
 }
 
@@ -276,7 +284,7 @@ func (client *Client) do(req *http.Request) (*http.Response, error) {
 			err  error
 			node selector.Node
 		)
-		if node, done, err = client.opts.selector.Select(req.Context()); err != nil {
+		if node, done, err = client.selector.Select(req.Context(), selector.WithNodeFilter(client.opts.nodeFilters...)); err != nil {
 			return nil, errors.ServiceUnavailable("NODE_NOT_FOUND", err.Error())
 		}
 		if client.insecure {
diff --git a/transport/http/context.go b/transport/http/context.go
index 347d93ff..f65e03fb 100644
--- a/transport/http/context.go
+++ b/transport/http/context.go
@@ -46,7 +46,7 @@ type responseWriter struct {
 	w    http.ResponseWriter
 }
 
-func (w *responseWriter) rest(res http.ResponseWriter) {
+func (w *responseWriter) reset(res http.ResponseWriter) {
 	w.w = res
 	w.code = http.StatusOK
 }
@@ -151,7 +151,7 @@ func (c *wrapper) Stream(code int, contentType string, rd io.Reader) error {
 }
 
 func (c *wrapper) Reset(res http.ResponseWriter, req *http.Request) {
-	c.w.rest(res)
+	c.w.reset(res)
 	c.res = res
 	c.req = req
 }
diff --git a/version.go b/version.go
index 08f05950..d0afa603 100644
--- a/version.go
+++ b/version.go
@@ -1,4 +1,4 @@
 package kratos
 
 // Release is the current kratos version.
-const Release = "v2.4.1"
+const Release = "v2.5.0"


Test patch:
diff --git a/internal/endpoint/endpoint_test.go b/internal/endpoint/endpoint_test.go
index e8870c80..c02878fd 100644
--- a/internal/endpoint/endpoint_test.go
+++ b/internal/endpoint/endpoint_test.go
@@ -6,41 +6,6 @@ import (
 	"testing"
 )
 
-func TestEndPoint(t *testing.T) {
-	type args struct {
-		url *url.URL
-	}
-	tests := []struct {
-		name string
-		args args
-		want bool
-	}{
-		// TODO: Add test cases.
-		{
-			name: "grpc://127.0.0.1?isSecure=false",
-			args: args{&url.URL{Scheme: "grpc", Host: "127.0.0.1", RawQuery: "isSecure=false"}},
-			want: false,
-		},
-		{
-			name: "grpc://127.0.0.1?isSecure=true",
-			args: args{&url.URL{Scheme: "grpc", Host: "127.0.0.1", RawQuery: "isSecure=true"}},
-			want: true,
-		},
-		{
-			name: "grpc://127.0.0.1",
-			args: args{&url.URL{Scheme: "grpc", Host: "127.0.0.1"}},
-			want: false,
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			if got := IsSecure(tt.args.url); !reflect.DeepEqual(got, tt.want) {
-				t.Errorf("GetQuery() = %v, want %v", got, tt.want)
-			}
-		})
-	}
-}
-
 func TestNewEndpoint(t *testing.T) {
 	type args struct {
 		scheme string
@@ -111,20 +76,6 @@ func TestParseEndpoint(t *testing.T) {
 			want:    "",
 			wantErr: false,
 		},
-
-		// Legacy
-		{
-			name:    "google",
-			args:    args{endpoints: []string{"grpc://www.google.com/?isSecure=true"}, scheme: "grpcs"},
-			want:    "www.google.com",
-			wantErr: false,
-		},
-		{
-			name:    "baidu",
-			args:    args{endpoints: []string{"http://www.baidu.com/?isSecure=true"}, scheme: "https"},
-			want:    "www.baidu.com",
-			wantErr: false,
-		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -140,56 +91,6 @@ func TestParseEndpoint(t *testing.T) {
 	}
 }
 
-func TestIsSecure(t *testing.T) {
-	tests := []struct {
-		url  *url.URL
-		want bool
-	}{
-		{
-			url:  &url.URL{Scheme: "http", Host: "127.0.0.1"},
-			want: false,
-		},
-		{
-			url:  &url.URL{Scheme: "http", Host: "127.0.0.1", RawQuery: "isSecure=false"},
-			want: false,
-		},
-		{
-			url:  &url.URL{Scheme: "grpc", Host: "127.0.0.1", RawQuery: "isSecure=true"},
-			want: true,
-		},
-	}
-	for _, tt := range tests {
-		if got := IsSecure(tt.url); got != tt.want {
-			t.Errorf("IsSecure() = %v, want %v", got, tt.want)
-		}
-	}
-}
-
-func TestLegacyURLToNew(t *testing.T) {
-	tests := []struct {
-		url  *url.URL
-		want *url.URL
-	}{
-		{
-			url:  &url.URL{Scheme: "http", Host: "www.google.com", RawQuery: "isSecure=true"},
-			want: &url.URL{Scheme: "https", Host: "www.google.com"},
-		},
-		{
-			url:  &url.URL{Scheme: "https", Host: "www.google.com", RawQuery: "isSecure=true"},
-			want: &url.URL{Scheme: "https", Host: "www.google.com", RawQuery: "isSecure=true"},
-		},
-		{
-			url:  &url.URL{Scheme: "http", Host: "go-kratos.dev", RawQuery: "isSecure=false"},
-			want: &url.URL{Scheme: "http", Host: "go-kratos.dev", RawQuery: "isSecure=false"},
-		},
-	}
-	for _, tt := range tests {
-		if got := legacyURLToNew(tt.url); !reflect.DeepEqual(got, tt.want) {
-			t.Errorf("legacyURLToNew() = %v, want %v", got, tt.want)
-		}
-	}
-}
-
 func TestSchema(t *testing.T) {
 	tests := []struct {
 		schema string
diff --git a/selector/p2c/p2c_test.go b/selector/p2c/p2c_test.go
index 03d549e2..f23412b7 100644
--- a/selector/p2c/p2c_test.go
+++ b/selector/p2c/p2c_test.go
@@ -16,7 +16,7 @@ import (
 )
 
 func TestWrr3(t *testing.T) {
-	p2c := New(WithFilter(filter.Version("v2.0.0")))
+	p2c := New()
 	var nodes []selector.Node
 	for i := 0; i < 3; i++ {
 		addr := fmt.Sprintf("127.0.0.%d:8080", i)
@@ -41,7 +41,7 @@ func TestWrr3(t *testing.T) {
 			d := time.Duration(rand.Intn(500)) * time.Millisecond
 			lk.Unlock()
 			time.Sleep(d)
-			n, done, err := p2c.Select(context.Background())
+			n, done, err := p2c.Select(context.Background(), selector.WithNodeFilter(filter.Version("v2.0.0")))
 			if err != nil {
 				t.Errorf("expect %v, got %v", nil, err)
 			}
@@ -92,7 +92,7 @@ func TestEmpty(t *testing.T) {
 }
 
 func TestOne(t *testing.T) {
-	p2c := New(WithFilter(filter.Version("v2.0.0")))
+	p2c := New()
 	var nodes []selector.Node
 	for i := 0; i < 1; i++ {
 		addr := fmt.Sprintf("127.0.0.%d:8080", i)
@@ -106,7 +106,7 @@ func TestOne(t *testing.T) {
 			}))
 	}
 	p2c.Apply(nodes)
-	n, done, err := p2c.Select(context.Background())
+	n, done, err := p2c.Select(context.Background(), selector.WithNodeFilter(filter.Version("v2.0.0")))
 	if err != nil {
 		t.Errorf("expect %v, got %v", nil, err)
 	}
diff --git a/selector/random/random_test.go b/selector/random/random_test.go
index 7e69f3ee..33e68994 100644
--- a/selector/random/random_test.go
+++ b/selector/random/random_test.go
@@ -10,7 +10,7 @@ import (
 )
 
 func TestWrr(t *testing.T) {
-	random := New(WithFilter(filter.Version("v2.0.0")))
+	random := New()
 	var nodes []selector.Node
 	nodes = append(nodes, selector.NewNode(
 		"http",
@@ -31,7 +31,7 @@ func TestWrr(t *testing.T) {
 	random.Apply(nodes)
 	var count1, count2 int
 	for i := 0; i < 200; i++ {
-		n, done, err := random.Select(context.Background())
+		n, done, err := random.Select(context.Background(), selector.WithNodeFilter(filter.Version("v2.0.0")))
 		if err != nil {
 			t.Errorf("expect no error, got %v", err)
 		}
diff --git a/selector/selector_test.go b/selector/selector_test.go
index 25e70acd..f7c1f1bc 100644
--- a/selector/selector_test.go
+++ b/selector/selector_test.go
@@ -49,7 +49,7 @@ func (b *mockWeightedNodeBuilder) Build(n Node) WeightedNode {
 	return &mockWeightedNode{Node: n}
 }
 
-func mockFilter(version string) Filter {
+func mockFilter(version string) NodeFilter {
 	return func(_ context.Context, nodes []Node) []Node {
 		newNodes := nodes[:0]
 		for _, n := range nodes {
@@ -83,7 +83,6 @@ func (b *mockBalancer) Pick(ctx context.Context, nodes []WeightedNode) (selected
 func TestDefault(t *testing.T) {
 	builder := DefaultBuilder{
 		Node:     &mockWeightedNodeBuilder{},
-		Filters:  []Filter{mockFilter("v2.0.0")},
 		Balancer: &mockBalancerBuilder{},
 	}
 	selector := builder.Build()
@@ -109,7 +108,7 @@ func TestDefault(t *testing.T) {
 			Metadata:  map[string]string{"weight": "10"},
 		}))
 	selector.Apply(nodes)
-	n, done, err := selector.Select(context.Background(), WithFilter(mockFilter("v2.0.0")))
+	n, done, err := selector.Select(context.Background(), WithNodeFilter(mockFilter("v2.0.0")))
 	if err != nil {
 		t.Errorf("expect %v, got %v", nil, err)
 	}
@@ -137,7 +136,7 @@ func TestDefault(t *testing.T) {
 	done(context.Background(), DoneInfo{})
 
 	// no v3.0.0 instance
-	n, done, err = selector.Select(context.Background(), WithFilter(mockFilter("v3.0.0")))
+	n, done, err = selector.Select(context.Background(), WithNodeFilter(mockFilter("v3.0.0")))
 	if !errors.Is(ErrNoAvailable, err) {
 		t.Errorf("expect %v, got %v", ErrNoAvailable, err)
 	}
@@ -150,7 +149,7 @@ func TestDefault(t *testing.T) {
 
 	// apply zero instance
 	selector.Apply([]Node{})
-	n, done, err = selector.Select(context.Background(), WithFilter(mockFilter("v2.0.0")))
+	n, done, err = selector.Select(context.Background(), WithNodeFilter(mockFilter("v2.0.0")))
 	if !errors.Is(ErrNoAvailable, err) {
 		t.Errorf("expect %v, got %v", ErrNoAvailable, err)
 	}
@@ -163,7 +162,7 @@ func TestDefault(t *testing.T) {
 
 	// apply zero instance
 	selector.Apply(nil)
-	n, done, err = selector.Select(context.Background(), WithFilter(mockFilter("v2.0.0")))
+	n, done, err = selector.Select(context.Background(), WithNodeFilter(mockFilter("v2.0.0")))
 	if !errors.Is(ErrNoAvailable, err) {
 		t.Errorf("expect %v, got %v", ErrNoAvailable, err)
 	}
diff --git a/selector/wrr/wrr_test.go b/selector/wrr/wrr_test.go
index e3e96ed4..f1d4b45f 100644
--- a/selector/wrr/wrr_test.go
+++ b/selector/wrr/wrr_test.go
@@ -11,7 +11,7 @@ import (
 )
 
 func TestWrr(t *testing.T) {
-	wrr := New(WithFilter(filter.Version("v2.0.0")))
+	wrr := New()
 	var nodes []selector.Node
 	nodes = append(nodes, selector.NewNode(
 		"http",
@@ -32,7 +32,7 @@ func TestWrr(t *testing.T) {
 	wrr.Apply(nodes)
 	var count1, count2 int
 	for i := 0; i < 90; i++ {
-		n, done, err := wrr.Select(context.Background())
+		n, done, err := wrr.Select(context.Background(), selector.WithNodeFilter(filter.Version("v2.0.0")))
 		if err != nil {
 			t.Errorf("expect no error, got %v", err)
 		}
diff --git a/transport/grpc/balancer_test.go b/transport/grpc/balancer_test.go
index bff519bd..5b700264 100644
--- a/transport/grpc/balancer_test.go
+++ b/transport/grpc/balancer_test.go
@@ -19,19 +19,10 @@ func TestTrailer(t *testing.T) {
 	}
 }
 
-func TestBalancerName(t *testing.T) {
-	o := &clientOptions{}
-
-	WithBalancerName("p2c")(o)
-	if !reflect.DeepEqual("p2c", o.balancerName) {
-		t.Errorf("expect %v, got %v", "p2c", o.balancerName)
-	}
-}
-
 func TestFilters(t *testing.T) {
 	o := &clientOptions{}
 
-	WithFilter(func(_ context.Context, nodes []selector.Node) []selector.Node {
+	WithNodeFilter(func(_ context.Context, nodes []selector.Node) []selector.Node {
 		return nodes
 	})(o)
 	if !reflect.DeepEqual(1, len(o.filters)) {
diff --git a/transport/http/client_test.go b/transport/http/client_test.go
index c768b364..4ee00456 100644
--- a/transport/http/client_test.go
+++ b/transport/http/client_test.go
@@ -182,13 +182,18 @@ func TestWithDiscovery(t *testing.T) {
 	}
 }
 
-func TestWithSelector(t *testing.T) {
-	ov := &selector.Default{}
-	o := WithSelector(ov)
+func TestWithNodeFilter(t *testing.T) {
+	ov := func(context.Context, []selector.Node) []selector.Node {
+		return []selector.Node{&selector.DefaultNode{}}
+	}
+	o := WithNodeFilter(ov)
 	co := &clientOptions{}
 	o(co)
-	if !reflect.DeepEqual(co.selector, ov) {
-		t.Errorf("expected selector to be %v, got %v", ov, co.selector)
+	for _, n := range co.nodeFilters {
+		ret := n(context.Background(), nil)
+		if len(ret) != 1 {
+			t.Errorf("expected node  length to be 1, got %v", len(ret))
+		}
 	}
 }