Repository: go-kratos/kratos
Pull request #1437 (2021-09-07 02:49:52)

Feat: add load balancer
add load balancer 

codecov-commenter: # [Codecov](https://codecov.io/gh/go-kratos/kratos/pull/1437?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos) Report
> Merging [#1437](https://codecov.io/gh/go-kratos/kratos/pull/1437?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos) (aa8a2be) into [main](https://codecov.io/gh/go-kratos/kratos/commit/fd9d324906dfb1e40c6a410aa6c949794ad9c84f?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos) (fd9d324) will **decrease** coverage by `0.09%`.
> The diff coverage is `26.82%`.

[![Impacted file tree graph](https://codecov.io/gh/go-kratos/kratos/pull/1437/graphs/tree.svg?width=650&height=150&src=pr&token=XwnmBTz78x&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos)](https://codecov.io/gh/go-kratos/kratos/pull/1437?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos)

```diff
@@            Coverage Diff             @@
##             main    #1437      +/-   ##
==========================================
- Coverage   64.82%   64.73%   -0.10%     
==========================================
  Files          64       65       +1     
  Lines        2920     2938      +18     
==========================================
+ Hits         1893     1902       +9     
- Misses        868      877       +9     
  Partials      159      159              
```


| [Impacted Files](https://codecov.io/gh/go-kratos/kratos/pull/1437?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos) | Coverage Δ | |
|---|---|---|
| [transport/http/resolver.go](https://codecov.io/gh/go-kratos/kratos/pull/1437/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos#diff-dHJhbnNwb3J0L2h0dHAvcmVzb2x2ZXIuZ28=) | `14.66% <0.00%> (+0.56%)` | :arrow_up: |
| [transport/http/balancer.go](https://codecov.io/gh/go-kratos/kratos/pull/1437/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos#diff-dHJhbnNwb3J0L2h0dHAvYmFsYW5jZXIuZ28=) | `30.00% <30.00%> (ø)` | |
| [transport/http/client.go](https://codecov.io/gh/go-kratos/kratos/pull/1437/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos#diff-dHJhbnNwb3J0L2h0dHAvY2xpZW50Lmdv) | `66.30% <33.33%> (+1.27%)` | :arrow_up: |
| [transport/grpc/client.go](https://codecov.io/gh/go-kratos/kratos/pull/1437/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos#diff-dHJhbnNwb3J0L2dycGMvY2xpZW50Lmdv) | `82.85% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/go-kratos/kratos/pull/1437?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/go-kratos/kratos/pull/1437?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos). Last update [fd9d324...aa8a2be](https://codecov.io/gh/go-kratos/kratos/pull/1437?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos).


Patch:
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 70c19cc7..a5c3a0b2 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -31,7 +31,6 @@ jobs:
       - name: Lint
         uses: golangci/golangci-lint-action@v2
         with:
-          version: v1.42
+          version: latest
           working-directory: ${{ matrix.workdir }}
-          skip-go-installation: true
           skip-pkg-cache: true
diff --git a/Makefile b/Makefile
index 0b11ede6..842c9562 100644
--- a/Makefile
+++ b/Makefile
@@ -9,8 +9,6 @@ BIN		:= 	""
 TOOLS_SHELL="./hack/tools.sh"
 # golangci-lint
 LINTER := bin/golangci-lint
-$(LINTER): 
-	curl -L https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s v1.42.0
 
 # check GOBIN
 ifneq ($(GOBIN),)
@@ -22,6 +20,9 @@ else
 	endif
 endif
 
+$(LINTER): 
+	curl -SL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s latest
+
 all:
 	@cd cmd/kratos && go build && cd - &> /dev/null
 	@cd cmd/protoc-gen-go-errors && go build && cd - &> /dev/null
diff --git a/README.md b/README.md
index 817de678..446677ae 100644
--- a/README.md
+++ b/README.md
@@ -63,7 +63,7 @@ docker run -it --rm -p 8000:8000 --workdir /workspace golang
 ```shell
 apt-get update && apt-get -y install protobuf-compiler
 export GOPROXY=https://goproxy.io,direct
-go get github.com/go-kratos/kratos/cmd/kratos/v2@latest && kratos upgrade
+go install github.com/go-kratos/kratos/cmd/kratos/v2@latest && kratos upgrade
 ```
   
 ```shell
diff --git a/README_zh.md b/README_zh.md
index 3b2fd176..a28f934d 100644
--- a/README_zh.md
+++ b/README_zh.md
@@ -51,9 +51,9 @@ Kratos 一套轻量级 Go 微服务框架，包含大量微服务相关功能及
 - [protoc-gen-go](https://github.com/protocolbuffers/protobuf-go)
 
 ### Installing
-##### go get 安装：
+##### go install 安装：
 ```
-go get github.com/go-kratos/kratos/cmd/kratos/v2@latest
+go install github.com/go-kratos/kratos/cmd/kratos/v2@latest
 kratos upgrade
 ```
 ##### 源码编译安装：
diff --git a/cmd/kratos/internal/base/path.go b/cmd/kratos/internal/base/path.go
index 39b41248..62df6f48 100644
--- a/cmd/kratos/internal/base/path.go
+++ b/cmd/kratos/internal/base/path.go
@@ -20,7 +20,7 @@ func kratosHome() string {
 	}
 	home := path.Join(dir, ".kratos")
 	if _, err := os.Stat(home); os.IsNotExist(err) {
-		if err := os.MkdirAll(home, 0700); err != nil {
+		if err := os.MkdirAll(home, 0o700); err != nil {
 			log.Fatal(err)
 		}
 	}
@@ -30,7 +30,7 @@ func kratosHome() string {
 func kratosHomeWithDir(dir string) string {
 	home := path.Join(kratosHome(), dir)
 	if _, err := os.Stat(home); os.IsNotExist(err) {
-		if err := os.MkdirAll(home, 0700); err != nil {
+		if err := os.MkdirAll(home, 0o700); err != nil {
 			log.Fatal(err)
 		}
 	}
@@ -103,7 +103,7 @@ func hasSets(name string, sets []string) bool {
 }
 
 func Tree(path string, dir string) {
-	filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
+	_ = filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
 		if !info.IsDir() {
 			fmt.Printf("%s %s (%v bytes)\n", color.GreenString("CREATED"), strings.Replace(path, dir+"/", "", -1), info.Size())
 		}
diff --git a/cmd/kratos/internal/base/repo.go b/cmd/kratos/internal/base/repo.go
index 07a06744..ec4cb09d 100644
--- a/cmd/kratos/internal/base/repo.go
+++ b/cmd/kratos/internal/base/repo.go
@@ -39,7 +39,7 @@ func (r *Repo) Path() string {
 	if end == -1 {
 		end = len(r.url)
 	}
-	branch := ""
+	var branch string
 	if r.branch == "" {
 		branch = "@main"
 	} else {
@@ -69,7 +69,7 @@ func (r *Repo) Clone(ctx context.Context) error {
 	if _, err := os.Stat(r.Path()); !os.IsNotExist(err) {
 		return r.Pull(ctx)
 	}
-	cmd := &exec.Cmd{}
+	var cmd *exec.Cmd
 	if r.branch == "" {
 		cmd = exec.Command("git", "clone", r.url, r.Path())
 	} else {
diff --git a/cmd/kratos/internal/change/change.go b/cmd/kratos/internal/change/change.go
index 2c14d651..374cf7c8 100644
--- a/cmd/kratos/internal/change/change.go
+++ b/cmd/kratos/internal/change/change.go
@@ -14,8 +14,11 @@ var CmdChange = &cobra.Command{
 	Long:  "Get a kratos release or commits info. Example: kratos changelog dev or kratos changelog {version}",
 	Run:   run,
 }
-var token string
-var repoURL string
+
+var (
+	token   string
+	repoURL string
+)
 
 func init() {
 	if repoURL = os.Getenv("KRATOS_REPO"); repoURL == "" {
@@ -26,8 +29,8 @@ func init() {
 }
 
 func run(cmd *cobra.Command, args []string) {
-	owner, repo := ParseGithubUrl(repoURL)
-	api := GithubApi{Owner: owner, Repo: repo, Token: token}
+	owner, repo := ParseGithubURL(repoURL)
+	api := GithubAPI{Owner: owner, Repo: repo, Token: token}
 	version := "latest"
 	if len(args) > 0 {
 		version = args[0]
diff --git a/cmd/kratos/internal/change/get.go b/cmd/kratos/internal/change/get.go
index 51031eef..a5432834 100644
--- a/cmd/kratos/internal/change/get.go
+++ b/cmd/kratos/internal/change/get.go
@@ -19,7 +19,7 @@ type ReleaseInfo struct {
 	} `json:"author"`
 	PublishedAt string `json:"published_at"`
 	Body        string `json:"body"`
-	HtmlUrl     string `json:"html_url"`
+	HTMLURL     string `json:"html_url"`
 }
 
 type CommitInfo struct {
@@ -32,20 +32,20 @@ type ErrorInfo struct {
 	Message string
 }
 
-type GithubApi struct {
+type GithubAPI struct {
 	Owner string
 	Repo  string
 	Token string
 }
 
 // GetReleaseInfo for getting kratos release info.
-func (g *GithubApi) GetReleaseInfo(version string) ReleaseInfo {
+func (g *GithubAPI) GetReleaseInfo(version string) ReleaseInfo {
 	api := fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/latest", g.Owner, g.Repo)
 	if version != "latest" {
 		api = fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/tags/%s", g.Owner, g.Repo, version)
 	}
 	resp, code := requestGithubAPI(api, "GET", nil, g.Token)
-	if code != 200 {
+	if code != http.StatusOK {
 		printGithubErrorInfo(resp)
 	}
 	releaseInfo := ReleaseInfo{}
@@ -57,14 +57,14 @@ func (g *GithubApi) GetReleaseInfo(version string) ReleaseInfo {
 }
 
 // GetCommitsInfo for getting kratos commits info.
-func (g *GithubApi) GetCommitsInfo() []CommitInfo {
+func (g *GithubAPI) GetCommitsInfo() []CommitInfo {
 	info := g.GetReleaseInfo("latest")
 	page := 1
 	var list []CommitInfo
 	for {
 		url := fmt.Sprintf("https://api.github.com/repos/%s/%s/commits?pre_page=100&page=%d&since=%s", g.Owner, g.Repo, page, info.PublishedAt)
 		resp, code := requestGithubAPI(url, "GET", nil, g.Token)
-		if code != 200 {
+		if code != http.StatusOK {
 			printGithubErrorInfo(resp)
 		}
 		var res []CommitInfo
@@ -73,7 +73,7 @@ func (g *GithubApi) GetCommitsInfo() []CommitInfo {
 			fatal(err)
 		}
 		list = append(list, res...)
-		if len(res) < 100 {
+		if len(res) < http.StatusContinue {
 			break
 		}
 		page++
@@ -179,14 +179,14 @@ func ParseReleaseInfo(info ReleaseInfo) string {
 		"Author: %s\nDate: %s\nUrl: %s\n\n%s\n\n%s\n\n%s\n",
 		info.Author.Login,
 		info.PublishedAt,
-		info.HtmlUrl,
+		info.HTMLURL,
 		splitters,
 		body,
 		splitters,
 	)
 }
 
-func ParseGithubUrl(url string) (owner string, repo string) {
+func ParseGithubURL(url string) (owner string, repo string) {
 	var start int
 	start = strings.Index(url, "//")
 	if start == -1 {
diff --git a/cmd/kratos/internal/project/new.go b/cmd/kratos/internal/project/new.go
index 434f03ad..f98552b0 100644
--- a/cmd/kratos/internal/project/new.go
+++ b/cmd/kratos/internal/project/new.go
@@ -20,7 +20,6 @@ type Project struct {
 
 // New new a project from remote repo.
 func (p *Project) New(ctx context.Context, dir string, layout string, branch string) error {
-
 	to := path.Join(dir, p.Name)
 	if _, err := os.Stat(to); !os.IsNotExist(err) {
 		fmt.Printf("🚫 %s already exists\n", p.Name)
@@ -29,7 +28,10 @@ func (p *Project) New(ctx context.Context, dir string, layout string, branch str
 			Message: "📂 Do you want to override the folder ?",
 			Help:    "Delete the existing folder and create the project.",
 		}
-		survey.AskOne(prompt, &override)
+		e := survey.AskOne(prompt, &override)
+		if e != nil {
+			return e
+		}
 		if !override {
 			return err
 		}
@@ -40,10 +42,13 @@ func (p *Project) New(ctx context.Context, dir string, layout string, branch str
 	if err := repo.CopyTo(ctx, to, p.Path, []string{".git", ".github"}); err != nil {
 		return err
 	}
-	os.Rename(
+	e := os.Rename(
 		path.Join(to, "cmd", "server"),
 		path.Join(to, "cmd", p.Name),
 	)
+	if e != nil {
+		return e
+	}
 	base.Tree(to, dir)
 
 	fmt.Printf("\n🍺 Project creation succeeded %s\n", color.GreenString(p.Name))
diff --git a/cmd/kratos/internal/project/project.go b/cmd/kratos/internal/project/project.go
index 3e4de68d..1ead3cbc 100644
--- a/cmd/kratos/internal/project/project.go
+++ b/cmd/kratos/internal/project/project.go
@@ -19,8 +19,10 @@ var CmdNew = &cobra.Command{
 	Run:   run,
 }
 
-var repoURL string
-var branch string
+var (
+	repoURL string
+	branch  string
+)
 
 func init() {
 	if repoURL = os.Getenv("KRATOS_LAYOUT_REPO"); repoURL == "" {
@@ -43,8 +45,8 @@ func run(cmd *cobra.Command, args []string) {
 			Message: "What is project name ?",
 			Help:    "Created project name.",
 		}
-		survey.AskOne(prompt, &name)
-		if name == "" {
+		err = survey.AskOne(prompt, &name)
+		if err != nil || name == "" {
 			return
 		}
 	} else {
diff --git a/cmd/kratos/internal/proto/add/proto.go b/cmd/kratos/internal/proto/add/proto.go
index e44b3c71..e93e7087 100644
--- a/cmd/kratos/internal/proto/add/proto.go
+++ b/cmd/kratos/internal/proto/add/proto.go
@@ -29,7 +29,7 @@ func (p *Proto) Generate() error {
 	}
 	to := path.Join(wd, p.Path)
 	if _, err := os.Stat(to); os.IsNotExist(err) {
-		if err := os.MkdirAll(to, 0700); err != nil {
+		if err := os.MkdirAll(to, 0o700); err != nil {
 			return err
 		}
 	}
@@ -37,5 +37,5 @@ func (p *Proto) Generate() error {
 	if _, err := os.Stat(name); !os.IsNotExist(err) {
 		return fmt.Errorf("%s already exists", p.Name)
 	}
-	return ioutil.WriteFile(name, body, 0644)
+	return ioutil.WriteFile(name, body, 0o644)
 }
diff --git a/cmd/kratos/internal/proto/client/client.go b/cmd/kratos/internal/proto/client/client.go
index 6ca4433b..62e4e446 100644
--- a/cmd/kratos/internal/proto/client/client.go
+++ b/cmd/kratos/internal/proto/client/client.go
@@ -14,15 +14,13 @@ import (
 	"github.com/spf13/cobra"
 )
 
-var (
-	// CmdClient represents the source command.
-	CmdClient = &cobra.Command{
-		Use:   "client",
-		Short: "Generate the proto client code",
-		Long:  "Generate the proto client code. Example: kratos proto client helloworld.proto",
-		Run:   run,
-	}
-)
+// CmdClient represents the source command.
+var CmdClient = &cobra.Command{
+	Use:   "client",
+	Short: "Generate the proto client code",
+	Long:  "Generate the proto client code. Example: kratos proto client helloworld.proto",
+	Run:   run,
+}
 
 var protoPath string
 
diff --git a/cmd/kratos/internal/proto/proto.go b/cmd/kratos/internal/proto/proto.go
index 87a1de9a..c23e5a83 100644
--- a/cmd/kratos/internal/proto/proto.go
+++ b/cmd/kratos/internal/proto/proto.go
@@ -23,5 +23,4 @@ func init() {
 }
 
 func run(cmd *cobra.Command, args []string) {
-
 }
diff --git a/cmd/kratos/internal/proto/server/server.go b/cmd/kratos/internal/proto/server/server.go
index f3970e7e..25dc289d 100644
--- a/cmd/kratos/internal/proto/server/server.go
+++ b/cmd/kratos/internal/proto/server/server.go
@@ -60,8 +60,10 @@ func run(cmd *cobra.Command, args []string) {
 			for _, e := range s.Elements {
 				r, ok := e.(*proto.RPC)
 				if ok {
-					cs.Methods = append(cs.Methods, &Method{Service: s.Name, Name: r.Name, Request: r.RequestType,
-						Reply: r.ReturnsType, Type: getMethodType(r.StreamsRequest, r.StreamsReturns)})
+					cs.Methods = append(cs.Methods, &Method{
+						Service: s.Name, Name: r.Name, Request: r.RequestType,
+						Reply: r.ReturnsType, Type: getMethodType(r.StreamsRequest, r.StreamsReturns),
+					})
 				}
 			}
 			res = append(res, cs)
@@ -81,7 +83,7 @@ func run(cmd *cobra.Command, args []string) {
 		if err != nil {
 			log.Fatal(err)
 		}
-		if err := ioutil.WriteFile(to, b, 0644); err != nil {
+		if err := ioutil.WriteFile(to, b, 0o644); err != nil {
 			log.Fatal(err)
 		}
 		fmt.Println(to)
diff --git a/cmd/kratos/internal/proto/server/template.go b/cmd/kratos/internal/proto/server/template.go
index f394322d..1d60ebba 100644
--- a/cmd/kratos/internal/proto/server/template.go
+++ b/cmd/kratos/internal/proto/server/template.go
@@ -34,7 +34,8 @@ func New{{ .Service }}Service() *{{ .Service }}Service {
 {{- $s1 := "google.protobuf.Empty" }}
 {{ range .Methods }}
 {{- if eq .Type 1 }}
-func (s *{{ .Service }}Service) {{ .Name }}(ctx context.Context, req {{ if eq .Request $s1 }}*emptypb.Empty{{ else }}*pb.{{ .Request }}{{ end }}) ({{ if eq .Reply $s1 }}*emptypb.Empty{{ else }}*pb.{{ .Reply }}{{ end }}, error) {
+func (s *{{ .Service }}Service) {{ .Name }}(ctx context.Context, req {{ if eq .Request $s1 }}*emptypb.Empty
+{{ else }}*pb.{{ .Request }}{{ end }}) ({{ if eq .Reply $s1 }}*emptypb.Empty{{ else }}*pb.{{ .Reply }}{{ end }}, error) {
 	return {{ if eq .Reply $s1 }}&emptypb.Empty{}{{ else }}&pb.{{ .Reply }}{}{{ end }}, nil
 }
 
@@ -70,7 +71,8 @@ func (s *{{ .Service }}Service) {{ .Name }}(conn pb.{{ .Service }}_{{ .Name }}Se
 }
 
 {{- else if eq .Type 4 }}
-func (s *{{ .Service }}Service) {{ .Name }}(req {{ if eq .Request $s1 }}*emptypb.Empty{{ else }}*pb.{{ .Request }}{{ end }}, conn pb.{{ .Service }}_{{ .Name }}Server) error {
+func (s *{{ .Service }}Service) {{ .Name }}(req {{ if eq .Request $s1 }}*emptypb.Empty
+{{ else }}*pb.{{ .Request }}{{ end }}, conn pb.{{ .Service }}_{{ .Name }}Server) error {
 	for {
 		err := conn.Send(&pb.{{ .Reply }}{})
 		if err != nil {
@@ -115,13 +117,14 @@ type Method struct {
 }
 
 func (s *Service) execute() ([]byte, error) {
+	const empty = "google.protobuf.Empty"
 	buf := new(bytes.Buffer)
 	for _, method := range s.Methods {
-		if (method.Type == unaryType && (method.Request == "google.protobuf.Empty" || method.Reply == "google.protobuf.Empty")) ||
-			(method.Type == returnsStreamsType && method.Request == "google.protobuf.Empty") {
+		if (method.Type == unaryType && (method.Request == empty || method.Reply == empty)) ||
+			(method.Type == returnsStreamsType && method.Request == empty) {
 			s.GoogleEmpty = true
 		}
-		if method.Type == twoWayStreamsType || method.Type == returnsStreamsType {
+		if method.Type == twoWayStreamsType || method.Type == requestStreamsType {
 			s.UseIO = true
 		}
 		if method.Type == unaryType {
diff --git a/cmd/kratos/internal/run/run.go b/cmd/kratos/internal/run/run.go
index 8025e3c9..ce5f17f7 100644
--- a/cmd/kratos/internal/run/run.go
+++ b/cmd/kratos/internal/run/run.go
@@ -48,15 +48,15 @@ func Run(cmd *cobra.Command, args []string) {
 			}
 		} else {
 			var cmdPaths []string
-			for k, _ := range cmdPath {
+			for k := range cmdPath {
 				cmdPaths = append(cmdPaths, k)
 			}
 			prompt := &survey.Select{
 				Message: "Which directory do you want to run?",
 				Options: cmdPaths,
 			}
-			survey.AskOne(prompt, &dir)
-			if dir == "" {
+			e := survey.AskOne(prompt, &dir)
+			if e != nil || dir == "" {
 				return
 			}
 			dir = path.Join(cmdPath[dir], dir)
@@ -70,7 +70,6 @@ func Run(cmd *cobra.Command, args []string) {
 		fmt.Fprintf(os.Stderr, "\033[31mERROR: %s\033[m\n", err.Error())
 		return
 	}
-	return
 }
 
 func findCMD(base string) (map[string]string, error) {
@@ -110,7 +109,7 @@ func findCMD(base string) (map[string]string, error) {
 		if root {
 			break
 		}
-		tmp = filepath.Join(base, "..")
+		_ = filepath.Join(base, "..")
 	}
 	return map[string]string{"": base}, nil
 }
diff --git a/cmd/protoc-gen-go-http/http.go b/cmd/protoc-gen-go-http/http.go
index fe0303e6..25c44320 100644
--- a/cmd/protoc-gen-go-http/http.go
+++ b/cmd/protoc-gen-go-http/http.go
@@ -177,10 +177,8 @@ func buildPathVars(method *protogen.Method, path string) (res []string) {
 }
 
 func camelCaseVars(s string) string {
-	var (
-		vars []string
-		subs = strings.Split(s, ".")
-	)
+	vars := make([]string, 0)
+	subs := strings.Split(s, ".")
 	for _, sub := range subs {
 		vars = append(vars, camelCase(sub))
 	}
diff --git a/cmd/protoc-gen-go-http/template.go b/cmd/protoc-gen-go-http/template.go
index 32fae69a..2c34647b 100644
--- a/cmd/protoc-gen-go-http/template.go
+++ b/cmd/protoc-gen-go-http/template.go
@@ -124,5 +124,5 @@ func (s *serviceDesc) execute() string {
 	if err := tmpl.Execute(buf, s); err != nil {
 		panic(err)
 	}
-	return strings.Trim(string(buf.Bytes()), "\r\n")
+	return strings.Trim(buf.String(), "\r\n")
 }
diff --git a/config/README.md b/config/README.md
index 0f280524..6e6363a0 100644
--- a/config/README.md
+++ b/config/README.md
@@ -1,12 +1,25 @@
 # Config
 
-## kube
+## kubernetes
 
 ```shell
-go get -u github.com/go-kratos/kratos/config/kubernetes/v2
+go get -u github.com/go-kratos/kratos/contrib/config/kubernetes/v2
 ```
 
-##apollo
+## apollo
+
+```shell
+go get -u github.com/go-kratos/kratos/contrib/config/apollo/v2
+```
+
+## etcd
+
+```shell
+go get -u github.com/go-kratos/kratos/contrib/config/etcd/v2
+```
+
+## nacos
+
 ```shell
-go get -u github.com/go-kratos/kratos/config/apollo/v2
+go get -u github.com/go-kratos/kratos/contrib/config/nacos/v2
 ```
\ No newline at end of file
diff --git a/container/group/group.go b/container/group/group.go
new file mode 100644
index 00000000..bb9d0568
--- /dev/null
+++ b/container/group/group.go
@@ -0,0 +1,64 @@
+// Package group provides a sample lazy load container.
+// The group only creating a new object not until the object is needed by user.
+// And it will cache all the objects to reduce the creation of object.
+package group
+
+import "sync"
+
+// Group is a lazy load container.
+type Group struct {
+	new  func() interface{}
+	vals map[string]interface{}
+	sync.RWMutex
+}
+
+// NewGroup news a group container.
+func NewGroup(new func() interface{}) *Group {
+	if new == nil {
+		panic("container.group: can't assign a nil to the new function")
+	}
+	return &Group{
+		new:  new,
+		vals: make(map[string]interface{}),
+	}
+}
+
+// Get gets the object by the given key.
+func (g *Group) Get(key string) interface{} {
+	g.RLock()
+	v, ok := g.vals[key]
+	if ok {
+		g.RUnlock()
+		return v
+	}
+	g.RUnlock()
+
+	// slowpath for group don`t have specified key value
+	g.Lock()
+	defer g.Unlock()
+	v, ok = g.vals[key]
+	if ok {
+		return v
+	}
+	v = g.new()
+	g.vals[key] = v
+	return v
+}
+
+// Reset resets the new function and deletes all existing objects.
+func (g *Group) Reset(new func() interface{}) {
+	if new == nil {
+		panic("container.group: can't assign a nil to the new function")
+	}
+	g.Lock()
+	g.new = new
+	g.Unlock()
+	g.Clear()
+}
+
+// Clear deletes all objects.
+func (g *Group) Clear() {
+	g.Lock()
+	g.vals = make(map[string]interface{})
+	g.Unlock()
+}
diff --git a/contrib/config/apollo/go.sum b/contrib/config/apollo/go.sum
index 44e44d95..95613eb9 100644
--- a/contrib/config/apollo/go.sum
+++ b/contrib/config/apollo/go.sum
@@ -57,7 +57,7 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-kratos/aegis v0.1.0/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw=
+github.com/go-kratos/aegis v0.1.1/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw=
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
@@ -66,6 +66,7 @@ github.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIh
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
diff --git a/contrib/config/etcd/config.go b/contrib/config/etcd/config.go
new file mode 100644
index 00000000..a18f3dc2
--- /dev/null
+++ b/contrib/config/etcd/config.go
@@ -0,0 +1,92 @@
+package etcd
+
+import (
+	"context"
+	"errors"
+
+	"github.com/go-kratos/kratos/v2/config"
+	clientv3 "go.etcd.io/etcd/client/v3"
+)
+
+// Option is etcd config option.
+type Option func(o *options)
+
+type options struct {
+	ctx    context.Context
+	path   string
+	prefix bool
+}
+
+//  WithContext with registry context.
+func WithContext(ctx context.Context) Option {
+	return Option(func(o *options) {
+		o.ctx = ctx
+	})
+}
+
+// WithPath is config path
+func WithPath(p string) Option {
+	return Option(func(o *options) {
+		o.path = p
+	})
+}
+
+// WithPrefix is config prefix
+func WithPrefix(prefix bool) Option {
+	return Option(func(o *options) {
+		o.prefix = prefix
+	})
+}
+
+type source struct {
+	client  *clientv3.Client
+	options *options
+}
+
+func New(client *clientv3.Client, opts ...Option) (config.Source, error) {
+	options := &options{
+		ctx:    context.Background(),
+		path:   "",
+		prefix: false,
+	}
+
+	for _, opt := range opts {
+		opt(options)
+	}
+
+	if options.path == "" {
+		return nil, errors.New("path invalid")
+	}
+
+	return &source{
+		client:  client,
+		options: options,
+	}, nil
+}
+
+// Load return the config values
+func (s *source) Load() ([]*config.KeyValue, error) {
+	var opts []clientv3.OpOption
+	if s.options.prefix {
+		opts = append(opts, clientv3.WithPrefix())
+	}
+
+	rsp, err := s.client.Get(s.options.ctx, s.options.path, opts...)
+	if err != nil {
+		return nil, err
+	}
+
+	kvs := make([]*config.KeyValue, 0)
+	for _, item := range rsp.Kvs {
+		kvs = append(kvs, &config.KeyValue{
+			Key:   string(item.Key),
+			Value: item.Value,
+		})
+	}
+	return kvs, nil
+}
+
+// Watch return the watcher
+func (s *source) Watch() (config.Watcher, error) {
+	return newWatcher(s), nil
+}
diff --git a/contrib/config/etcd/go.mod b/contrib/config/etcd/go.mod
new file mode 100644
index 00000000..b8c6ccc7
--- /dev/null
+++ b/contrib/config/etcd/go.mod
@@ -0,0 +1,11 @@
+module github.com/go-kratos/kratos/contrib/config/etcd/v2
+
+go 1.15
+
+require (
+	github.com/go-kratos/kratos/v2 v2.0.0
+	go.etcd.io/etcd/client/v3 v3.5.0
+	google.golang.org/grpc v1.40.0
+)
+
+replace github.com/go-kratos/kratos/v2 => ../../../
diff --git a/contrib/config/etcd/go.sum b/contrib/config/etcd/go.sum
new file mode 100644
index 00000000..adaa3bc0
--- /dev/null
+++ b/contrib/config/etcd/go.sum
@@ -0,0 +1,301 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
+github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
+github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
+github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
+github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
+github.com/go-kratos/aegis v0.1.1/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw=
+github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
+github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
+github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
+github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
+github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
+github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
+github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
+github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
+github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
+github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
+github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
+github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
+github.com/shirou/gopsutil/v3 v3.21.8/go.mod h1:YWp/H8Qs5fVmf17v7JNZzA0mPJ+mS2e9JdiUF9LlKzQ=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
+github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs=
+github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+go.etcd.io/etcd/api/v3 v3.5.0 h1:GsV3S+OfZEOCNXdtNkBSR7kgLobAa/SO6tCxRa0GAYw=
+go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
+go.etcd.io/etcd/client/pkg/v3 v3.5.0 h1:2aQv6F436YnN7I4VbI8PPYrBhu+SmrTaADcf8Mi/6PU=
+go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
+go.etcd.io/etcd/client/v3 v3.5.0 h1:62Eh0XOro+rDwkrypAGDfgmNh5Joq+z+W9HZdlXMzek=
+go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0=
+go.opentelemetry.io/otel v1.0.0-RC2/go.mod h1:w1thVQ7qbAy8MHb0IFj8a5Q2QU0l2ksf8u/CN8m3NOM=
+go.opentelemetry.io/otel/sdk v1.0.0-RC2/go.mod h1:fgwHyiDn4e5k40TD9VX243rOxXR+jzsWBZYA2P5jpEw=
+go.opentelemetry.io/otel/trace v1.0.0-RC2/go.mod h1:JPQ+z6nNw9mqEGT8o3eoPTdnNI+Aj5JcxEsVGREIAy4=
+go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
+go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
+go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
+go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
+go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U=
+go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71 h1:ikCpsnYR+Ew0vu99XlDp55lGgDJdIMx3f4a18jfse/s=
+golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
+golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
+google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67 h1:VmMSf20ssFK0+u1dscyTH9bU4/M4y+X/xNfkvD6kGtM=
+google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
+google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
+google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
+google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
+google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q=
+google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
+google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
diff --git a/contrib/config/etcd/watcher.go b/contrib/config/etcd/watcher.go
new file mode 100644
index 00000000..6a2d8cce
--- /dev/null
+++ b/contrib/config/etcd/watcher.go
@@ -0,0 +1,44 @@
+package etcd
+
+import (
+	"github.com/go-kratos/kratos/v2/config"
+	clientv3 "go.etcd.io/etcd/client/v3"
+)
+
+type watcher struct {
+	source    *source
+	ch        clientv3.WatchChan
+	closeChan chan struct{}
+}
+
+func newWatcher(s *source) *watcher {
+	w := &watcher{
+		source:    s,
+		closeChan: make(chan struct{}),
+	}
+
+	var opts []clientv3.OpOption
+	if s.options.prefix {
+		opts = append(opts, clientv3.WithPrefix())
+	}
+	w.ch = s.client.Watch(s.options.ctx, s.options.path, opts...)
+
+	return w
+}
+
+func (s *watcher) Next() ([]*config.KeyValue, error) {
+	select {
+	case _, ok := <-s.ch:
+		if !ok {
+			return nil, nil
+		}
+		return s.source.Load()
+	case <-s.closeChan:
+		return nil, nil
+	}
+}
+
+func (s *watcher) Stop() error {
+	close(s.closeChan)
+	return nil
+}
diff --git a/contrib/config/kubernetes/go.sum b/contrib/config/kubernetes/go.sum
index 38742b63..c892b6fa 100644
--- a/contrib/config/kubernetes/go.sum
+++ b/contrib/config/kubernetes/go.sum
@@ -65,7 +65,7 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
 github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
-github.com/go-kratos/aegis v0.1.0/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw=
+github.com/go-kratos/aegis v0.1.1/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw=
 github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
 github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc=
 github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
@@ -81,6 +81,7 @@ github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvSc
 github.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U=
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
diff --git a/contrib/config/nacos/go.sum b/contrib/config/nacos/go.sum
index d47d588c..269ca936 100644
--- a/contrib/config/nacos/go.sum
+++ b/contrib/config/nacos/go.sum
@@ -27,11 +27,12 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
 github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
-github.com/go-kratos/aegis v0.1.0/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw=
+github.com/go-kratos/aegis v0.1.1/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw=
 github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
 github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
 github.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U=
 github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
+github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s=
diff --git a/contrib/log/fluent/go.sum b/contrib/log/fluent/go.sum
index f719ca5b..cef40a50 100644
--- a/contrib/log/fluent/go.sum
+++ b/contrib/log/fluent/go.sum
@@ -21,10 +21,11 @@ github.com/fluent/fluent-logger-golang v1.5.0 h1:wBpSzYjRlzgwwAOw/3ig3Jaxrb5xTJl
 github.com/fluent/fluent-logger-golang v1.5.0/go.mod h1:2/HCT/jTy78yGyeNGQLGQsjF3zzzAuy6Xlk6FCMV5eU=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/go-kratos/aegis v0.1.0/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw=
+github.com/go-kratos/aegis v0.1.1/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw=
 github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
 github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
 github.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U=
+github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
diff --git a/contrib/log/zap/go.mod b/contrib/log/zap/go.mod
new file mode 100644
index 00000000..71abb53a
--- /dev/null
+++ b/contrib/log/zap/go.mod
@@ -0,0 +1,10 @@
+module github.com/go-kratos/kratos/contrib/log/zap/v2
+
+go 1.15
+
+require (
+	github.com/go-kratos/kratos/v2 v2.0.5
+	go.uber.org/zap v1.19.0
+)
+
+replace github.com/go-kratos/kratos/v2 => ../../../
diff --git a/contrib/log/zap/go.sum b/contrib/log/zap/go.sum
new file mode 100644
index 00000000..a2bf8ea1
--- /dev/null
+++ b/contrib/log/zap/go.sum
@@ -0,0 +1,188 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
+github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
+github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/go-kratos/aegis v0.1.1/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw=
+github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
+github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U=
+github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
+github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
+github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
+github.com/shirou/gopsutil/v3 v3.21.8/go.mod h1:YWp/H8Qs5fVmf17v7JNZzA0mPJ+mS2e9JdiUF9LlKzQ=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs=
+github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8=
+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+go.opentelemetry.io/otel v1.0.0-RC2/go.mod h1:w1thVQ7qbAy8MHb0IFj8a5Q2QU0l2ksf8u/CN8m3NOM=
+go.opentelemetry.io/otel/sdk v1.0.0-RC2/go.mod h1:fgwHyiDn4e5k40TD9VX243rOxXR+jzsWBZYA2P5jpEw=
+go.opentelemetry.io/otel/trace v1.0.0-RC2/go.mod h1:JPQ+z6nNw9mqEGT8o3eoPTdnNI+Aj5JcxEsVGREIAy4=
+go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
+go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
+go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
+go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
+go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
+go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
+go.uber.org/zap v1.19.0 h1:mZQZefskPPCMIBCSEH0v2/iUqqLrYtaeqwD6FUGUnFE=
+go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
+golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71 h1:ikCpsnYR+Ew0vu99XlDp55lGgDJdIMx3f4a18jfse/s=
+golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA=
+golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
+google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
+google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/contrib/log/zap/zap.go b/contrib/log/zap/zap.go
new file mode 100644
index 00000000..8c091806
--- /dev/null
+++ b/contrib/log/zap/zap.go
@@ -0,0 +1,48 @@
+package zap
+
+import (
+	"fmt"
+
+	"github.com/go-kratos/kratos/v2/log"
+	"go.uber.org/zap"
+)
+
+var _ log.Logger = (*Logger)(nil)
+
+type Logger struct {
+	log *zap.Logger
+}
+
+func NewLogger(zlog *zap.Logger) *Logger {
+	return &Logger{zlog}
+}
+
+func (l *Logger) Log(level log.Level, keyvals ...interface{}) error {
+	if len(keyvals) == 0 || len(keyvals)%2 != 0 {
+		l.log.Warn(fmt.Sprint("Keyvalues must appear in pairs: ", keyvals))
+		return nil
+	}
+
+	var data []zap.Field
+	for i := 0; i < len(keyvals); i += 2 {
+		data = append(data, zap.Any(fmt.Sprint(keyvals[i]), keyvals[i+1]))
+	}
+
+	switch level {
+	case log.LevelDebug:
+		l.log.Debug("", data...)
+	case log.LevelInfo:
+		l.log.Info("", data...)
+	case log.LevelWarn:
+		l.log.Warn("", data...)
+	case log.LevelError:
+		l.log.Error("", data...)
+	case log.LevelFatal:
+		l.log.Fatal("", data...)
+	}
+	return nil
+}
+
+func (l *Logger) Sync() error {
+	return l.log.Sync()
+}
diff --git a/contrib/metrics/datadog/go.sum b/contrib/metrics/datadog/go.sum
index 1b1826f1..a928fe28 100644
--- a/contrib/metrics/datadog/go.sum
+++ b/contrib/metrics/datadog/go.sum
@@ -23,10 +23,11 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/go-kratos/aegis v0.1.0/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw=
+github.com/go-kratos/aegis v0.1.1/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw=
 github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
 github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
 github.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U=
+github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
diff --git a/contrib/metrics/prometheus/go.sum b/contrib/metrics/prometheus/go.sum
index bde8b907..8afc05cc 100644
--- a/contrib/metrics/prometheus/go.sum
+++ b/contrib/metrics/prometheus/go.sum
@@ -68,7 +68,7 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
-github.com/go-kratos/aegis v0.1.0/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw=
+github.com/go-kratos/aegis v0.1.1/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw=
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
@@ -81,6 +81,7 @@ github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFG
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
diff --git a/contrib/registry/consul/go.sum b/contrib/registry/consul/go.sum
index 9ed191d5..86ffd694 100644
--- a/contrib/registry/consul/go.sum
+++ b/contrib/registry/consul/go.sum
@@ -28,10 +28,11 @@ github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
 github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/go-kratos/aegis v0.1.0/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw=
+github.com/go-kratos/aegis v0.1.1/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw=
 github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
 github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
 github.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U=
+github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
diff --git a/contrib/registry/etcd/go.sum b/contrib/registry/etcd/go.sum
index e4648253..60e22bba 100644
--- a/contrib/registry/etcd/go.sum
+++ b/contrib/registry/etcd/go.sum
@@ -39,7 +39,7 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
-github.com/go-kratos/aegis v0.1.0/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw=
+github.com/go-kratos/aegis v0.1.1/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw=
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
@@ -51,6 +51,7 @@ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
diff --git a/contrib/registry/etcd/registry.go b/contrib/registry/etcd/registry.go
index db593e59..cdde7665 100644
--- a/contrib/registry/etcd/registry.go
+++ b/contrib/registry/etcd/registry.go
@@ -109,13 +109,13 @@ func (r *Registry) GetService(ctx context.Context, name string) ([]*registry.Ser
 	if err != nil {
 		return nil, err
 	}
-	var items []*registry.ServiceInstance
-	for _, kv := range resp.Kvs {
+	items := make([]*registry.ServiceInstance, len(resp.Kvs))
+	for i, kv := range resp.Kvs {
 		si, err := unmarshal(kv.Value)
 		if err != nil {
 			return nil, err
 		}
-		items = append(items, si)
+		items[i] = si
 	}
 	return items, nil
 }
@@ -123,7 +123,7 @@ func (r *Registry) GetService(ctx context.Context, name string) ([]*registry.Ser
 // Watch creates a watcher according to the service name.
 func (r *Registry) Watch(ctx context.Context, name string) (registry.Watcher, error) {
 	key := fmt.Sprintf("%s/%s", r.opts.namespace, name)
-	return newWatcher(ctx, key, r.client), nil
+	return newWatcher(ctx, key, r.client)
 }
 
 // registerWithKV create a new lease, return current leaseID
@@ -161,9 +161,9 @@ func (r *Registry) heartBeat(ctx context.Context, leaseID clientv3.LeaseID, key
 				cancelCtx, cancel := context.WithCancel(ctx)
 				go func() {
 					defer cancel()
-					id, err := r.registerWithKV(cancelCtx, key, value)
-					if err != nil {
-						errChan <- err
+					id, registerErr := r.registerWithKV(cancelCtx, key, value)
+					if registerErr != nil {
+						errChan <- registerErr
 					} else {
 						idChan <- id
 					}
diff --git a/contrib/registry/etcd/watcher.go b/contrib/registry/etcd/watcher.go
index aa6562b1..0e7fefd3 100644
--- a/contrib/registry/etcd/watcher.go
+++ b/contrib/registry/etcd/watcher.go
@@ -7,9 +7,7 @@ import (
 	clientv3 "go.etcd.io/etcd/client/v3"
 )
 
-var (
-	_ registry.Watcher = &watcher{}
-)
+var _ registry.Watcher = &watcher{}
 
 type watcher struct {
 	key       string
@@ -21,7 +19,7 @@ type watcher struct {
 	first     bool
 }
 
-func newWatcher(ctx context.Context, key string, client *clientv3.Client) *watcher {
+func newWatcher(ctx context.Context, key string, client *clientv3.Client) (*watcher, error) {
 	w := &watcher{
 		key:     key,
 		watcher: clientv3.NewWatcher(client),
@@ -30,8 +28,11 @@ func newWatcher(ctx context.Context, key string, client *clientv3.Client) *watch
 	}
 	w.ctx, w.cancel = context.WithCancel(ctx)
 	w.watchChan = w.watcher.Watch(w.ctx, key, clientv3.WithPrefix(), clientv3.WithRev(0))
-	w.watcher.RequestProgress(context.Background())
-	return w
+	err := w.watcher.RequestProgress(context.Background())
+	if err != nil {
+		return nil, err
+	}
+	return w, nil
 }
 
 func (w *watcher) Next() ([]*registry.ServiceInstance, error) {
@@ -47,7 +48,6 @@ func (w *watcher) Next() ([]*registry.ServiceInstance, error) {
 	case <-w.watchChan:
 		return w.getInstance()
 	}
-
 }
 
 func (w *watcher) Stop() error {
@@ -60,13 +60,13 @@ func (w *watcher) getInstance() ([]*registry.ServiceInstance, error) {
 	if err != nil {
 		return nil, err
 	}
-	var items []*registry.ServiceInstance
-	for _, kv := range resp.Kvs {
+	items := make([]*registry.ServiceInstance, len(resp.Kvs))
+	for i, kv := range resp.Kvs {
 		si, err := unmarshal(kv.Value)
 		if err != nil {
 			return nil, err
 		}
-		items = append(items, si)
+		items[i] = si
 	}
 	return items, nil
 }
diff --git a/contrib/registry/kubernetes/go.sum b/contrib/registry/kubernetes/go.sum
index fd4d8c2f..4f374a16 100644
--- a/contrib/registry/kubernetes/go.sum
+++ b/contrib/registry/kubernetes/go.sum
@@ -66,7 +66,7 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
 github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
-github.com/go-kratos/aegis v0.1.0/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw=
+github.com/go-kratos/aegis v0.1.1/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw=
 github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
 github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc=
 github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
@@ -78,6 +78,7 @@ github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvSc
 github.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U=
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
diff --git a/contrib/registry/nacos/go.sum b/contrib/registry/nacos/go.sum
index bffb97e3..41bd7d4b 100644
--- a/contrib/registry/nacos/go.sum
+++ b/contrib/registry/nacos/go.sum
@@ -27,11 +27,12 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
 github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
-github.com/go-kratos/aegis v0.1.0/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw=
+github.com/go-kratos/aegis v0.1.1/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw=
 github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
 github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
 github.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U=
 github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
+github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s=
diff --git a/contrib/registry/zookeeper/go.sum b/contrib/registry/zookeeper/go.sum
index d9de5d62..c2ca4cd3 100644
--- a/contrib/registry/zookeeper/go.sum
+++ b/contrib/registry/zookeeper/go.sum
@@ -17,12 +17,13 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/go-kratos/aegis v0.1.0/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw=
+github.com/go-kratos/aegis v0.1.1/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw=
 github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
 github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
 github.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U=
 github.com/go-zookeeper/zk v1.0.2 h1:4mx0EYENAdX/B/rbunjlt5+4RTA/a9SMHBRuSKdGxPM=
 github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw=
+github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
diff --git a/errors/errors.go b/errors/errors.go
index 8b2e7f55..64466fcb 100644
--- a/errors/errors.go
+++ b/errors/errors.go
@@ -5,7 +5,6 @@ import (
 	"fmt"
 
 	httpstatus "github.com/go-kratos/kratos/v2/transport/http/status"
-
 	"google.golang.org/genproto/googleapis/rpc/errdetails"
 	"google.golang.org/grpc/status"
 	"google.golang.org/protobuf/proto"
@@ -102,16 +101,19 @@ func FromError(err error) *Error {
 	}
 	gs, ok := status.FromError(err)
 	if ok {
+		ret := New(
+			httpstatus.FromGRPCCode(gs.Code()),
+			UnknownReason,
+			gs.Message(),
+		)
 		for _, detail := range gs.Details() {
 			switch d := detail.(type) {
 			case *errdetails.ErrorInfo:
-				return New(
-					httpstatus.FromGRPCCode(gs.Code()),
-					d.Reason,
-					gs.Message(),
-				).WithMetadata(d.Metadata)
+				ret.Reason = d.Reason
+				return ret.WithMetadata(d.Metadata)
 			}
 		}
+		return ret
 	}
 	return New(UnknownCode, UnknownReason, err.Error())
 }
diff --git a/examples/auth/jwt/main.go b/examples/auth/jwt/main.go
new file mode 100644
index 00000000..b1cd22eb
--- /dev/null
+++ b/examples/auth/jwt/main.go
@@ -0,0 +1,68 @@
+package main
+
+import (
+	"context"
+	"log"
+
+	"github.com/go-kratos/kratos/examples/helloworld/helloworld"
+	"github.com/go-kratos/kratos/v2"
+	"github.com/go-kratos/kratos/v2/middleware/auth/jwt"
+	"github.com/go-kratos/kratos/v2/transport/grpc"
+	"github.com/go-kratos/kratos/v2/transport/http"
+	jwtv4 "github.com/golang-jwt/jwt/v4"
+)
+
+type server struct {
+	helloworld.UnimplementedGreeterServer
+
+	hc helloworld.GreeterClient
+}
+
+func (s *server) SayHello(ctx context.Context, in *helloworld.HelloRequest) (*helloworld.HelloReply, error) {
+	return &helloworld.HelloReply{Message: "hello from service"}, nil
+}
+
+func main() {
+	testKey := "testKey"
+	httpSrv := http.NewServer(
+		http.Address(":8000"),
+		http.Middleware(
+			jwt.Server(func(token *jwtv4.Token) (interface{}, error) {
+				return []byte(testKey), nil
+			}),
+		),
+	)
+	grpcSrv := grpc.NewServer(
+		grpc.Address(":9000"),
+		grpc.Middleware(
+			jwt.Server(func(token *jwtv4.Token) (interface{}, error) {
+				return []byte(testKey), nil
+			}),
+		),
+	)
+	serviceTestKey := "serviceTestKey"
+	con, _ := grpc.DialInsecure(
+		context.Background(),
+		grpc.WithEndpoint("dns:///127.0.0.1:9001"),
+		grpc.WithMiddleware(
+			jwt.Client(func(token *jwtv4.Token) (interface{}, error) {
+				return []byte(serviceTestKey), nil
+			}),
+		),
+	)
+	s := &server{
+		hc: helloworld.NewGreeterClient(con),
+	}
+	helloworld.RegisterGreeterServer(grpcSrv, s)
+	helloworld.RegisterGreeterHTTPServer(httpSrv, s)
+	app := kratos.New(
+		kratos.Name("helloworld"),
+		kratos.Server(
+			httpSrv,
+			grpcSrv,
+		),
+	)
+	if err := app.Run(); err != nil {
+		log.Fatal(err)
+	}
+}
diff --git a/examples/benchmark/main.go b/examples/benchmark/main.go
index fa3ae4f5..08021c77 100644
--- a/examples/benchmark/main.go
+++ b/examples/benchmark/main.go
@@ -36,17 +36,17 @@ func main() {
 	if argsLen > 1 {
 		webFramework = args[1]
 	}
-	if argsLen > 2 {
+	if argsLen > 2 { //nolint:gomnd
 		sleepTime, _ = strconv.Atoi(args[2])
 		if sleepTime == -1 {
 			cpuBound = true
 			sleepTime = 0
 		}
 	}
-	if argsLen > 3 {
+	if argsLen > 3 { //nolint:gomnd
 		port, _ = strconv.Atoi(args[3])
 	}
-	if argsLen > 4 {
+	if argsLen > 4 { //nolint:gomnd
 		samplingPoint, _ = strconv.Atoi(args[4])
 	}
 	sleepTimeDuration = time.Duration(sleepTime) * time.Millisecond
@@ -108,14 +108,14 @@ func kratosHandle(w http2.ResponseWriter, q *http2.Request) {
 			runtime.Gosched()
 		}
 	}
-	w.Write(message)
+	_, _ = w.Write(message)
 }
 
 func GinServer() {
 	gin.SetMode(gin.ReleaseMode)
 	mux := gin.New()
 	mux.GET("/", ginHandler)
-	mux.Run(":" + strconv.Itoa(port))
+	_ = mux.Run(":" + strconv.Itoa(port))
 }
 
 func ginHandler(c *gin.Context) {
@@ -128,13 +128,13 @@ func ginHandler(c *gin.Context) {
 			runtime.Gosched()
 		}
 	}
-	c.Writer.Write(message)
+	_, _ = c.Writer.Write(message)
 }
 
 func EchoServer() {
 	e := echo.New()
 	e.GET("/", echoHandler)
-	e.Start(":" + strconv.Itoa(port))
+	_ = e.Start(":" + strconv.Itoa(port))
 }
 
 func echoHandler(c echo.Context) error {
@@ -147,7 +147,7 @@ func echoHandler(c echo.Context) error {
 			runtime.Gosched()
 		}
 	}
-	c.Response().Write(message)
+	_, _ = c.Response().Write(message)
 	return nil
 }
 
@@ -155,7 +155,7 @@ func MuxServer() {
 	r := mux.NewRouter()
 	r.HandleFunc("/", muxHandle)
 	http2.Handle("/", r)
-	http2.ListenAndServe(":"+strconv.Itoa(port), r)
+	_ = http2.ListenAndServe(":"+strconv.Itoa(port), r)
 }
 
 func muxHandle(w http2.ResponseWriter, q *http2.Request) {
@@ -168,7 +168,7 @@ func muxHandle(w http2.ResponseWriter, q *http2.Request) {
 			runtime.Gosched()
 		}
 	}
-	w.Write(message)
+	_, _ = w.Write(message)
 }
 
 func pow(targetBits int) {
diff --git a/examples/blog/internal/biz/article.go b/examples/blog/internal/biz/article.go
index 291d2e25..bf5a6bc5 100644
--- a/examples/blog/internal/biz/article.go
+++ b/examples/blog/internal/biz/article.go
@@ -2,12 +2,13 @@ package biz
 
 import (
 	"context"
-	"github.com/go-kratos/kratos/v2/log"
 	"time"
+
+	"github.com/go-kratos/kratos/v2/log"
 )
 
 type Article struct {
-	Id        int64
+	ID        int64
 	Title     string
 	Content   string
 	CreatedAt time.Time
diff --git a/examples/blog/internal/data/article.go b/examples/blog/internal/data/article.go
index d951746a..c13cdf87 100644
--- a/examples/blog/internal/data/article.go
+++ b/examples/blog/internal/data/article.go
@@ -29,7 +29,7 @@ func (ar *articleRepo) ListArticle(ctx context.Context) ([]*biz.Article, error)
 	rv := make([]*biz.Article, 0)
 	for _, p := range ps {
 		rv = append(rv, &biz.Article{
-			Id:        p.ID,
+			ID:        p.ID,
 			Title:     p.Title,
 			Content:   p.Content,
 			CreatedAt: p.CreatedAt,
@@ -45,7 +45,7 @@ func (ar *articleRepo) GetArticle(ctx context.Context, id int64) (*biz.Article,
 		return nil, err
 	}
 	return &biz.Article{
-		Id:        p.ID,
+		ID:        p.ID,
 		Title:     p.Title,
 		Content:   p.Content,
 		CreatedAt: p.CreatedAt,
diff --git a/examples/blog/internal/data/data.go b/examples/blog/internal/data/data.go
index 0bbaa829..dc92829e 100644
--- a/examples/blog/internal/data/data.go
+++ b/examples/blog/internal/data/data.go
@@ -16,6 +16,7 @@ import (
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/attribute"
 	"go.opentelemetry.io/otel/trace"
+
 	// init mysql driver
 	_ "github.com/go-sql-driver/mysql"
 )
diff --git a/examples/blog/internal/data/ent/schema/article.go b/examples/blog/internal/data/ent/schema/article.go
index b43ca34f..79578526 100644
--- a/examples/blog/internal/data/ent/schema/article.go
+++ b/examples/blog/internal/data/ent/schema/article.go
@@ -1,11 +1,12 @@
 package schema
 
 import (
+	"time"
+
 	"entgo.io/ent"
 	"entgo.io/ent/dialect"
 	"entgo.io/ent/schema/edge"
 	"entgo.io/ent/schema/field"
-	"time"
 )
 
 // Article holds the schema definition for the Article entity.
diff --git a/examples/blog/internal/data/ent/schema/comment.go b/examples/blog/internal/data/ent/schema/comment.go
index 0f5e2c02..37eafb7b 100644
--- a/examples/blog/internal/data/ent/schema/comment.go
+++ b/examples/blog/internal/data/ent/schema/comment.go
@@ -1,11 +1,12 @@
 package schema
 
 import (
+	"time"
+
 	"entgo.io/ent"
 	"entgo.io/ent/dialect"
 	"entgo.io/ent/schema/edge"
 	"entgo.io/ent/schema/field"
-	"time"
 )
 
 // Comment holds the schema definition for the Comment entity.
diff --git a/examples/blog/internal/data/ent/schema/tag.go b/examples/blog/internal/data/ent/schema/tag.go
index 229ae6d9..3db2cb6b 100644
--- a/examples/blog/internal/data/ent/schema/tag.go
+++ b/examples/blog/internal/data/ent/schema/tag.go
@@ -1,11 +1,12 @@
 package schema
 
 import (
+	"time"
+
 	"entgo.io/ent"
 	"entgo.io/ent/dialect"
 	"entgo.io/ent/schema/edge"
 	"entgo.io/ent/schema/field"
-	"time"
 )
 
 // Tag holds the schema definition for the Tag entity.
diff --git a/examples/blog/internal/data/redis.go b/examples/blog/internal/data/redis.go
index 088290e2..3bbffa9a 100644
--- a/examples/blog/internal/data/redis.go
+++ b/examples/blog/internal/data/redis.go
@@ -3,6 +3,7 @@ package data
 import (
 	"context"
 	"fmt"
+
 	"github.com/go-redis/redis/v8"
 )
 
diff --git a/examples/blog/internal/server/grpc.go b/examples/blog/internal/server/grpc.go
index 6b14380d..4bf89fab 100644
--- a/examples/blog/internal/server/grpc.go
+++ b/examples/blog/internal/server/grpc.go
@@ -14,7 +14,7 @@ import (
 
 // NewGRPCServer new a gRPC server.
 func NewGRPCServer(c *conf.Server, logger log.Logger, blog *service.BlogService) *grpc.Server {
-	var opts = []grpc.ServerOption{
+	opts := []grpc.ServerOption{
 		grpc.Middleware(
 			recovery.Recovery(),
 			tracing.Server(),
diff --git a/examples/blog/internal/server/http.go b/examples/blog/internal/server/http.go
index 340713fd..f17a882c 100644
--- a/examples/blog/internal/server/http.go
+++ b/examples/blog/internal/server/http.go
@@ -14,7 +14,7 @@ import (
 
 // NewHTTPServer new a HTTP server.
 func NewHTTPServer(c *conf.Server, logger log.Logger, blog *service.BlogService) *http.Server {
-	var opts = []http.ServerOption{
+	opts := []http.ServerOption{
 		http.Middleware(
 			recovery.Recovery(),
 			tracing.Server(),
diff --git a/examples/blog/internal/service/article.go b/examples/blog/internal/service/article.go
index e72a7d18..fd16fd07 100644
--- a/examples/blog/internal/service/article.go
+++ b/examples/blog/internal/service/article.go
@@ -50,7 +50,7 @@ func (s *BlogService) GetArticle(ctx context.Context, req *pb.GetArticleRequest)
 	if err != nil {
 		return nil, err
 	}
-	return &pb.GetArticleReply{Article: &pb.Article{Id: p.Id, Title: p.Title, Content: p.Content, Like: p.Like}}, nil
+	return &pb.GetArticleReply{Article: &pb.Article{Id: p.ID, Title: p.Title, Content: p.Content, Like: p.Like}}, nil
 }
 
 func (s *BlogService) ListArticle(ctx context.Context, req *pb.ListArticleRequest) (*pb.ListArticleReply, error) {
@@ -58,7 +58,7 @@ func (s *BlogService) ListArticle(ctx context.Context, req *pb.ListArticleReques
 	reply := &pb.ListArticleReply{}
 	for _, p := range ps {
 		reply.Results = append(reply.Results, &pb.Article{
-			Id:      p.Id,
+			Id:      p.ID,
 			Title:   p.Title,
 			Content: p.Content,
 		})
diff --git a/examples/config/apollo/main.go b/examples/config/apollo/main.go
index 23d0c7db..1e756aef 100644
--- a/examples/config/apollo/main.go
+++ b/examples/config/apollo/main.go
@@ -28,6 +28,8 @@ func main() {
 	if err != nil {
 		panic(err)
 	}
+	log.Printf("service: %s", name)
+
 	// Defines the config JSON Field
 	var v struct {
 		Name    string `json:"name"`
@@ -35,7 +37,7 @@ func main() {
 	}
 
 	// Unmarshal the config to struct
-	if err := c.Scan(&v); err != nil {
+	if err = c.Scan(&v); err != nil {
 		panic(err)
 	}
 	log.Printf("config: %+v", v)
@@ -48,12 +50,12 @@ func main() {
 	log.Printf("service: %s", name)
 
 	// watch key
-	if err := c.Watch("name", func(key string, value config.Value) {
-		name, err := value.String()
-		if err != nil {
-			panic(err)
+	if err = c.Watch("name", func(key string, value config.Value) {
+		n, e := value.String()
+		if e != nil {
+			panic(e)
 		}
-		log.Printf("config changed: %s = %s\n", key, name)
+		log.Printf("config changed: %s = %s\n", key, n)
 	}); err != nil {
 		panic(err)
 	}
diff --git a/examples/errors/client/main.go b/examples/errors/client/main.go
index b9b9807e..54df1de6 100644
--- a/examples/errors/client/main.go
+++ b/examples/errors/client/main.go
@@ -4,6 +4,8 @@ import (
 	"context"
 	"log"
 
+	stdhttp "net/http"
+
 	"github.com/go-kratos/kratos/examples/errors/api"
 	pb "github.com/go-kratos/kratos/examples/helloworld/helloworld"
 	"github.com/go-kratos/kratos/v2/errors"
@@ -27,7 +29,7 @@ func callHTTP() {
 	client := pb.NewGreeterHTTPClient(conn)
 	reply, err := client.SayHello(context.Background(), &pb.HelloRequest{Name: "empty"})
 	if err != nil {
-		if errors.Code(err) == 500 {
+		if errors.Code(err) == stdhttp.StatusInternalServerError {
 			log.Println(err)
 		}
 		if api.IsUserNotFound(err) {
diff --git a/examples/errors/server/main.go b/examples/errors/server/main.go
index 45f7ea6a..089af6c0 100644
--- a/examples/errors/server/main.go
+++ b/examples/errors/server/main.go
@@ -18,7 +18,7 @@ var (
 	// Name is the name of the compiled software.
 	Name = "errors"
 	// Version is the version of the compiled software.
-	Version = "v1.0.0"
+	// Version = "v1.0.0"
 )
 
 // server is used to implement helloworld.GreeterServer.
diff --git a/examples/event/kafka/kafka.go b/examples/event/kafka/kafka.go
index e4a3b6fe..dd4c0500 100644
--- a/examples/event/kafka/kafka.go
+++ b/examples/event/kafka/kafka.go
@@ -2,14 +2,17 @@ package kafka
 
 import (
 	"context"
+	"log"
+
 	"github.com/go-kratos/kratos/examples/event/event"
 	"github.com/segmentio/kafka-go"
-	"log"
 )
 
-var _ event.Sender = (*kafkaSender)(nil)
-var _ event.Receiver = (*kafkaReceiver)(nil)
-var _ event.Event = (*Message)(nil)
+var (
+	_ event.Sender   = (*kafkaSender)(nil)
+	_ event.Receiver = (*kafkaReceiver)(nil)
+	_ event.Event    = (*Message)(nil)
+)
 
 type Message struct {
 	key   string
diff --git a/examples/event/memory/memory.go b/examples/event/memory/memory.go
index 1b3b0800..67fe471a 100644
--- a/examples/event/memory/memory.go
+++ b/examples/event/memory/memory.go
@@ -2,14 +2,17 @@ package memory
 
 import (
 	"context"
-	"github.com/go-kratos/kratos/examples/event/event"
 	"log"
 	"sync"
+
+	"github.com/go-kratos/kratos/examples/event/event"
 )
 
-var _ event.Sender = (*memorySender)(nil)
-var _ event.Receiver = (*memoryReceiver)(nil)
-var _ event.Event = (*Message)(nil)
+var (
+	_ event.Sender   = (*memorySender)(nil)
+	_ event.Receiver = (*memoryReceiver)(nil)
+	_ event.Event    = (*Message)(nil)
+)
 
 var (
 	chanMap = struct {
@@ -47,6 +50,7 @@ func (m *memorySender) Send(ctx context.Context, msg event.Event) error {
 	}
 	return nil
 }
+
 func (m *memorySender) Close() error {
 	return nil
 }
@@ -74,9 +78,9 @@ func (m *memoryReceiver) Close() error {
 func NewMemory(topic string) (event.Sender, event.Receiver) {
 	chanMap.RLock()
 	if _, ok := chanMap.cm[topic]; !ok {
-		//chanMap.Lock()
+		// chanMap.Lock()
 		chanMap.cm[topic] = make(chan *Message, ChanSize)
-		//chanMap.Unlock()
+		// chanMap.Unlock()
 	}
 	defer chanMap.RUnlock()
 
diff --git a/examples/event/receiver/main.go b/examples/event/receiver/main.go
index af39c345..0497a626 100644
--- a/examples/event/receiver/main.go
+++ b/examples/event/receiver/main.go
@@ -19,10 +19,9 @@ func main() {
 		panic(err)
 	}
 	receive(receiver)
-	select {
-	case <-sigs:
-		_ = receiver.Close()
-	}
+
+	<-sigs
+	_ = receiver.Close()
 }
 
 func receive(receiver event.Receiver) {
diff --git a/examples/event/sender/main.go b/examples/event/sender/main.go
index 4443306a..3cd6ecfa 100644
--- a/examples/event/sender/main.go
+++ b/examples/event/sender/main.go
@@ -3,6 +3,7 @@ package main
 import (
 	"context"
 	"fmt"
+
 	"github.com/go-kratos/kratos/examples/event/event"
 	"github.com/go-kratos/kratos/examples/event/kafka"
 )
diff --git a/examples/go.mod b/examples/go.mod
index be2d8b25..54730a13 100644
--- a/examples/go.mod
+++ b/examples/go.mod
@@ -19,6 +19,7 @@ require (
 	github.com/go-redis/redis/extra/redisotel v0.3.0
 	github.com/go-redis/redis/v8 v8.11.2
 	github.com/go-sql-driver/mysql v1.6.0
+	github.com/golang-jwt/jwt/v4 v4.0.0
 	github.com/google/wire v0.5.0
 	github.com/gorilla/handlers v1.5.1
 	github.com/gorilla/mux v1.8.0
diff --git a/examples/go.sum b/examples/go.sum
index 9042089c..e58693d9 100644
--- a/examples/go.sum
+++ b/examples/go.sum
@@ -164,7 +164,7 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
 github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
-github.com/go-kratos/aegis v0.1.0/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw=
+github.com/go-kratos/aegis v0.1.1/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw=
 github.com/go-kratos/gin v0.1.0 h1:yq5GfZnSNo8cOIqxqPE0FVNQ8fm++oKQBd3/rTTp4oI=
 github.com/go-kratos/gin v0.1.0/go.mod h1:KzE88+mV1QJdmiT+sU2D560ucOXnWZx+4m0OnVcSyzQ=
 github.com/go-kratos/grpc-gateway/v2 v2.0.0-20210804092615-bdc92eb5ce83 h1:YN1KsqX3mB8hkSrOLZv3xZaZh+lZ/L7QC00RATFLJSg=
@@ -209,7 +209,10 @@ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zV
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
 github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
+github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
 github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
+github.com/golang-jwt/jwt/v4 v4.0.0 h1:RAqyYixv1p7uEnocuy8P1nru5wprCh/MH2BIlW5z5/o=
+github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/glog v0.0.0-20210429001901-424d2337a529 h1:2voWjNECnrZRbfwXxHB1/j8wa6xdKn85B5NzgVL/pTU=
 github.com/golang/glog v0.0.0-20210429001901-424d2337a529/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
diff --git a/examples/header/server/main.go b/examples/header/server/main.go
index 5b2db136..e15c5857 100644
--- a/examples/header/server/main.go
+++ b/examples/header/server/main.go
@@ -17,7 +17,7 @@ var (
 	// Name is the name of the compiled software.
 	Name = "helloworld"
 	// Version is the version of the compiled software.
-	Version = "v1.0.0"
+	// Version = "v1.0.0"
 )
 
 // server is used to implement helloworld.GreeterServer.
diff --git a/examples/helloworld/client/main.go b/examples/helloworld/client/main.go
index 1be1c779..a371acb3 100644
--- a/examples/helloworld/client/main.go
+++ b/examples/helloworld/client/main.go
@@ -36,7 +36,7 @@ func callHTTP() {
 	log.Printf("[http] SayHello %s\n", reply.Message)
 
 	// returns error
-	reply, err = client.SayHello(context.Background(), &pb.HelloRequest{Name: "error"})
+	_, err = client.SayHello(context.Background(), &pb.HelloRequest{Name: "error"})
 	if err != nil {
 		log.Printf("[http] SayHello error: %v\n", err)
 	}
diff --git a/examples/helloworld/server/main.go b/examples/helloworld/server/main.go
index f60cdfcb..33a876b7 100644
--- a/examples/helloworld/server/main.go
+++ b/examples/helloworld/server/main.go
@@ -18,7 +18,7 @@ var (
 	// Name is the name of the compiled software.
 	Name = "helloworld"
 	// Version is the version of the compiled software.
-	Version = "v1.0.0"
+	// Version = "v1.0.0"
 )
 
 // server is used to implement helloworld.GreeterServer.
diff --git a/examples/http/echo/main.go b/examples/http/echo/main.go
index ed359912..ebc8117b 100644
--- a/examples/http/echo/main.go
+++ b/examples/http/echo/main.go
@@ -1,10 +1,11 @@
 package main
 
 import (
+	"log"
+
 	"github.com/go-kratos/kratos/v2"
 	"github.com/go-kratos/kratos/v2/transport/http"
 	"github.com/labstack/echo/v4"
-	"log"
 )
 
 func main() {
diff --git a/examples/http/errors/main.go b/examples/http/errors/main.go
index 335bc7d9..09afb89f 100644
--- a/examples/http/errors/main.go
+++ b/examples/http/errors/main.go
@@ -41,7 +41,7 @@ func errorEncoder(w stdhttp.ResponseWriter, r *stdhttp.Request, err error) {
 	}
 	w.Header().Set("Content-Type", "application/"+codec.Name())
 	w.WriteHeader(se.Code)
-	w.Write(body)
+	_, _ = w.Write(body)
 }
 
 func main() {
diff --git a/examples/http/upload/main.go b/examples/http/upload/main.go
index 794c9691..db97ba37 100644
--- a/examples/http/upload/main.go
+++ b/examples/http/upload/main.go
@@ -19,7 +19,7 @@ func uploadFile(ctx http.Context) error {
 	}
 	defer file.Close()
 
-	f, err := os.OpenFile(handler.Filename, os.O_WRONLY|os.O_CREATE, 0666)
+	f, err := os.OpenFile(handler.Filename, os.O_WRONLY|os.O_CREATE, 0o666)
 	if err != nil {
 		return err
 	}
diff --git a/examples/i18n/internal/biz/greeter.go b/examples/i18n/internal/biz/greeter.go
index e8202139..8b35979e 100644
--- a/examples/i18n/internal/biz/greeter.go
+++ b/examples/i18n/internal/biz/greeter.go
@@ -2,6 +2,7 @@ package biz
 
 import (
 	"context"
+
 	"github.com/go-kratos/kratos/v2/log"
 )
 
diff --git a/examples/i18n/internal/data/data.go b/examples/i18n/internal/data/data.go
index d45fc5da..269e9f18 100644
--- a/examples/i18n/internal/data/data.go
+++ b/examples/i18n/internal/data/data.go
@@ -10,14 +10,13 @@ import (
 var ProviderSet = wire.NewSet(NewData, NewGreeterRepo)
 
 // Data .
-type Data struct {
-	// TODO wrapped database client
+type Data struct { // TODO wrapped database client
 }
 
 // NewData .
 func NewData(c *conf.Data, logger log.Logger) (*Data, func(), error) {
 	cleanup := func() {
-		logger.Log(log.LevelInfo, "closing the data resources")
+		_ = logger.Log(log.LevelInfo, "closing the data resources")
 	}
 	return &Data{}, cleanup, nil
 }
diff --git a/examples/i18n/internal/data/greeter.go b/examples/i18n/internal/data/greeter.go
index 2fd947ef..20002cb9 100644
--- a/examples/i18n/internal/data/greeter.go
+++ b/examples/i18n/internal/data/greeter.go
@@ -2,6 +2,7 @@ package data
 
 import (
 	"context"
+
 	"github.com/go-kratos/kratos/examples/i18n/internal/biz"
 	"github.com/go-kratos/kratos/v2/log"
 )
diff --git a/examples/i18n/internal/pkg/middleware/localize/localize.go b/examples/i18n/internal/pkg/middleware/localize/localize.go
index c2d611bc..d865a647 100644
--- a/examples/i18n/internal/pkg/middleware/localize/localize.go
+++ b/examples/i18n/internal/pkg/middleware/localize/localize.go
@@ -2,6 +2,7 @@ package localize
 
 import (
 	"context"
+
 	"github.com/BurntSushi/toml"
 	"github.com/nicksnyder/go-i18n/v2/i18n"
 	"golang.org/x/text/language"
diff --git a/examples/i18n/internal/server/grpc.go b/examples/i18n/internal/server/grpc.go
index a8da23ae..2858baec 100644
--- a/examples/i18n/internal/server/grpc.go
+++ b/examples/i18n/internal/server/grpc.go
@@ -15,7 +15,7 @@ import (
 
 // NewGRPCServer new a gRPC server.
 func NewGRPCServer(c *conf.Server, greeter *service.GreeterService, logger log.Logger) *grpc.Server {
-	var opts = []grpc.ServerOption{
+	opts := []grpc.ServerOption{
 		grpc.Middleware(
 			recovery.Recovery(),
 			tracing.Server(),
diff --git a/examples/i18n/internal/server/http.go b/examples/i18n/internal/server/http.go
index 28aaa568..cb94fdc3 100644
--- a/examples/i18n/internal/server/http.go
+++ b/examples/i18n/internal/server/http.go
@@ -16,7 +16,7 @@ import (
 
 // NewHTTPServer new a HTTP server.
 func NewHTTPServer(c *conf.Server, greeter *service.GreeterService, logger log.Logger) *http.Server {
-	var opts = []http.ServerOption{
+	opts := []http.ServerOption{
 		http.Middleware(
 			recovery.Recovery(),
 			tracing.Server(),
diff --git a/examples/i18n/internal/service/greeter.go b/examples/i18n/internal/service/greeter.go
index 030e4b52..8ddf181d 100644
--- a/examples/i18n/internal/service/greeter.go
+++ b/examples/i18n/internal/service/greeter.go
@@ -2,10 +2,11 @@ package service
 
 import (
 	"context"
+
 	"github.com/go-kratos/kratos/examples/i18n/internal/pkg/middleware/localize"
 	"github.com/nicksnyder/go-i18n/v2/i18n"
 
-	"github.com/go-kratos/kratos/examples/i18n/api/helloworld/v1"
+	v1 "github.com/go-kratos/kratos/examples/i18n/api/helloworld/v1"
 	"github.com/go-kratos/kratos/examples/i18n/internal/biz"
 	"github.com/go-kratos/kratos/v2/log"
 )
diff --git a/examples/metadata/server/main.go b/examples/metadata/server/main.go
index e8ef1daa..3c92f8ba 100644
--- a/examples/metadata/server/main.go
+++ b/examples/metadata/server/main.go
@@ -18,7 +18,7 @@ var (
 	// Name is the name of the compiled software.
 	Name = "helloworld"
 	// Version is the version of the compiled software.
-	Version = "v1.0.0"
+	// Version = "v1.0.0"
 )
 
 // server is used to implement helloworld.GreeterServer.
diff --git a/examples/metrics/main.go b/examples/metrics/main.go
index 1d3824c4..c7a84d80 100644
--- a/examples/metrics/main.go
+++ b/examples/metrics/main.go
@@ -21,7 +21,7 @@ var (
 	// Name is the name of the compiled software.
 	Name = "metrics"
 	// Version is the version of the compiled software.
-	Version = "v1.0.0"
+	// Version = "v1.0.0"
 
 	_metricSeconds = prometheus.NewHistogramVec(prometheus.HistogramOpts{
 		Namespace: "server",
diff --git a/examples/registry/consul/client/main.go b/examples/registry/consul/client/main.go
index 10ceaee0..8a81efb2 100644
--- a/examples/registry/consul/client/main.go
+++ b/examples/registry/consul/client/main.go
@@ -14,19 +14,13 @@ import (
 )
 
 func main() {
-	client, err := api.NewClient(api.DefaultConfig())
+	consulCli, err := api.NewClient(api.DefaultConfig())
 	if err != nil {
 		panic(err)
 	}
-	for {
-		callHTTP(client)
-		callGRPC(client)
-		time.Sleep(time.Second)
-	}
-}
+	r := consul.New(consulCli)
 
-func callGRPC(cli *api.Client) {
-	r := consul.New(cli)
+	// new grpc client
 	conn, err := grpc.DialInsecure(
 		context.Background(),
 		grpc.WithEndpoint("discovery:///helloworld"),
@@ -36,17 +30,10 @@ func callGRPC(cli *api.Client) {
 		log.Fatal(err)
 	}
 	defer conn.Close()
-	client := helloworld.NewGreeterClient(conn)
-	reply, err := client.SayHello(context.Background(), &helloworld.HelloRequest{Name: "kratos"})
-	if err != nil {
-		log.Fatal(err)
-	}
-	log.Printf("[grpc] SayHello %+v\n", reply)
-}
+	gClient := helloworld.NewGreeterClient(conn)
 
-func callHTTP(cli *api.Client) {
-	r := consul.New(cli)
-	conn, err := http.NewClient(
+	// new http client
+	hConn, err := http.NewClient(
 		context.Background(),
 		http.WithMiddleware(
 			recovery.Recovery(),
@@ -57,13 +44,28 @@ func callHTTP(cli *api.Client) {
 	if err != nil {
 		log.Fatal(err)
 	}
-	defer conn.Close()
-	time.Sleep(time.Millisecond * 250)
-	client := helloworld.NewGreeterHTTPClient(conn)
+	defer hConn.Close()
+	hClient := helloworld.NewGreeterHTTPClient(hConn)
+
+	for {
+		time.Sleep(time.Second)
+		callGRPC(gClient)
+		callHTTP(hClient)
+	}
+}
+
+func callGRPC(client helloworld.GreeterClient) {
 	reply, err := client.SayHello(context.Background(), &helloworld.HelloRequest{Name: "kratos"})
 	if err != nil {
 		log.Fatal(err)
 	}
-	log.Printf("[http] SayHello %s\n", reply.Message)
+	log.Printf("[grpc] SayHello %+v\n", reply)
+}
 
+func callHTTP(client helloworld.GreeterHTTPClient) {
+	reply, err := client.SayHello(context.Background(), &helloworld.HelloRequest{Name: "kratos"})
+	if err != nil {
+		log.Fatal(err)
+	}
+	log.Printf("[http] SayHello %s\n", reply.Message)
 }
diff --git a/examples/registry/consul/server/main.go b/examples/registry/consul/server/main.go
index 6da9587f..6c19c00c 100644
--- a/examples/registry/consul/server/main.go
+++ b/examples/registry/consul/server/main.go
@@ -3,11 +3,13 @@ package main
 import (
 	"context"
 	"fmt"
-	"log"
+	"os"
 
 	"github.com/go-kratos/kratos/contrib/registry/consul/v2"
 	"github.com/go-kratos/kratos/examples/helloworld/helloworld"
 	"github.com/go-kratos/kratos/v2"
+	"github.com/go-kratos/kratos/v2/log"
+	"github.com/go-kratos/kratos/v2/middleware/logging"
 	"github.com/go-kratos/kratos/v2/middleware/recovery"
 	"github.com/go-kratos/kratos/v2/transport/grpc"
 	"github.com/go-kratos/kratos/v2/transport/http"
@@ -25,6 +27,9 @@ func (s *server) SayHello(ctx context.Context, in *helloworld.HelloRequest) (*he
 }
 
 func main() {
+	logger := log.NewStdLogger(os.Stdout)
+	log := log.NewHelper(logger)
+
 	consulClient, err := api.NewClient(api.DefaultConfig())
 	if err != nil {
 		log.Fatal(err)
@@ -34,12 +39,14 @@ func main() {
 		http.Address(":8000"),
 		http.Middleware(
 			recovery.Recovery(),
+			logging.Server(logger),
 		),
 	)
 	grpcSrv := grpc.NewServer(
 		grpc.Address(":9000"),
 		grpc.Middleware(
 			recovery.Recovery(),
+			logging.Server(logger),
 		),
 	)
 
diff --git a/examples/stream/client/main.go b/examples/stream/client/main.go
index 5086ae7e..20a78c9c 100644
--- a/examples/stream/client/main.go
+++ b/examples/stream/client/main.go
@@ -74,7 +74,6 @@ func uploadLog(client hello.HelloClient) {
 		time.Sleep(time.Millisecond * 50)
 		number++
 	}
-	return
 }
 
 func chat(client hello.HelloClient) {
diff --git a/examples/stream/service/hello.go b/examples/stream/service/hello.go
index b0b531ae..cf95a269 100644
--- a/examples/stream/service/hello.go
+++ b/examples/stream/service/hello.go
@@ -28,6 +28,7 @@ func (s *HelloService) GetNumber(req *pb.GetNumberRequest, conn pb.Hello_GetNumb
 		time.Sleep(time.Second)
 	}
 }
+
 func (s *HelloService) UploadLog(conn pb.Hello_UploadLogServer) error {
 	for {
 		req, err := conn.Recv()
@@ -40,6 +41,7 @@ func (s *HelloService) UploadLog(conn pb.Hello_UploadLogServer) error {
 		fmt.Println(req.Log)
 	}
 }
+
 func (s *HelloService) Chat(conn pb.Hello_ChatServer) error {
 	for {
 		req, err := conn.Recv()
diff --git a/examples/swagger/server/main.go b/examples/swagger/server/main.go
index 9c187b89..fad64445 100644
--- a/examples/swagger/server/main.go
+++ b/examples/swagger/server/main.go
@@ -19,7 +19,7 @@ var (
 	// Name is the name of the compiled software.
 	Name = "helloworld"
 	// Version is the version of the compiled software.
-	Version = "v1.0.0"
+	// Version = "v1.0.0"
 )
 
 // server is used to implement helloworld.GreeterServer.
diff --git a/examples/tls/server/main.go b/examples/tls/server/main.go
index e954938b..b6e9cba5 100644
--- a/examples/tls/server/main.go
+++ b/examples/tls/server/main.go
@@ -17,7 +17,7 @@ var (
 	// Name is the name of the compiled software.
 	Name = "helloworld"
 	// Version is the version of the compiled software.
-	Version = "v1.0.0"
+	// Version = "v1.0.0"
 )
 
 // server is used to implement helloworld.GreeterServer.
diff --git a/examples/traces/app/message/main.go b/examples/traces/app/message/main.go
index 7f59c69b..7eb88847 100644
--- a/examples/traces/app/message/main.go
+++ b/examples/traces/app/message/main.go
@@ -25,7 +25,7 @@ var (
 	// Name is the name of the compiled software.
 	Name = "message"
 	// Version is the version of the compiled software.
-	Version = "v1.0.0"
+	// Version = "v1.0.0"
 )
 
 // server is used to implement helloworld.GreeterServer.
diff --git a/examples/traces/app/user/main.go b/examples/traces/app/user/main.go
index ad672a1e..b0ce5d8c 100644
--- a/examples/traces/app/user/main.go
+++ b/examples/traces/app/user/main.go
@@ -29,7 +29,7 @@ var (
 	// Name is the name of the compiled software.
 	Name = "user"
 	// Version is the version of the compiled software.
-	Version = "v1.0.0"
+	// Version = "v1.0.0"
 )
 
 // server is used to implement helloworld.GreeterServer.
diff --git a/examples/validate/main.go b/examples/validate/main.go
index bd42edfb..58904e8f 100644
--- a/examples/validate/main.go
+++ b/examples/validate/main.go
@@ -4,7 +4,7 @@ import (
 	"context"
 	"log"
 
-	"github.com/go-kratos/kratos/examples/validate/api"
+	v1 "github.com/go-kratos/kratos/examples/validate/api"
 	"github.com/go-kratos/kratos/v2"
 	"github.com/go-kratos/kratos/v2/middleware/validate"
 	"github.com/go-kratos/kratos/v2/transport/grpc"
@@ -16,7 +16,7 @@ var (
 	// Name is the name of the compiled software.
 	Name = "errors"
 	// Version is the version of the compiled software.
-	Version = "v1.0.0"
+	// Version = "v1.0.0"
 )
 
 type server struct {
diff --git a/go.mod b/go.mod
index cc429a27..c5d51e85 100644
--- a/go.mod
+++ b/go.mod
@@ -4,8 +4,9 @@ go 1.15
 
 require (
 	github.com/fsnotify/fsnotify v1.4.9
-	github.com/go-kratos/aegis v0.1.0
+	github.com/go-kratos/aegis v0.1.1
 	github.com/go-playground/form/v4 v4.2.0
+	github.com/golang-jwt/jwt/v4 v4.0.0
 	github.com/google/uuid v1.3.0
 	github.com/gorilla/mux v1.8.0
 	github.com/imdario/mergo v0.3.12
diff --git a/go.sum b/go.sum
index 0601ae32..91d27434 100644
--- a/go.sum
+++ b/go.sum
@@ -20,14 +20,16 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
 github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/go-kratos/aegis v0.1.0 h1:Ip1qlSOw/b4o3JOWXQrSgwkjQjuCsirShk6gmzCg+44=
-github.com/go-kratos/aegis v0.1.0/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw=
+github.com/go-kratos/aegis v0.1.1 h1:QnIwUwVMnwIU8hZ+p4LKJnSEd1IE6qBBPTV7fgr3XQI=
+github.com/go-kratos/aegis v0.1.1/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw=
 github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY=
 github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
 github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
 github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
 github.com/go-playground/form/v4 v4.2.0 h1:N1wh+Goz61e6w66vo8vJkQt+uwZSoLz50kZPJWR8eic=
 github.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U=
+github.com/golang-jwt/jwt/v4 v4.0.0 h1:RAqyYixv1p7uEnocuy8P1nru5wprCh/MH2BIlW5z5/o=
+github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
diff --git a/hack/.lintcheck_failures b/hack/.lintcheck_failures
index d97c8777..65c33a88 100644
--- a/hack/.lintcheck_failures
+++ b/hack/.lintcheck_failures
@@ -1,6 +1,3 @@
-./examples
-./cmd/protoc-gen-go-http
-./cmd/kratos
 ./contrib/metrics/datadog
 ./contrib/config/nacos
 ./contrib/config/apollo
@@ -9,4 +6,3 @@
 ./contrib/registry/consul
 ./contrib/registry/kubernetes
 ./contrib/registry/zookeeper
-./contrib/registry/etcd
diff --git a/hack/tools.sh b/hack/tools.sh
index 12a0e9b8..0fd8ad8e 100755
--- a/hack/tools.sh
+++ b/hack/tools.sh
@@ -100,7 +100,7 @@ function tidy() {
 }
 
 function help() {
-	echo "use: lint, test, fix, tidy"
+	echo "use: lint, test, test_coverage, fix, tidy"
 }
 
 case $1 in
diff --git a/middleware/auth/jwt/jwt.go b/middleware/auth/jwt/jwt.go
new file mode 100644
index 00000000..23b648d5
--- /dev/null
+++ b/middleware/auth/jwt/jwt.go
@@ -0,0 +1,150 @@
+package jwt
+
+import (
+	"context"
+	"fmt"
+	"strings"
+
+	"github.com/golang-jwt/jwt/v4"
+
+	"github.com/go-kratos/kratos/v2/errors"
+	"github.com/go-kratos/kratos/v2/middleware"
+	"github.com/go-kratos/kratos/v2/transport"
+)
+
+type authKey struct{}
+
+const (
+
+	// bearerWord the bearer key word for authorization
+	bearerWord string = "Bearer"
+
+	// bearerFormat authorization token format
+	bearerFormat string = "Bearer %s"
+
+	// authorizationKey holds the key used to store the JWT Token in the request header.
+	authorizationKey string = "Authorization"
+)
+
+var (
+	ErrMissingJwtToken        = errors.Unauthorized("UNAUTHORIZED", "JWT token is missing")
+	ErrMissingKeyFunc         = errors.Unauthorized("UNAUTHORIZED", "keyFunc is missing")
+	ErrTokenInvalid           = errors.Unauthorized("UNAUTHORIZED", "Token is invalid")
+	ErrTokenExpired           = errors.Unauthorized("UNAUTHORIZED", "JWT token has expired")
+	ErrTokenParseFail         = errors.Unauthorized("UNAUTHORIZED", "Fail to parse JWT token ")
+	ErrUnSupportSigningMethod = errors.Unauthorized("UNAUTHORIZED", "Wrong signing method")
+	ErrWrongContext           = errors.Unauthorized("UNAUTHORIZED", "Wrong context for middelware")
+	ErrNeedTokenProvider      = errors.Unauthorized("UNAUTHORIZED", "Token provider is missing")
+	ErrSignToken              = errors.Unauthorized("UNAUTHORIZED", "Can not sign token.Is the key correct?")
+	ErrGetKey                 = errors.Unauthorized("UNAUTHORIZED", "Can not get key while signing token")
+)
+
+// Option is jwt option.
+type Option func(*options)
+
+// Parser is a jwt parser
+type options struct {
+	signingMethod jwt.SigningMethod
+	claims        jwt.Claims
+}
+
+// WithSigningMethod with signing method option.
+func WithSigningMethod(method jwt.SigningMethod) Option {
+	return func(o *options) {
+		o.signingMethod = method
+	}
+}
+
+// WithClaims with customer claim
+func WithClaims(claims jwt.Claims) Option {
+	return func(o *options) {
+		o.claims = claims
+	}
+}
+
+// Server is a server auth middleware. Check the token and extract the info from token.
+func Server(keyFunc jwt.Keyfunc, opts ...Option) middleware.Middleware {
+	o := &options{
+		signingMethod: jwt.SigningMethodHS256,
+		claims:        jwt.StandardClaims{},
+	}
+	for _, opt := range opts {
+		opt(o)
+	}
+	return func(handler middleware.Handler) middleware.Handler {
+		return func(ctx context.Context, req interface{}) (interface{}, error) {
+			if header, ok := transport.FromServerContext(ctx); ok {
+				if keyFunc == nil {
+					return nil, ErrMissingKeyFunc
+				}
+				auths := strings.SplitN(header.RequestHeader().Get(authorizationKey), " ", 2)
+				if len(auths) != 2 || !strings.EqualFold(auths[0], bearerWord) {
+					return nil, ErrMissingJwtToken
+				}
+				jwtToken := auths[1]
+				tokenInfo, err := jwt.Parse(jwtToken, keyFunc)
+				if err != nil {
+					if ve, ok := err.(*jwt.ValidationError); ok {
+						if ve.Errors&jwt.ValidationErrorMalformed != 0 {
+							return nil, ErrTokenInvalid
+						} else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 {
+							return nil, ErrTokenExpired
+						} else {
+							return nil, ErrTokenParseFail
+						}
+					}
+				} else if !tokenInfo.Valid {
+					return nil, ErrTokenInvalid
+				} else if tokenInfo.Method != o.signingMethod {
+					return nil, ErrUnSupportSigningMethod
+				}
+				ctx = NewContext(ctx, tokenInfo.Claims)
+				return handler(ctx, req)
+			}
+			return nil, ErrWrongContext
+		}
+	}
+}
+
+// Client is a client jwt middleware.
+func Client(keyProvider jwt.Keyfunc, opts ...Option) middleware.Middleware {
+	o := &options{
+		signingMethod: jwt.SigningMethodHS256,
+		claims:        jwt.StandardClaims{},
+	}
+	for _, opt := range opts {
+		opt(o)
+	}
+	return func(handler middleware.Handler) middleware.Handler {
+		return func(ctx context.Context, req interface{}) (interface{}, error) {
+			if keyProvider == nil {
+				return nil, ErrNeedTokenProvider
+			}
+			token := jwt.NewWithClaims(o.signingMethod, o.claims)
+			key, err := keyProvider(token)
+			if err != nil {
+				return nil, ErrGetKey
+			}
+			tokenStr, err := token.SignedString(key)
+			if err != nil {
+				return nil, ErrSignToken
+			}
+			if clientContext, ok := transport.FromClientContext(ctx); ok {
+				clientContext.RequestHeader().Set(authorizationKey, fmt.Sprintf(bearerFormat, tokenStr))
+				return handler(ctx, req)
+			}
+			return nil, ErrWrongContext
+		}
+	}
+}
+
+// NewContext put auth info into context
+func NewContext(ctx context.Context, info jwt.Claims) context.Context {
+	return context.WithValue(ctx, authKey{}, info)
+}
+
+// FromContext extract auth info from context
+func FromContext(ctx context.Context) (token jwt.Claims, ok bool) {
+	token, ok = ctx.Value(authKey{}).(jwt.Claims)
+	return
+}
diff --git a/middleware/circuitbreaker/circuitbreaker.go b/middleware/circuitbreaker/circuitbreaker.go
index c80d45ba..5317e5d2 100644
--- a/middleware/circuitbreaker/circuitbreaker.go
+++ b/middleware/circuitbreaker/circuitbreaker.go
@@ -5,48 +5,58 @@ import (
 
 	"github.com/go-kratos/aegis/circuitbreaker"
 	"github.com/go-kratos/aegis/circuitbreaker/sre"
+	"github.com/go-kratos/kratos/v2/container/group"
 	"github.com/go-kratos/kratos/v2/errors"
 	"github.com/go-kratos/kratos/v2/middleware"
+	"github.com/go-kratos/kratos/v2/transport"
 )
 
+// ErrNotAllowed is request failed due to circuit breaker triggered.
+var ErrNotAllowed = errors.New(503, "CIRCUITBREAKER", "request failed due to circuit breaker triggered")
+
 // Option is circuit breaker option.
 type Option func(*options)
 
-// WithBreaker set circuit breaker implentation
-func WithBreaker(breaker circuitbreaker.CircuitBreaker) Option {
+// WithGroup with circuit breaker group.
+// NOTE: implements generics circuitbreaker.CircuitBreaker
+func WithGroup(g *group.Group) Option {
 	return func(o *options) {
-		o.breaker = breaker
+		o.group = g
 	}
 }
 
 type options struct {
-	breaker circuitbreaker.CircuitBreaker
+	group *group.Group
 }
 
 // Client circuitbreaker middleware will return errBreakerTriggered when the circuit
 // breaker is triggered and the request is rejected directly.
 func Client(opts ...Option) middleware.Middleware {
-	options := &options{
-		breaker: sre.NewBreaker(),
+	opt := &options{
+		group: group.NewGroup(func() interface{} {
+			return sre.NewBreaker()
+		}),
 	}
 	for _, o := range opts {
-		o(options)
+		o(opt)
 	}
 	return func(handler middleware.Handler) middleware.Handler {
 		return func(ctx context.Context, req interface{}) (interface{}, error) {
-			if err := options.breaker.Allow(); err != nil {
+			info, _ := transport.FromServerContext(ctx)
+			breaker := opt.group.Get(info.Operation()).(circuitbreaker.CircuitBreaker)
+			if err := breaker.Allow(); err != nil {
 				// rejected
 				// NOTE: when client reject requets locally,
 				// continue add counter let the drop ratio higher.
-				options.breaker.MarkFailed()
-				return nil, errors.New(503, "CIRCUITBREAKER", "request failed due to circuit breaker triggered")
+				breaker.MarkFailed()
+				return nil, ErrNotAllowed
 			}
 			// allowed
 			reply, err := handler(ctx, req)
-			if err != nil {
-				options.breaker.MarkFailed()
+			if err != nil && (errors.IsInternalServer(err) || errors.IsServiceUnavailable(err) || errors.IsGatewayTimeout(err)) {
+				breaker.MarkFailed()
 			} else {
-				options.breaker.MarkSuccess()
+				breaker.MarkSuccess()
 			}
 			return reply, err
 		}
diff --git a/middleware/ratelimit/limiter.go b/middleware/ratelimit/ratelimit.go
similarity index 83%
rename from middleware/ratelimit/limiter.go
rename to middleware/ratelimit/ratelimit.go
index bcfbb4fc..5bfae27b 100644
--- a/middleware/ratelimit/limiter.go
+++ b/middleware/ratelimit/ratelimit.go
@@ -9,6 +9,9 @@ import (
 	"github.com/go-kratos/kratos/v2/middleware"
 )
 
+// ErrLimitExceed is service unavailable due to rate limit exceeded.
+var ErrLimitExceed = errors.New(429, "RATELIMIT", "service unavailable due to rate limit exceeded")
+
 // Option is ratelimit option.
 type Option func(*options)
 
@@ -37,7 +40,7 @@ func Server(opts ...Option) middleware.Middleware {
 			done, e := options.limiter.Allow()
 			if e != nil {
 				// rejected
-				return nil, errors.New(429, "RATELIMIT", "service unavailable due to rate limit exceeded")
+				return nil, ErrLimitExceed
 			}
 			// allowed
 			reply, err = handler(ctx, req)
diff --git a/middleware/recovery/recovery.go b/middleware/recovery/recovery.go
index 8cb26e24..94f3eb54 100644
--- a/middleware/recovery/recovery.go
+++ b/middleware/recovery/recovery.go
@@ -2,7 +2,6 @@ package recovery
 
 import (
 	"context"
-	"fmt"
 	"runtime"
 
 	"github.com/go-kratos/kratos/v2/errors"
@@ -10,6 +9,9 @@ import (
 	"github.com/go-kratos/kratos/v2/middleware"
 )
 
+// ErrUnknownRequest is unknown request error.
+var ErrUnknownRequest = errors.InternalServer("UNKNOWN", "unknown request error")
+
 // HandlerFunc is recovery handler func.
 type HandlerFunc func(ctx context.Context, req, err interface{}) error
 
@@ -40,7 +42,7 @@ func Recovery(opts ...Option) middleware.Middleware {
 	op := options{
 		logger: log.DefaultLogger,
 		handler: func(ctx context.Context, req, err interface{}) error {
-			return errors.InternalServer("RECOVERY", fmt.Sprintf("panic triggered: %v", err))
+			return ErrUnknownRequest
 		},
 	}
 	for _, o := range opts {
diff --git a/selector/balancer.go b/selector/balancer.go
new file mode 100644
index 00000000..161072bd
--- /dev/null
+++ b/selector/balancer.go
@@ -0,0 +1,30 @@
+package selector
+
+import (
+	"context"
+	"time"
+)
+
+// Balancer is balancer interface
+type Balancer interface {
+	Pick(ctx context.Context, nodes []WeightedNode) (selected WeightedNode, done DoneFunc, err error)
+}
+
+// WeightedNode calculates scheduling weight in real time
+type WeightedNode interface {
+	Node
+
+	// Weight is the runtime calculated weight
+	Weight() float64
+
+	// Pick the node
+	Pick() DoneFunc
+
+	// PickElapsed is time elapsed since the latest pick
+	PickElapsed() time.Duration
+}
+
+// WeightedNodeBuilder is WeightedNode Builder
+type WeightedNodeBuilder interface {
+	Build(Node) WeightedNode
+}
diff --git a/selector/default.go b/selector/default.go
new file mode 100644
index 00000000..63a19bb4
--- /dev/null
+++ b/selector/default.go
@@ -0,0 +1,49 @@
+package selector
+
+import (
+	"context"
+	"sync"
+)
+
+// Default is composite selector.
+type Default struct {
+	NodeBuilder WeightedNodeBuilder
+	Balancer    Balancer
+
+	lk            sync.RWMutex
+	weightedNodes []Node
+}
+
+// Select select one node.
+func (d *Default) Select(ctx context.Context, opts ...SelectOption) (selected Node, done DoneFunc, err error) {
+	d.lk.RLock()
+	weightedNodes := d.weightedNodes
+	d.lk.RUnlock()
+	var options SelectOptions
+	for _, o := range opts {
+		o(&options)
+	}
+	for _, f := range options.Filters {
+		weightedNodes = f(ctx, weightedNodes)
+	}
+	candidates := make([]WeightedNode, 0, len(weightedNodes))
+	for _, n := range weightedNodes {
+		candidates = append(candidates, n.(WeightedNode))
+	}
+	if len(candidates) == 0 {
+		return nil, nil, ErrNoAvailable
+	}
+	return d.Balancer.Pick(ctx, candidates)
+}
+
+// Apply update nodes info.
+func (d *Default) Apply(nodes []Node) {
+	weightedNodes := make([]Node, 0, len(nodes))
+	for _, n := range nodes {
+		weightedNodes = append(weightedNodes, d.NodeBuilder.Build(n))
+	}
+	d.lk.Lock()
+	// TODO: Do not delete unchanged nodes
+	d.weightedNodes = weightedNodes
+	d.lk.Unlock()
+}
diff --git a/selector/filter/version.go b/selector/filter/version.go
new file mode 100644
index 00000000..66dc98f7
--- /dev/null
+++ b/selector/filter/version.go
@@ -0,0 +1,20 @@
+package filter
+
+import (
+	"context"
+
+	"github.com/go-kratos/kratos/v2/selector"
+)
+
+// Version is verion filter.
+func Version(version string) selector.Filter {
+	return func(_ context.Context, nodes []selector.Node) []selector.Node {
+		filters := make([]selector.Node, 0, len(nodes))
+		for _, n := range nodes {
+			if n.Version() == version {
+				filters = append(filters, n)
+			}
+		}
+		return filters
+	}
+}
diff --git a/selector/node/default.go b/selector/node/default.go
new file mode 100644
index 00000000..56871a97
--- /dev/null
+++ b/selector/node/default.go
@@ -0,0 +1,60 @@
+package node
+
+import (
+	"strconv"
+
+	"github.com/go-kratos/kratos/v2/registry"
+	"github.com/go-kratos/kratos/v2/selector"
+)
+
+// Node is slector node
+type Node struct {
+	addr     string
+	weight   *int64
+	version  string
+	name     string
+	metadata map[string]string
+}
+
+// Address is node address
+func (n *Node) Address() string {
+	return n.addr
+}
+
+// ServiceName is node serviceName
+func (n *Node) ServiceName() string {
+	return n.name
+}
+
+// InitialWeight is node initialWeight
+func (n *Node) InitialWeight() *int64 {
+	return n.weight
+}
+
+// Version is node version
+func (n *Node) Version() string {
+	return n.version
+}
+
+// Metadata is node metadata
+func (n *Node) Metadata() map[string]string {
+	return n.metadata
+}
+
+// New node
+func New(addr string, ins *registry.ServiceInstance) selector.Node {
+	n := &Node{
+		addr: addr,
+	}
+	if ins != nil {
+		n.name = ins.Name
+		n.version = ins.Version
+		n.metadata = ins.Metadata
+		if str, ok := ins.Metadata["weight"]; ok {
+			if weight, err := strconv.ParseInt(str, 10, 64); err == nil {
+				n.weight = &weight
+			}
+		}
+	}
+	return n
+}
diff --git a/selector/node/direct/direct.go b/selector/node/direct/direct.go
new file mode 100644
index 00000000..9e409849
--- /dev/null
+++ b/selector/node/direct/direct.go
@@ -0,0 +1,52 @@
+package direct
+
+import (
+	"context"
+	"sync/atomic"
+	"time"
+
+	"github.com/go-kratos/kratos/v2/selector"
+)
+
+const (
+	defaultWeight = 100
+)
+
+var (
+	_ selector.WeightedNode        = &node{}
+	_ selector.WeightedNodeBuilder = &Builder{}
+)
+
+// node is endpoint instance
+type node struct {
+	selector.Node
+
+	// last lastPick timestamp
+	lastPick int64
+}
+
+// Builder is direct node builder
+type Builder struct{}
+
+// Build create node
+func (*Builder) Build(n selector.Node) selector.WeightedNode {
+	return &node{Node: n, lastPick: 0}
+}
+
+func (n *node) Pick() selector.DoneFunc {
+	now := time.Now().UnixNano()
+	atomic.StoreInt64(&n.lastPick, now)
+	return func(ctx context.Context, di selector.DoneInfo) {}
+}
+
+// Weight is node effective weight
+func (n *node) Weight() float64 {
+	if n.InitialWeight() != nil {
+		return float64(*n.InitialWeight())
+	}
+	return defaultWeight
+}
+
+func (n *node) PickElapsed() time.Duration {
+	return time.Duration(time.Now().UnixNano() - atomic.LoadInt64(&n.lastPick))
+}
diff --git a/selector/node/ewma/node.go b/selector/node/ewma/node.go
new file mode 100644
index 00000000..06b37178
--- /dev/null
+++ b/selector/node/ewma/node.go
@@ -0,0 +1,180 @@
+package ewma
+
+import (
+	"container/list"
+	"context"
+	"math"
+	"sync"
+	"sync/atomic"
+	"time"
+
+	"github.com/go-kratos/kratos/v2/errors"
+	"github.com/go-kratos/kratos/v2/selector"
+)
+
+const (
+	// The mean lifetime of `cost`, it reaches its half-life after Tau*ln(2).
+	tau = int64(time.Millisecond * 600)
+	// if statistic not collected,we add a big lag penalty to endpoint
+	penalty = uint64(time.Second * 10)
+)
+
+var (
+	_ selector.WeightedNode        = &node{}
+	_ selector.WeightedNodeBuilder = &Builder{}
+)
+
+// node is endpoint instance
+type node struct {
+	selector.Node
+
+	// client statistic data
+	lag       int64
+	success   uint64
+	inflight  int64
+	inflights *list.List
+	// last collected timestamp
+	stamp     int64
+	predictTs int64
+	predict   int64
+	// request number in a period time
+	reqs int64
+	// last lastPick timestamp
+	lastPick int64
+
+	errHandler func(err error) (isErr bool)
+	lk         sync.RWMutex
+}
+
+// Builder is ewma node builder.
+type Builder struct {
+	ErrHandler func(err error) (isErr bool)
+}
+
+// Build create a weighted node.
+func (b *Builder) Build(n selector.Node) selector.WeightedNode {
+	s := &node{
+		Node:       n,
+		lag:        0,
+		success:    1000,
+		inflight:   1,
+		inflights:  list.New(),
+		errHandler: b.ErrHandler,
+	}
+	return s
+}
+
+func (n *node) health() uint64 {
+	return atomic.LoadUint64(&n.success)
+}
+
+func (n *node) load() (load uint64) {
+	now := time.Now().UnixNano()
+	avgLag := atomic.LoadInt64(&n.lag)
+	lastPredictTs := atomic.LoadInt64(&n.predictTs)
+	predicInterval := avgLag / 5
+	if predicInterval < int64(time.Millisecond*5) {
+		predicInterval = int64(time.Millisecond * 5)
+	} else if predicInterval > int64(time.Millisecond*200) {
+		predicInterval = int64(time.Millisecond * 200)
+	}
+	if now-lastPredictTs > predicInterval {
+		if atomic.CompareAndSwapInt64(&n.predictTs, lastPredictTs, now) {
+			var (
+				total   int64
+				count   int
+				predict int64
+			)
+			n.lk.RLock()
+			first := n.inflights.Front()
+			for first != nil {
+				lag := now - first.Value.(int64)
+				if lag > avgLag {
+					count++
+					total += lag
+				}
+				first = first.Next()
+			}
+			if count > (n.inflights.Len()/2 + 1) {
+				predict = total / int64(count)
+			}
+			n.lk.RUnlock()
+			atomic.StoreInt64(&n.predict, predict)
+		}
+	}
+
+	if avgLag == 0 {
+		// penalty是node刚启动时没有数据时的惩罚值，默认为1e9 * 10
+		load = penalty * uint64(atomic.LoadInt64(&n.inflight))
+	} else {
+		predict := atomic.LoadInt64(&n.predict)
+		if predict > avgLag {
+			avgLag = predict
+		}
+		load = uint64(avgLag) * uint64(atomic.LoadInt64(&n.inflight))
+	}
+	return
+}
+
+// Pick pick a node.
+func (n *node) Pick() selector.DoneFunc {
+	now := time.Now().UnixNano()
+	atomic.StoreInt64(&n.lastPick, now)
+	atomic.AddInt64(&n.inflight, 1)
+	atomic.AddInt64(&n.reqs, 1)
+	n.lk.Lock()
+	e := n.inflights.PushBack(now)
+	n.lk.Unlock()
+	return func(ctx context.Context, di selector.DoneInfo) {
+		n.lk.Lock()
+		n.inflights.Remove(e)
+		n.lk.Unlock()
+		atomic.AddInt64(&n.inflight, -1)
+
+		now := time.Now().UnixNano()
+		// get moving average ratio w
+		stamp := atomic.SwapInt64(&n.stamp, now)
+		td := now - stamp
+		if td < 0 {
+			td = 0
+		}
+		w := math.Exp(float64(-td) / float64(tau))
+
+		start := e.Value.(int64)
+		lag := now - start
+		if lag < 0 {
+			lag = 0
+		}
+		oldLag := atomic.LoadInt64(&n.lag)
+		if oldLag == 0 {
+			w = 0.0
+		}
+		lag = int64(float64(oldLag)*w + float64(lag)*(1.0-w))
+		atomic.StoreInt64(&n.lag, lag)
+
+		success := uint64(1000) // error value ,if error set 1
+		if di.Err != nil {
+			if n.errHandler != nil {
+				if n.errHandler(di.Err) {
+					success = 0
+				}
+			} else if errors.Is(context.DeadlineExceeded, di.Err) || errors.Is(context.Canceled, di.Err) ||
+				errors.IsServiceUnavailable(di.Err) || errors.IsGatewayTimeout(di.Err) {
+				success = 0
+			}
+		}
+		oldSuc := atomic.LoadUint64(&n.success)
+		success = uint64(float64(oldSuc)*w + float64(success)*(1.0-w))
+		atomic.StoreUint64(&n.success, success)
+	}
+}
+
+// Weight is node effective weight.
+func (n *node) Weight() (weight float64) {
+	weight = float64(n.health()*uint64(time.Second)) / float64(n.load())
+	return
+}
+
+func (n *node) PickElapsed() time.Duration {
+	return time.Duration(time.Now().UnixNano() - atomic.LoadInt64(&n.lastPick))
+}
diff --git a/selector/options.go b/selector/options.go
new file mode 100644
index 00000000..f4ce0f73
--- /dev/null
+++ b/selector/options.go
@@ -0,0 +1,21 @@
+package selector
+
+import "context"
+
+// SelectOptions is Select Options.
+type SelectOptions struct {
+	Filters []Filter
+}
+
+// SelectOption is Selector option.
+type SelectOption func(*SelectOptions)
+
+// Filter is node filter function.
+type Filter func(context.Context, []Node) []Node
+
+// WithFilter with filter options
+func WithFilter(fn ...Filter) SelectOption {
+	return func(opts *SelectOptions) {
+		opts.Filters = fn
+	}
+}
diff --git a/selector/p2c/p2c.go b/selector/p2c/p2c.go
new file mode 100644
index 00000000..88cd3a7b
--- /dev/null
+++ b/selector/p2c/p2c.go
@@ -0,0 +1,74 @@
+package p2c
+
+import (
+	"context"
+	"math/rand"
+	"sync/atomic"
+	"time"
+
+	"github.com/go-kratos/kratos/v2/selector"
+	"github.com/go-kratos/kratos/v2/selector/node/ewma"
+)
+
+const (
+	forcePick = time.Second * 3
+	// Name is balancer name
+	Name = "p2c"
+)
+
+var _ selector.Balancer = &Balancer{}
+
+// New creates a p2c selector.
+func New() selector.Selector {
+	return &selector.Default{
+		NodeBuilder: &ewma.Builder{},
+		Balancer: &Balancer{
+			r: rand.New(rand.NewSource(time.Now().UnixNano())),
+		},
+	}
+}
+
+// Balancer is p2c selector.
+type Balancer struct {
+	r  *rand.Rand
+	lk int64
+}
+
+// choose two distinct nodes.
+func (s *Balancer) prePick(nodes []selector.WeightedNode) (nodeA selector.WeightedNode, nodeB selector.WeightedNode) {
+	a := s.r.Intn(len(nodes))
+	b := s.r.Intn(len(nodes) - 1)
+	if b >= a {
+		b = b + 1
+	}
+	nodeA, nodeB = nodes[a], nodes[b]
+	return
+}
+
+// Pick pick a node.
+func (s *Balancer) Pick(ctx context.Context, nodes []selector.WeightedNode) (selector.WeightedNode, selector.DoneFunc, error) {
+	if len(nodes) == 0 {
+		return nil, nil, selector.ErrNoAvailable
+	} else if len(nodes) == 1 {
+		done := nodes[0].Pick()
+		return nodes[0], done, nil
+	}
+
+	var pc, upc selector.WeightedNode
+	nodeA, nodeB := s.prePick(nodes)
+	// meta.Weight为服务发布者在discovery中设置的权重
+	if nodeB.Weight() > nodeA.Weight() {
+		pc, upc = nodeB, nodeA
+	} else {
+		pc, upc = nodeA, nodeB
+	}
+
+	// 如果落选节点在forceGap期间内从来没有被选中一次，则强制选一次
+	// 利用强制的机会，来触发成功率、延迟的更新
+	if upc.PickElapsed() > forcePick && atomic.CompareAndSwapInt64(&s.lk, 0, 1) {
+		pc = upc
+		atomic.StoreInt64(&s.lk, 0)
+	}
+	done := pc.Pick()
+	return pc, done, nil
+}
diff --git a/selector/random/random.go b/selector/random/random.go
new file mode 100644
index 00000000..30e33d4d
--- /dev/null
+++ b/selector/random/random.go
@@ -0,0 +1,38 @@
+package random
+
+import (
+	"context"
+	"math/rand"
+
+	"github.com/go-kratos/kratos/v2/selector"
+	"github.com/go-kratos/kratos/v2/selector/node/direct"
+)
+
+var (
+	_ selector.Balancer = &Balancer{}
+
+	// Name is balancer name
+	Name = "random"
+)
+
+// Balancer is a random balancer.
+type Balancer struct{}
+
+// New random a selector.
+func New() selector.Selector {
+	return &selector.Default{
+		Balancer:    &Balancer{},
+		NodeBuilder: &direct.Builder{},
+	}
+}
+
+// Pick pick a weighted node.
+func (p *Balancer) Pick(_ context.Context, nodes []selector.WeightedNode) (selector.WeightedNode, selector.DoneFunc, error) {
+	if len(nodes) == 0 {
+		return nil, nil, selector.ErrNoAvailable
+	}
+	cur := rand.Intn(len(nodes))
+	selected := nodes[cur]
+	d := selected.Pick()
+	return selected, d, nil
+}
diff --git a/selector/selector.go b/selector/selector.go
new file mode 100644
index 00000000..21572ac7
--- /dev/null
+++ b/selector/selector.go
@@ -0,0 +1,66 @@
+package selector
+
+import (
+	"context"
+
+	"github.com/go-kratos/kratos/v2/errors"
+)
+
+// ErrNoAvailable is no available node.
+var ErrNoAvailable = errors.ServiceUnavailable("no_available_node", "")
+
+// Selector is node pick balancer.
+type Selector interface {
+	Rebalancer
+
+	// Select nodes
+	// if err == nil, selected and done must not be empty.
+	Select(ctx context.Context, opts ...SelectOption) (selected Node, done DoneFunc, err error)
+}
+
+// Rebalancer is nodes rebalancer.
+type Rebalancer interface {
+	// apply all nodes when any changes happen
+	Apply(nodes []Node)
+}
+
+// Node is node interface.
+type Node interface {
+	// Address is the unique address under the same service
+	Address() string
+
+	// ServiceName is service name
+	ServiceName() string
+
+	// InitialWeight is the initial value of scheduling weight
+	// if not set return nil
+	InitialWeight() *int64
+
+	// Version is service node version
+	Version() string
+
+	// Metadata is the kv pair metadata associated with the service instance.
+	// version,namespace,region,protocol etc..
+	Metadata() map[string]string
+}
+
+// DoneInfo is callback info when RPC invoke done.
+type DoneInfo struct {
+	// Response Error
+	Err error
+	// Response Metadata
+	ReplyMeta ReplyMeta
+
+	// BytesSent indicates if any bytes have been sent to the server.
+	BytesSent bool
+	// BytesReceived indicates if any byte has been received from the server.
+	BytesReceived bool
+}
+
+// ReplyMeta is Reply Metadata.
+type ReplyMeta interface {
+	Get(key string) string
+}
+
+// DoneFunc is callback function when RPC invoke done.
+type DoneFunc func(ctx context.Context, di DoneInfo)
diff --git a/transport/grpc/balancer/balancer.go b/transport/grpc/balancer/balancer.go
new file mode 100644
index 00000000..573f9b7a
--- /dev/null
+++ b/transport/grpc/balancer/balancer.go
@@ -0,0 +1,106 @@
+package balancer
+
+import (
+	"sync"
+
+	"github.com/go-kratos/kratos/v2/registry"
+	"github.com/go-kratos/kratos/v2/selector"
+	"github.com/go-kratos/kratos/v2/selector/node"
+	"github.com/go-kratos/kratos/v2/selector/p2c"
+	"github.com/go-kratos/kratos/v2/selector/random"
+
+	gBalancer "google.golang.org/grpc/balancer"
+	"google.golang.org/grpc/balancer/base"
+	"google.golang.org/grpc/metadata"
+)
+
+var (
+	_ base.PickerBuilder = &Builder{}
+	_ gBalancer.Picker   = &Picker{}
+
+	mu sync.Mutex
+)
+
+func init() {
+	// inject global grpc balancer
+	SetGlobalBalancer(random.Name, random.New())
+	SetGlobalBalancer(p2c.Name, p2c.New())
+}
+
+// SetGlobalBalancer set grpc balancer with scheme.
+func SetGlobalBalancer(scheme string, selector selector.Selector) {
+	mu.Lock()
+	defer mu.Unlock()
+
+	b := base.NewBalancerBuilder(
+		scheme,
+		&Builder{selector},
+		base.Config{HealthCheck: true},
+	)
+	gBalancer.Register(b)
+}
+
+// Builder is grpc balancer builder.
+type Builder struct {
+	selector selector.Selector
+}
+
+// Build creates a grpc Picker.
+func (b *Builder) Build(info base.PickerBuildInfo) gBalancer.Picker {
+	nodes := make([]selector.Node, 0)
+	subConns := make(map[string]gBalancer.SubConn)
+	for conn, info := range info.ReadySCs {
+		if _, ok := subConns[info.Address.Addr]; ok {
+			continue
+		}
+		subConns[info.Address.Addr] = conn
+
+		ins, _ := info.Address.Attributes.Value("rawServiceInstance").(*registry.ServiceInstance)
+		nodes = append(nodes, node.New(info.Address.Addr, ins))
+	}
+	p := &Picker{
+		selector: b.selector,
+		subConns: subConns,
+	}
+	p.selector.Apply(nodes)
+	return p
+}
+
+// Picker is a grpc picker.
+type Picker struct {
+	subConns map[string]gBalancer.SubConn
+	selector selector.Selector
+}
+
+// Pick pick instances.
+func (p *Picker) Pick(info gBalancer.PickInfo) (gBalancer.PickResult, error) {
+	n, done, err := p.selector.Select(info.Ctx)
+	if err != nil {
+		return gBalancer.PickResult{}, err
+	}
+	sub := p.subConns[n.Address()]
+
+	return gBalancer.PickResult{
+		SubConn: sub,
+		Done: func(di gBalancer.DoneInfo) {
+			done(info.Ctx, selector.DoneInfo{
+				Err:           di.Err,
+				BytesSent:     di.BytesSent,
+				BytesReceived: di.BytesReceived,
+				ReplyMeta:     Trailer(di.Trailer),
+			})
+		},
+	}, nil
+}
+
+// Trailer is a grpc trailder MD.
+type Trailer metadata.MD
+
+// Get get a grpc trailer value.
+func (t Trailer) Get(k string) string {
+	v := metadata.MD(t).Get(k)
+	if len(v) > 0 {
+		return v[0]
+	}
+	return ""
+}
diff --git a/transport/grpc/client.go b/transport/grpc/client.go
index 9982bcf9..fa33c707 100644
--- a/transport/grpc/client.go
+++ b/transport/grpc/client.go
@@ -8,14 +8,16 @@ import (
 
 	"github.com/go-kratos/kratos/v2/middleware"
 	"github.com/go-kratos/kratos/v2/registry"
+	"github.com/go-kratos/kratos/v2/selector/random"
 	"github.com/go-kratos/kratos/v2/transport"
 	"github.com/go-kratos/kratos/v2/transport/grpc/resolver/discovery"
 
 	// init resolver
 	_ "github.com/go-kratos/kratos/v2/transport/grpc/resolver/direct"
+	// init balancer
+	_ "github.com/go-kratos/kratos/v2/transport/grpc/balancer"
 
 	"google.golang.org/grpc"
-	"google.golang.org/grpc/balancer/roundrobin"
 	"google.golang.org/grpc/credentials"
 	grpcmd "google.golang.org/grpc/metadata"
 )
@@ -107,7 +109,7 @@ func dial(ctx context.Context, insecure bool, opts ...ClientOption) (*grpc.Clien
 		ints = append(ints, options.ints...)
 	}
 	grpcOpts := []grpc.DialOption{
-		grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, roundrobin.Name)),
+		grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, random.Name)),
 		grpc.WithChainUnaryInterceptor(ints...),
 	}
 	if options.discovery != nil {
diff --git a/transport/grpc/resolver/discovery/resolver.go b/transport/grpc/resolver/discovery/resolver.go
index a02f2cc5..908af867 100644
--- a/transport/grpc/resolver/discovery/resolver.go
+++ b/transport/grpc/resolver/discovery/resolver.go
@@ -46,6 +46,7 @@ func (r *discoveryResolver) watch() {
 
 func (r *discoveryResolver) update(ins []*registry.ServiceInstance) {
 	addrs := make([]resolver.Address, 0)
+	endpoints := make(map[string]struct{})
 	for _, in := range ins {
 		endpoint, err := endpoint.ParseEndpoint(in.Endpoints, "grpc", !r.insecure)
 		if err != nil {
@@ -55,11 +56,17 @@ func (r *discoveryResolver) update(ins []*registry.ServiceInstance) {
 		if endpoint == "" {
 			continue
 		}
+		// filter redundant endpoints
+		if _, ok := endpoints[endpoint]; ok {
+			continue
+		}
+		endpoints[endpoint] = struct{}{}
 		addr := resolver.Address{
 			ServerName: in.Name,
 			Attributes: parseAttributes(in.Metadata),
 			Addr:       endpoint,
 		}
+		addr.Attributes = addr.Attributes.WithValues("rawServiceInstance", in)
 		addrs = append(addrs, addr)
 	}
 	if len(addrs) == 0 {
diff --git a/transport/grpc/server.go b/transport/grpc/server.go
index 4b1fe9b1..748302c2 100644
--- a/transport/grpc/server.go
+++ b/transport/grpc/server.go
@@ -92,7 +92,7 @@ func Options(opts ...grpc.ServerOption) ServerOption {
 // Server is a gRPC server wrapper.
 type Server struct {
 	*grpc.Server
-	ctx        context.Context
+	baseCtx    context.Context
 	tlsConf    *tls.Config
 	lis        net.Listener
 	once       sync.Once
@@ -112,6 +112,7 @@ type Server struct {
 // NewServer creates a gRPC server by options.
 func NewServer(opts ...ServerOption) *Server {
 	srv := &Server{
+		baseCtx: context.Background(),
 		network: "tcp",
 		address: ":0",
 		timeout: 1 * time.Second,
@@ -175,7 +176,7 @@ func (s *Server) Start(ctx context.Context) error {
 	if _, err := s.Endpoint(); err != nil {
 		return err
 	}
-	s.ctx = ctx
+	s.baseCtx = ctx
 	s.log.Infof("[gRPC] server listening on: %s", s.lis.Addr().String())
 	s.health.Resume()
 	return s.Serve(s.lis)
@@ -191,7 +192,7 @@ func (s *Server) Stop(ctx context.Context) error {
 
 func (s *Server) unaryServerInterceptor() grpc.UnaryServerInterceptor {
 	return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
-		ctx, cancel := ic.Merge(ctx, s.ctx)
+		ctx, cancel := ic.Merge(ctx, s.baseCtx)
 		defer cancel()
 		md, _ := grpcmd.FromIncomingContext(ctx)
 		replyHeader := grpcmd.MD{}
diff --git a/transport/http/balancer/balancer.go b/transport/http/balancer/balancer.go
deleted file mode 100644
index 200eb78a..00000000
--- a/transport/http/balancer/balancer.go
+++ /dev/null
@@ -1,21 +0,0 @@
-package balancer
-
-import (
-	"context"
-
-	"github.com/go-kratos/kratos/v2/registry"
-)
-
-// DoneInfo is callback when rpc done
-type DoneInfo struct {
-	Err     error
-	Trailer map[string]string
-}
-
-// Balancer is node pick balancer
-type Balancer interface {
-	// Pick one node
-	Pick(ctx context.Context) (node *registry.ServiceInstance, done func(context.Context, DoneInfo), err error)
-	// Update nodes when nodes removed or added
-	Update(nodes []*registry.ServiceInstance)
-}
diff --git a/transport/http/balancer/random/random.go b/transport/http/balancer/random/random.go
deleted file mode 100644
index e4cf7438..00000000
--- a/transport/http/balancer/random/random.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package random
-
-import (
-	"context"
-	"fmt"
-	"math/rand"
-	"sync"
-
-	"github.com/go-kratos/kratos/v2/registry"
-	"github.com/go-kratos/kratos/v2/transport/http/balancer"
-)
-
-var _ balancer.Balancer = &Balancer{}
-
-type Balancer struct {
-	lock  sync.RWMutex
-	nodes []*registry.ServiceInstance
-}
-
-func New() *Balancer {
-	return &Balancer{}
-}
-
-func (b *Balancer) Pick(ctx context.Context) (node *registry.ServiceInstance, done func(context.Context, balancer.DoneInfo), err error) {
-	b.lock.RLock()
-	nodes := b.nodes
-	b.lock.RUnlock()
-
-	if len(nodes) == 0 {
-		return nil, nil, fmt.Errorf("no instances available")
-	}
-	if len(nodes) == 1 {
-		return nodes[0], func(context.Context, balancer.DoneInfo) {}, nil
-	}
-	idx := rand.Intn(len(nodes))
-	return nodes[idx], func(context.Context, balancer.DoneInfo) {}, nil
-}
-
-func (b *Balancer) Update(nodes []*registry.ServiceInstance) {
-	b.lock.Lock()
-	defer b.lock.Unlock()
-	b.nodes = nodes
-}
diff --git a/transport/http/calloption.go b/transport/http/calloption.go
index 0eb4a02e..f6851895 100644
--- a/transport/http/calloption.go
+++ b/transport/http/calloption.go
@@ -1,6 +1,10 @@
 package http
 
-import "net/http"
+import (
+	"net/http"
+
+	"github.com/go-kratos/kratos/v2/selector"
+)
 
 // CallOption configures a Call before it starts or extracts information from
 // a Call after it completes.
@@ -18,6 +22,7 @@ type callInfo struct {
 	contentType  string
 	operation    string
 	pathTemplate string
+	filters      []selector.Filter
 }
 
 // EmptyCallOption does not alter the Call configuration.
@@ -88,6 +93,22 @@ func (o PathTemplateCallOption) before(c *callInfo) error {
 	return nil
 }
 
+// SelectFilter is http select filter
+func SelectFilter(filters ...selector.Filter) CallOption {
+	return SelectFilterCallOption{filters: filters}
+}
+
+// SelectFilterCallOption is set call select filters
+type SelectFilterCallOption struct {
+	EmptyCallOption
+	filters []selector.Filter
+}
+
+func (o SelectFilterCallOption) before(c *callInfo) error {
+	c.filters = o.filters
+	return nil
+}
+
 // Header returns a CallOptions that retrieves the http response header
 // from server reply.
 func Header(header *http.Header) CallOption {
diff --git a/transport/http/client.go b/transport/http/client.go
index 36f576a5..14dc9a3b 100644
--- a/transport/http/client.go
+++ b/transport/http/client.go
@@ -12,14 +12,13 @@ import (
 
 	"github.com/go-kratos/kratos/v2/encoding"
 	"github.com/go-kratos/kratos/v2/errors"
-	"github.com/go-kratos/kratos/v2/internal/endpoint"
 	"github.com/go-kratos/kratos/v2/internal/host"
 	"github.com/go-kratos/kratos/v2/internal/httputil"
 	"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/random"
 	"github.com/go-kratos/kratos/v2/transport"
-	"github.com/go-kratos/kratos/v2/transport/http/balancer"
-	"github.com/go-kratos/kratos/v2/transport/http/balancer/random"
 )
 
 // DecodeErrorFunc is decode error func.
@@ -45,7 +44,7 @@ type clientOptions struct {
 	decoder      DecodeResponseFunc
 	errorDecoder DecodeErrorFunc
 	transport    http.RoundTripper
-	balancer     balancer.Balancer
+	selector     selector.Selector
 	discovery    registry.Discovery
 	middleware   []middleware.Middleware
 	block        bool
@@ -114,12 +113,10 @@ func WithDiscovery(d registry.Discovery) ClientOption {
 	}
 }
 
-// WithBalancer with client balancer.
-// Experimental
-// Notice: This type is EXPERIMENTAL and may be changed or removed in a later release.
-func WithBalancer(b balancer.Balancer) ClientOption {
+// WithSelector with client selector.
+func WithSelector(selector selector.Selector) ClientOption {
 	return func(o *clientOptions) {
-		o.balancer = b
+		o.selector = selector
 	}
 }
 
@@ -155,7 +152,7 @@ func NewClient(ctx context.Context, opts ...ClientOption) (*Client, error) {
 		decoder:      DefaultResponseDecoder,
 		errorDecoder: DefaultErrorDecoder,
 		transport:    http.DefaultTransport,
-		balancer:     random.New(),
+		selector:     random.New(),
 	}
 	for _, o := range opts {
 		o(&options)
@@ -173,7 +170,7 @@ func NewClient(ctx context.Context, opts ...ClientOption) (*Client, error) {
 	var r *resolver
 	if options.discovery != nil {
 		if target.Scheme == "discovery" {
-			if r, err = newResolver(ctx, options.discovery, target, options.balancer, options.block, insecure); err != nil {
+			if r, err = newResolver(ctx, options.discovery, target, options.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 {
@@ -235,17 +232,13 @@ func (client *Client) Invoke(ctx context.Context, method, path string, args inte
 
 func (client *Client) invoke(ctx context.Context, req *http.Request, args interface{}, reply interface{}, c callInfo, opts ...CallOption) error {
 	h := func(ctx context.Context, in interface{}) (interface{}, error) {
-		var done func(context.Context, balancer.DoneInfo)
+		var done func(context.Context, selector.DoneInfo)
 		if client.r != nil {
 			var (
 				err  error
-				node *registry.ServiceInstance
+				node selector.Node
 			)
-			if node, done, err = client.opts.balancer.Pick(ctx); err != nil {
-				return nil, errors.ServiceUnavailable("NODE_NOT_FOUND", err.Error())
-			}
-			endpoint, err := endpoint.ParseEndpoint(node.Endpoints, "http", !client.insecure)
-			if err != nil {
+			if node, done, err = client.opts.selector.Select(ctx, selector.WithFilter(c.filters...)); err != nil {
 				return nil, errors.ServiceUnavailable("NODE_NOT_FOUND", err.Error())
 			}
 			if client.insecure {
@@ -253,12 +246,12 @@ func (client *Client) invoke(ctx context.Context, req *http.Request, args interf
 			} else {
 				req.URL.Scheme = "https"
 			}
-			req.URL.Host = endpoint
-			req.Host = endpoint
+			req.URL.Host = node.Address()
+			req.Host = node.Address()
 		}
 		res, err := client.do(ctx, req, c)
 		if done != nil {
-			done(ctx, balancer.DoneInfo{Err: err})
+			done(ctx, selector.DoneInfo{Err: err})
 		}
 		if res != nil {
 			cs := csAttempt{res: res}
diff --git a/transport/http/resolver.go b/transport/http/resolver.go
index cb882b16..b745bb3a 100644
--- a/transport/http/resolver.go
+++ b/transport/http/resolver.go
@@ -5,19 +5,15 @@ import (
 	"errors"
 	"net/url"
 	"strings"
-	"sync"
 	"time"
 
 	"github.com/go-kratos/kratos/v2/internal/endpoint"
 	"github.com/go-kratos/kratos/v2/log"
 	"github.com/go-kratos/kratos/v2/registry"
+	"github.com/go-kratos/kratos/v2/selector"
+	"github.com/go-kratos/kratos/v2/selector/node"
 )
 
-// Updater is resolver nodes updater
-type Updater interface {
-	Update(nodes []*registry.ServiceInstance)
-}
-
 // Target is resolver target
 type Target struct {
 	Scheme    string
@@ -45,9 +41,7 @@ func parseTarget(endpoint string, insecure bool) (*Target, error) {
 }
 
 type resolver struct {
-	lock    sync.RWMutex
-	nodes   []*registry.ServiceInstance
-	updater Updater
+	rebalancer selector.Rebalancer
 
 	target  *Target
 	watcher registry.Watcher
@@ -56,17 +50,17 @@ type resolver struct {
 	insecure bool
 }
 
-func newResolver(ctx context.Context, discovery registry.Discovery, target *Target, updater Updater, block, insecure bool) (*resolver, error) {
+func newResolver(ctx context.Context, discovery registry.Discovery, target *Target, rebalancer selector.Rebalancer, block, insecure bool) (*resolver, error) {
 	watcher, err := discovery.Watch(ctx, target.Endpoint)
 	if err != nil {
 		return nil, err
 	}
 	r := &resolver{
-		target:   target,
-		watcher:  watcher,
-		logger:   log.NewHelper(log.DefaultLogger),
-		updater:  updater,
-		insecure: insecure,
+		target:     target,
+		watcher:    watcher,
+		logger:     log.NewHelper(log.DefaultLogger),
+		rebalancer: rebalancer,
+		insecure:   insecure,
 	}
 	if block {
 		done := make(chan error, 1)
@@ -77,8 +71,7 @@ func newResolver(ctx context.Context, discovery registry.Discovery, target *Targ
 					done <- err
 					return
 				}
-				r.update(services)
-				if len(r.nodes) > 0 {
+				if r.update(services) {
 					done <- nil
 					return
 				}
@@ -119,27 +112,25 @@ func newResolver(ctx context.Context, discovery registry.Discovery, target *Targ
 	return r, nil
 }
 
-func (r *resolver) update(services []*registry.ServiceInstance) {
-	nodes := make([]*registry.ServiceInstance, 0)
-	for _, in := range services {
-		ept, err := endpoint.ParseEndpoint(in.Endpoints, "http", !r.insecure)
+func (r *resolver) update(services []*registry.ServiceInstance) bool {
+	nodes := make([]selector.Node, 0)
+	for _, ins := range services {
+		ept, err := endpoint.ParseEndpoint(ins.Endpoints, "http", !r.insecure)
 		if err != nil {
-			r.logger.Errorf("Failed to parse (%v) discovery endpoint: %v error %v", r.target, in.Endpoints, err)
+			r.logger.Errorf("Failed to parse (%v) discovery endpoint: %v error %v", r.target, ins.Endpoints, err)
 			continue
 		}
 		if ept == "" {
 			continue
 		}
-		nodes = append(nodes, in)
+		nodes = append(nodes, node.New(ept, ins))
 	}
-	if len(nodes) != 0 {
-		r.updater.Update(nodes)
-		r.lock.Lock()
-		r.nodes = nodes
-		r.lock.Unlock()
-	} else {
+	if len(nodes) == 0 {
 		r.logger.Warnf("[http resovler]Zero endpoint found,refused to write,ser: %s ins: %v", r.target.Endpoint, nodes)
+		return false
 	}
+	r.rebalancer.Apply(nodes)
+	return true
 }
 
 func (r *resolver) Close() error {
diff --git a/transport/http/server.go b/transport/http/server.go
index fd78ab3b..15dc0b57 100644
--- a/transport/http/server.go
+++ b/transport/http/server.go
@@ -105,46 +105,57 @@ func TLSConfig(c *tls.Config) ServerOption {
 	}
 }
 
+// StrictSlash is with mux's StrictSlash
+// If true, when the path pattern is "/path/", accessing "/path" will
+// redirect to the former and vice versa.
+func StrictSlash(strictSlash bool) ServerOption {
+	return func(o *Server) {
+		o.strictSlash = strictSlash
+	}
+}
+
 // Server is an HTTP server wrapper.
 type Server struct {
 	*http.Server
-	lis      net.Listener
-	tlsConf  *tls.Config
-	once     sync.Once
-	endpoint *url.URL
-	err      error
-	network  string
-	address  string
-	timeout  time.Duration
-	filters  []FilterFunc
-	ms       []middleware.Middleware
-	dec      DecodeRequestFunc
-	enc      EncodeResponseFunc
-	ene      EncodeErrorFunc
-	router   *mux.Router
-	log      *log.Helper
+	lis         net.Listener
+	tlsConf     *tls.Config
+	once        sync.Once
+	endpoint    *url.URL
+	err         error
+	network     string
+	address     string
+	timeout     time.Duration
+	filters     []FilterFunc
+	ms          []middleware.Middleware
+	dec         DecodeRequestFunc
+	enc         EncodeResponseFunc
+	ene         EncodeErrorFunc
+	strictSlash bool
+	router      *mux.Router
+	log         *log.Helper
 }
 
 // NewServer creates an HTTP server by options.
 func NewServer(opts ...ServerOption) *Server {
 	srv := &Server{
-		network: "tcp",
-		address: ":0",
-		timeout: 1 * time.Second,
-		dec:     DefaultRequestDecoder,
-		enc:     DefaultResponseEncoder,
-		ene:     DefaultErrorEncoder,
-		log:     log.NewHelper(log.DefaultLogger),
+		network:     "tcp",
+		address:     ":0",
+		timeout:     1 * time.Second,
+		dec:         DefaultRequestDecoder,
+		enc:         DefaultResponseEncoder,
+		ene:         DefaultErrorEncoder,
+		strictSlash: true,
+		log:         log.NewHelper(log.DefaultLogger),
 	}
 	for _, o := range opts {
 		o(srv)
 	}
+	srv.router = mux.NewRouter().StrictSlash(srv.strictSlash)
+	srv.router.Use(srv.filter())
 	srv.Server = &http.Server{
-		Handler:   FilterChain(srv.filters...)(srv),
+		Handler:   FilterChain(srv.filters...)(srv.router),
 		TLSConfig: srv.tlsConf,
 	}
-	srv.router = mux.NewRouter()
-	srv.router.Use(srv.filter())
 	return srv
 }
 
@@ -168,20 +179,30 @@ func (s *Server) HandleFunc(path string, h http.HandlerFunc) {
 	s.router.HandleFunc(path, h)
 }
 
+// HandleHeader registers a new route with a matcher for the header.
+func (s *Server) HandleHeader(key, val string, h http.HandlerFunc) {
+	s.router.Headers(key, val).Handler(h)
+}
+
 // ServeHTTP should write reply headers and data to the ResponseWriter and then return.
 func (s *Server) ServeHTTP(res http.ResponseWriter, req *http.Request) {
-	s.router.ServeHTTP(res, req)
+	s.Handler.ServeHTTP(res, req)
 }
 
 func (s *Server) filter() mux.MiddlewareFunc {
 	return func(next http.Handler) http.Handler {
 		return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
-			ctx, cancel := context.WithCancel(req.Context())
-			defer cancel()
+			var (
+				ctx    context.Context
+				cancel context.CancelFunc
+			)
 			if s.timeout > 0 {
-				ctx, cancel = context.WithTimeout(ctx, s.timeout)
-				defer cancel()
+				ctx, cancel = context.WithTimeout(req.Context(), s.timeout)
+			} else {
+				ctx, cancel = context.WithCancel(req.Context())
 			}
+			defer cancel()
+
 			pathTemplate := req.URL.Path
 			if route := mux.CurrentRoute(req); route != nil {
 				// /path/123 -> /path/{id}


Test patch:
diff --git a/container/group/example_test.go b/container/group/example_test.go
new file mode 100644
index 00000000..dc90f229
--- /dev/null
+++ b/container/group/example_test.go
@@ -0,0 +1,42 @@
+package group
+
+import "fmt"
+
+type Counter struct {
+	Value int
+}
+
+func (c *Counter) Incr() {
+	c.Value++
+}
+
+func ExampleGroup_Get() {
+	group := NewGroup(func() interface{} {
+		fmt.Println("Only Once")
+		return &Counter{}
+	})
+
+	// Create a new Counter
+	group.Get("pass").(*Counter).Incr()
+
+	// Get the created Counter again.
+	group.Get("pass").(*Counter).Incr()
+	// Output:
+	// Only Once
+}
+
+func ExampleGroup_Reset() {
+	group := NewGroup(func() interface{} {
+		return &Counter{}
+	})
+
+	// Reset the new function and clear all created objects.
+	group.Reset(func() interface{} {
+		fmt.Println("reset")
+		return &Counter{}
+	})
+
+	// Create a new Counter
+	group.Get("pass").(*Counter).Incr()
+	// Output:reset
+}
diff --git a/container/group/group_test.go b/container/group/group_test.go
new file mode 100644
index 00000000..46a7d4ac
--- /dev/null
+++ b/container/group/group_test.go
@@ -0,0 +1,65 @@
+package group
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestGroupGet(t *testing.T) {
+	count := 0
+	g := NewGroup(func() interface{} {
+		count++
+		return count
+	})
+	v := g.Get("key_0")
+	assert.Equal(t, 1, v.(int))
+
+	v = g.Get("key_1")
+	assert.Equal(t, 2, v.(int))
+
+	v = g.Get("key_0")
+	assert.Equal(t, 1, v.(int))
+	assert.Equal(t, 2, count)
+}
+
+func TestGroupReset(t *testing.T) {
+	g := NewGroup(func() interface{} {
+		return 1
+	})
+	g.Get("key")
+	call := false
+	g.Reset(func() interface{} {
+		call = true
+		return 1
+	})
+
+	length := 0
+	for range g.vals {
+		length++
+	}
+
+	assert.Equal(t, 0, length)
+
+	g.Get("key")
+	assert.Equal(t, true, call)
+}
+
+func TestGroupClear(t *testing.T) {
+	g := NewGroup(func() interface{} {
+		return 1
+	})
+	g.Get("key")
+	length := 0
+	for range g.vals {
+		length++
+	}
+	assert.Equal(t, 1, length)
+
+	g.Clear()
+	length = 0
+	for range g.vals {
+		length++
+	}
+	assert.Equal(t, 0, length)
+}
diff --git a/contrib/config/etcd/config_test.go b/contrib/config/etcd/config_test.go
new file mode 100644
index 00000000..c83eb690
--- /dev/null
+++ b/contrib/config/etcd/config_test.go
@@ -0,0 +1,66 @@
+package etcd
+
+import (
+	"context"
+	"testing"
+	"time"
+
+	clientv3 "go.etcd.io/etcd/client/v3"
+	"google.golang.org/grpc"
+)
+
+const testKey = "/kratos/test/config"
+
+func TestConfig(t *testing.T) {
+	client, err := clientv3.New(clientv3.Config{
+		Endpoints:   []string{"127.0.0.1:2379"},
+		DialTimeout: time.Second, DialOptions: []grpc.DialOption{grpc.WithBlock()},
+	})
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer func() {
+		_ = client.Close()
+	}()
+	if _, err = client.Put(context.Background(), testKey, "test config"); err != nil {
+		t.Fatal(err)
+	}
+
+	source, err := New(client, WithPath(testKey))
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	kvs, err := source.Load()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if len(kvs) != 1 || kvs[0].Key != testKey || string(kvs[0].Value) != "test config" {
+		t.Fatal("config error")
+	}
+
+	w, err := source.Watch()
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer func() {
+		_ = w.Stop()
+	}()
+
+	if _, err = client.Put(context.Background(), testKey, "new config"); err != nil {
+		t.Error(err)
+	}
+
+	if kvs, err = w.Next(); err != nil {
+		t.Fatal(err)
+	}
+
+	if len(kvs) != 1 || kvs[0].Key != testKey || string(kvs[0].Value) != "new config" {
+		t.Fatal("config error")
+	}
+
+	if _, err := client.Delete(context.Background(), testKey); err != nil {
+		t.Error(err)
+	}
+}
diff --git a/contrib/log/zap/zap_test.go b/contrib/log/zap/zap_test.go
new file mode 100644
index 00000000..efaca258
--- /dev/null
+++ b/contrib/log/zap/zap_test.go
@@ -0,0 +1,22 @@
+package zap
+
+import (
+	"testing"
+
+	"go.uber.org/zap"
+
+	"github.com/go-kratos/kratos/v2/log"
+)
+
+func TestLogger(t *testing.T) {
+	logger := NewLogger(zap.NewExample())
+
+	defer func() { _ = logger.Sync() }()
+
+	zlog := log.NewHelper(logger)
+
+	zlog.Debugw("log", "debug")
+	zlog.Infow("log", "info")
+	zlog.Warnw("log", "warn")
+	zlog.Errorw("log", "error")
+}
diff --git a/contrib/registry/etcd/registry_test.go b/contrib/registry/etcd/registry_test.go
index 67e9d244..844d67ff 100644
--- a/contrib/registry/etcd/registry_test.go
+++ b/contrib/registry/etcd/registry_test.go
@@ -3,17 +3,20 @@ package etcd
 import (
 	"context"
 	"fmt"
-	"google.golang.org/grpc"
 	"testing"
 	"time"
 
+	"google.golang.org/grpc"
+
 	"github.com/go-kratos/kratos/v2/registry"
 	clientv3 "go.etcd.io/etcd/client/v3"
 )
 
 func TestRegistry(t *testing.T) {
-	client, err := clientv3.New(clientv3.Config{Endpoints: []string{"127.0.0.1:2379"},
-		DialTimeout: time.Second, DialOptions: []grpc.DialOption{grpc.WithBlock()}})
+	client, err := clientv3.New(clientv3.Config{
+		Endpoints:   []string{"127.0.0.1:2379"},
+		DialTimeout: time.Second, DialOptions: []grpc.DialOption{grpc.WithBlock()},
+	})
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -30,11 +33,13 @@ func TestRegistry(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	defer w.Stop()
+	defer func() {
+		_ = w.Stop()
+	}()
 	go func() {
 		for {
-			res, err := w.Next()
-			if err != nil {
+			res, err1 := w.Next()
+			if err1 != nil {
 				return
 			}
 			t.Logf("watch: %d", len(res))
@@ -45,8 +50,8 @@ func TestRegistry(t *testing.T) {
 	}()
 	time.Sleep(time.Second)
 
-	if err := r.Register(ctx, s); err != nil {
-		t.Fatal(err)
+	if err1 := r.Register(ctx, s); err1 != nil {
+		t.Fatal(err1)
 	}
 	time.Sleep(time.Second)
 
@@ -58,8 +63,8 @@ func TestRegistry(t *testing.T) {
 		t.Errorf("not expected: %+v", res)
 	}
 
-	if err := r.Deregister(ctx, s); err != nil {
-		t.Fatal(err)
+	if err1 := r.Deregister(ctx, s); err1 != nil {
+		t.Fatal(err1)
 	}
 	time.Sleep(time.Second)
 
@@ -73,8 +78,10 @@ func TestRegistry(t *testing.T) {
 }
 
 func TestHeartBeat(t *testing.T) {
-	client, err := clientv3.New(clientv3.Config{Endpoints: []string{"127.0.0.1:2379"},
-		DialTimeout: time.Second, DialOptions: []grpc.DialOption{grpc.WithBlock()}})
+	client, err := clientv3.New(clientv3.Config{
+		Endpoints:   []string{"127.0.0.1:2379"},
+		DialTimeout: time.Second, DialOptions: []grpc.DialOption{grpc.WithBlock()},
+	})
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -88,14 +95,16 @@ func TestHeartBeat(t *testing.T) {
 
 	go func() {
 		r := New(client)
-		w, err := r.Watch(ctx, s.Name)
-		if err != nil {
+		w, err1 := r.Watch(ctx, s.Name)
+		if err1 != nil {
 			return
 		}
-		defer w.Stop()
+		defer func() {
+			_ = w.Stop()
+		}()
 		for {
-			res, err := w.Next()
-			if err != nil {
+			res, err2 := w.Next()
+			if err2 != nil {
 				return
 			}
 			t.Logf("watch: %d", len(res))
@@ -141,5 +150,4 @@ func TestHeartBeat(t *testing.T) {
 	if len(res) == 0 {
 		t.Errorf("reconnect failed")
 	}
-
 }
diff --git a/errors/errors_test.go b/errors/errors_test.go
index b9287950..1408ec67 100644
--- a/errors/errors_test.go
+++ b/errors/errors_test.go
@@ -5,6 +5,10 @@ import (
 	"fmt"
 	"net/http"
 	"testing"
+
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
+	"google.golang.org/grpc/test/grpc_testing"
 )
 
 func TestError(t *testing.T) {
@@ -46,4 +50,11 @@ func TestError(t *testing.T) {
 	if se.Reason != "reason" {
 		t.Errorf("got %+v want %+v", se, err)
 	}
+
+	gs2, _ := status.New(codes.InvalidArgument, "bad request").WithDetails(&grpc_testing.Empty{})
+	se2 := FromError(gs2.Err())
+	// codes.InvalidArgument should convert to http.StatusBadRequest
+	if se2.Code != http.StatusBadRequest {
+		t.Errorf("convert code err, got %d want %d", UnknownCode, http.StatusBadRequest)
+	}
 }
diff --git a/examples/event/memory/memory_test.go b/examples/event/memory/memory_test.go
index 3d6ec2cd..813bcf86 100644
--- a/examples/event/memory/memory_test.go
+++ b/examples/event/memory/memory_test.go
@@ -3,9 +3,10 @@ package memory
 import (
 	"context"
 	"fmt"
-	"github.com/go-kratos/kratos/examples/event/event"
 	"testing"
 	"time"
+
+	"github.com/go-kratos/kratos/examples/event/event"
 )
 
 func TestSendAndReceive(t *testing.T) {
@@ -14,7 +15,6 @@ func TestSendAndReceive(t *testing.T) {
 		t.Log(fmt.Sprintf("key:%s, value:%s\n", event.Key(), event.Value()))
 		return nil
 	})
-
 	if err != nil {
 		t.Error(err)
 	}
diff --git a/examples/log/logrus_test.go b/examples/log/logrus_test.go
index c8e204e0..62f1cb48 100644
--- a/examples/log/logrus_test.go
+++ b/examples/log/logrus_test.go
@@ -18,9 +18,27 @@ func TestLoggerLog(t *testing.T) {
 		kvs       []interface{}
 		want      string
 	}{
-		"json format":   {level: logrus.InfoLevel, formatter: &logrus.JSONFormatter{}, logLevel: log.LevelInfo, kvs: []interface{}{"case", "json format", "msg", "1"}, want: `{"case":"json format","level":"info","msg":"1"`},
-		"level unmatch": {level: logrus.InfoLevel, formatter: &logrus.JSONFormatter{}, logLevel: log.LevelDebug, kvs: []interface{}{"case", "level unmatch", "msg", "1"}, want: ""},
-		"no tags":       {level: logrus.InfoLevel, formatter: &logrus.JSONFormatter{}, logLevel: log.LevelInfo, kvs: []interface{}{"msg", "1"}, want: `{"level":"info","msg":"1"`},
+		"json format": {
+			level:     logrus.InfoLevel,
+			formatter: &logrus.JSONFormatter{},
+			logLevel:  log.LevelInfo,
+			kvs:       []interface{}{"case", "json format", "msg", "1"},
+			want:      `{"case":"json format","level":"info","msg":"1"`,
+		},
+		"level unmatch": {
+			level:     logrus.InfoLevel,
+			formatter: &logrus.JSONFormatter{},
+			logLevel:  log.LevelDebug,
+			kvs:       []interface{}{"case", "level unmatch", "msg", "1"},
+			want:      "",
+		},
+		"no tags": {
+			level:     logrus.InfoLevel,
+			formatter: &logrus.JSONFormatter{},
+			logLevel:  log.LevelInfo,
+			kvs:       []interface{}{"msg", "1"},
+			want:      `{"level":"info","msg":"1"`,
+		},
 	}
 
 	for name, test := range tests {
diff --git a/examples/log/zap_test.go b/examples/log/zap_test.go
index 5cfb444c..d41de9ab 100644
--- a/examples/log/zap_test.go
+++ b/examples/log/zap_test.go
@@ -35,5 +35,6 @@ func TestZapLogger(t *testing.T) {
 	zlog.Infow("name", "kratos", "from", "opensource")
 	zlog.Infow("name", "kratos", "from")
 
-	defer logger.Sync()
+	// zap stdout/stderr Sync bugs in OSX, see https://github.com/uber-go/zap/issues/370
+	_ = logger.Sync()
 }
diff --git a/examples/registry/registry_test.go b/examples/registry/registry_test.go
index 56c64d85..0094d52b 100644
--- a/examples/registry/registry_test.go
+++ b/examples/registry/registry_test.go
@@ -9,6 +9,7 @@ import (
 
 	"github.com/go-kratos/kratos/examples/helloworld/helloworld"
 	pb "github.com/go-kratos/kratos/examples/helloworld/helloworld"
+	"github.com/stretchr/testify/assert"
 
 	consulregistry "github.com/go-kratos/kratos/contrib/registry/consul/v2"
 	etcdregistry "github.com/go-kratos/kratos/contrib/registry/etcd/v2"
@@ -105,7 +106,7 @@ func TestETCD(t *testing.T) {
 	}
 	callHTTP(t, r)
 	callGRPC(t, r)
-	srv.Stop()
+	assert.NoError(t, srv.Stop())
 }
 
 func TestConsul(t *testing.T) {
@@ -120,5 +121,5 @@ func TestConsul(t *testing.T) {
 	}
 	callHTTP(t, r)
 	callGRPC(t, r)
-	srv.Stop()
+	assert.NoError(t, srv.Stop())
 }
diff --git a/examples/tls/tls_test.go b/examples/tls/tls_test.go
index 166f727b..27c961bf 100644
--- a/examples/tls/tls_test.go
+++ b/examples/tls/tls_test.go
@@ -16,6 +16,7 @@ import (
 	"github.com/go-kratos/kratos/v2/registry"
 	"github.com/go-kratos/kratos/v2/transport/grpc"
 	"github.com/go-kratos/kratos/v2/transport/http"
+	"github.com/stretchr/testify/assert"
 	etcd "go.etcd.io/etcd/client/v3"
 )
 
@@ -127,6 +128,6 @@ func TestETCD(t *testing.T) {
 	}
 	callHTTP(t, r, tlsConf)
 	callGRPC(t, r, tlsConf)
-	srv.Stop()
-	srvTLS.Stop()
+	assert.NoError(t, srv.Stop())
+	assert.NoError(t, srvTLS.Stop())
 }
diff --git a/middleware/auth/jwt/jwt_test.go b/middleware/auth/jwt/jwt_test.go
new file mode 100644
index 00000000..627b585e
--- /dev/null
+++ b/middleware/auth/jwt/jwt_test.go
@@ -0,0 +1,331 @@
+package jwt
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"net/http"
+	"testing"
+	"time"
+
+	"github.com/golang-jwt/jwt/v4"
+
+	"github.com/go-kratos/kratos/v2/middleware"
+	"github.com/go-kratos/kratos/v2/transport"
+	"github.com/stretchr/testify/assert"
+)
+
+type headerCarrier http.Header
+
+func (hc headerCarrier) Get(key string) string { return http.Header(hc).Get(key) }
+
+func (hc headerCarrier) Set(key string, value string) { http.Header(hc).Set(key, value) }
+
+// Keys lists the keys stored in this carrier.
+func (hc headerCarrier) Keys() []string {
+	keys := make([]string, 0, len(hc))
+	for k := range http.Header(hc) {
+		keys = append(keys, k)
+	}
+	return keys
+}
+
+func newTokenHeader(headerKey string, token string) *headerCarrier {
+	header := &headerCarrier{}
+	header.Set(headerKey, token)
+	return header
+}
+
+type Transport struct {
+	kind      transport.Kind
+	endpoint  string
+	operation string
+	reqHeader transport.Header
+}
+
+func (tr *Transport) Kind() transport.Kind {
+	return tr.kind
+}
+
+func (tr *Transport) Endpoint() string {
+	return tr.endpoint
+}
+
+func (tr *Transport) Operation() string {
+	return tr.operation
+}
+
+func (tr *Transport) RequestHeader() transport.Header {
+	return tr.reqHeader
+}
+
+func (tr *Transport) ReplyHeader() transport.Header {
+	return nil
+}
+
+func TestServer(t *testing.T) {
+	testKey := "testKey"
+	mapClaims := jwt.MapClaims{}
+	mapClaims["name"] = "xiaoli"
+	claims := jwt.NewWithClaims(jwt.SigningMethodHS256, mapClaims)
+	token, err := claims.SignedString([]byte(testKey))
+	if err != nil {
+		panic(err)
+	}
+	token = fmt.Sprintf(bearerFormat, token)
+	tests := []struct {
+		name          string
+		ctx           context.Context
+		signingMethod jwt.SigningMethod
+		exceptErr     error
+		key           string
+	}{
+		{
+			name:          "normal",
+			ctx:           transport.NewServerContext(context.Background(), &Transport{reqHeader: newTokenHeader(authorizationKey, token)}),
+			signingMethod: jwt.SigningMethodHS256,
+			exceptErr:     nil,
+			key:           testKey,
+		},
+		{
+			name:          "miss token",
+			ctx:           transport.NewServerContext(context.Background(), &Transport{reqHeader: headerCarrier{}}),
+			signingMethod: jwt.SigningMethodHS256,
+			exceptErr:     ErrMissingJwtToken,
+			key:           testKey,
+		},
+		{
+			name: "token invalid",
+			ctx: transport.NewServerContext(context.Background(), &Transport{
+				reqHeader: newTokenHeader(authorizationKey, fmt.Sprintf(bearerFormat, "12313123")),
+			}),
+			signingMethod: jwt.SigningMethodHS256,
+			exceptErr:     ErrTokenInvalid,
+			key:           testKey,
+		},
+		{
+			name:          "method invalid",
+			ctx:           transport.NewServerContext(context.Background(), &Transport{reqHeader: newTokenHeader(authorizationKey, token)}),
+			signingMethod: jwt.SigningMethodES384,
+			exceptErr:     ErrUnSupportSigningMethod,
+			key:           testKey,
+		},
+		{
+			name:          "miss signing method",
+			ctx:           transport.NewServerContext(context.Background(), &Transport{reqHeader: newTokenHeader(authorizationKey, token)}),
+			signingMethod: nil,
+			exceptErr:     nil,
+			key:           testKey,
+		},
+		{
+			name:          "miss signing method",
+			ctx:           transport.NewServerContext(context.Background(), &Transport{reqHeader: newTokenHeader(authorizationKey, token)}),
+			signingMethod: nil,
+			exceptErr:     nil,
+			key:           testKey,
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			var testToken jwt.Claims
+			next := func(ctx context.Context, req interface{}) (interface{}, error) {
+				t.Log(req)
+				testToken, _ = FromContext(ctx)
+				return "reply", nil
+			}
+			var server middleware.Handler
+			if test.signingMethod != nil {
+				server = Server(func(token *jwt.Token) (interface{}, error) {
+					return []byte(test.key), nil
+				}, WithSigningMethod(test.signingMethod))(next)
+			} else {
+				server = Server(func(token *jwt.Token) (interface{}, error) {
+					return []byte(test.key), nil
+				})(next)
+			}
+			_, err2 := server(test.ctx, test.name)
+			assert.Equal(t, test.exceptErr, err2)
+			if test.exceptErr == nil {
+				assert.NotNil(t, testToken)
+				_, ok := testToken.(jwt.MapClaims)
+				assert.True(t, ok)
+			}
+		})
+	}
+}
+
+func TestClient(t *testing.T) {
+	testKey := "testKey"
+	claims := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.StandardClaims{})
+	token, err := claims.SignedString([]byte(testKey))
+	if err != nil {
+		panic(err)
+	}
+	tProvider := func(*jwt.Token) (interface{}, error) {
+		return []byte(testKey), nil
+	}
+	tests := []struct {
+		name          string
+		expectError   error
+		tokenProvider jwt.Keyfunc
+	}{
+		{
+			name:          "normal",
+			expectError:   nil,
+			tokenProvider: tProvider,
+		},
+		{
+			name:          "miss token provider",
+			expectError:   ErrNeedTokenProvider,
+			tokenProvider: nil,
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			next := func(ctx context.Context, req interface{}) (interface{}, error) {
+				return "reply", nil
+			}
+			handler := Client(test.tokenProvider)(next)
+			header := &headerCarrier{}
+			_, err2 := handler(transport.NewClientContext(context.Background(), &Transport{reqHeader: header}), "ok")
+			assert.Equal(t, test.expectError, err2)
+			if err2 == nil {
+				assert.Equal(t, fmt.Sprintf(bearerFormat, token), header.Get(authorizationKey))
+			}
+		})
+	}
+}
+
+func TestTokenExpire(t *testing.T) {
+	testKey := "testKey"
+	claims := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.StandardClaims{
+		ExpiresAt: time.Now().Add(time.Millisecond).Unix(),
+	})
+	token, err := claims.SignedString([]byte(testKey))
+	if err != nil {
+		panic(err)
+	}
+	token = fmt.Sprintf(bearerFormat, token)
+	time.Sleep(time.Second)
+	next := func(ctx context.Context, req interface{}) (interface{}, error) {
+		t.Log(req)
+		return "reply", nil
+	}
+	ctx := transport.NewServerContext(context.Background(), &Transport{reqHeader: newTokenHeader(authorizationKey, token)})
+	server := Server(func(token *jwt.Token) (interface{}, error) {
+		return []byte(testKey), nil
+	}, WithSigningMethod(jwt.SigningMethodHS256))(next)
+	_, err2 := server(ctx, "test expire token")
+	assert.Equal(t, ErrTokenExpired, err2)
+}
+
+func TestMissingKeyFunc(t *testing.T) {
+	testKey := "testKey"
+	mapClaims := jwt.MapClaims{}
+	mapClaims["name"] = "xiaoli"
+	claims := jwt.NewWithClaims(jwt.SigningMethodHS256, mapClaims)
+	token, err := claims.SignedString([]byte(testKey))
+	if err != nil {
+		panic(err)
+	}
+	token = fmt.Sprintf(bearerFormat, token)
+	test := struct {
+		name          string
+		ctx           context.Context
+		signingMethod jwt.SigningMethod
+		exceptErr     error
+		key           string
+	}{
+		name:          "miss key",
+		ctx:           transport.NewServerContext(context.Background(), &Transport{reqHeader: newTokenHeader(authorizationKey, token)}),
+		signingMethod: jwt.SigningMethodHS256,
+		exceptErr:     ErrMissingKeyFunc,
+		key:           "",
+	}
+
+	var testToken jwt.Claims
+	next := func(ctx context.Context, req interface{}) (interface{}, error) {
+		t.Log(req)
+		testToken, _ = FromContext(ctx)
+		return "reply", nil
+	}
+	server := Server(nil)(next)
+	_, err2 := server(test.ctx, test.name)
+	assert.Equal(t, test.exceptErr, err2)
+	if test.exceptErr == nil {
+		assert.NotNil(t, testToken)
+	}
+}
+
+func TestClientWithClaims(t *testing.T) {
+	testKey := "testKey"
+	mapClaims := jwt.MapClaims{}
+	mapClaims["name"] = "xiaoli"
+	claims := jwt.NewWithClaims(jwt.SigningMethodHS256, mapClaims)
+	token, err := claims.SignedString([]byte(testKey))
+	if err != nil {
+		panic(err)
+	}
+	tProvider := func(*jwt.Token) (interface{}, error) {
+		return []byte(testKey), nil
+	}
+	test := struct {
+		name          string
+		expectError   error
+		tokenProvider jwt.Keyfunc
+	}{
+		name:          "normal",
+		expectError:   nil,
+		tokenProvider: tProvider,
+	}
+
+	t.Run(test.name, func(t *testing.T) {
+		next := func(ctx context.Context, req interface{}) (interface{}, error) {
+			return "reply", nil
+		}
+		handler := Client(test.tokenProvider, WithClaims(mapClaims))(next)
+		header := &headerCarrier{}
+		_, err2 := handler(transport.NewClientContext(context.Background(), &Transport{reqHeader: header}), "ok")
+		assert.Equal(t, test.expectError, err2)
+		if err2 == nil {
+			assert.Equal(t, fmt.Sprintf(bearerFormat, token), header.Get(authorizationKey))
+		}
+	})
+}
+
+func TestClientMissKey(t *testing.T) {
+	testKey := "testKey"
+	mapClaims := jwt.MapClaims{}
+	mapClaims["name"] = "xiaoli"
+	claims := jwt.NewWithClaims(jwt.SigningMethodHS256, mapClaims)
+	token, err := claims.SignedString([]byte(testKey))
+	if err != nil {
+		panic(err)
+	}
+	tProvider := func(*jwt.Token) (interface{}, error) {
+		return nil, errors.New("some error")
+	}
+	test := struct {
+		name          string
+		expectError   error
+		tokenProvider jwt.Keyfunc
+	}{
+		name:          "normal",
+		expectError:   ErrGetKey,
+		tokenProvider: tProvider,
+	}
+
+	t.Run(test.name, func(t *testing.T) {
+		next := func(ctx context.Context, req interface{}) (interface{}, error) {
+			return "reply", nil
+		}
+		handler := Client(test.tokenProvider, WithClaims(mapClaims))(next)
+		header := &headerCarrier{}
+		_, err2 := handler(transport.NewClientContext(context.Background(), &Transport{reqHeader: header}), "ok")
+		assert.Equal(t, test.expectError, err2)
+		if err2 == nil {
+			assert.Equal(t, fmt.Sprintf(bearerFormat, token), header.Get(authorizationKey))
+		}
+	})
+}
diff --git a/transport/grpc/server_test.go b/transport/grpc/server_test.go
index 4af5e454..3b9f1ede 100644
--- a/transport/grpc/server_test.go
+++ b/transport/grpc/server_test.go
@@ -143,7 +143,7 @@ func TestServer_unaryServerInterceptor(t *testing.T) {
 	u, err := url.Parse("grpc://hello/world")
 	assert.NoError(t, err)
 	srv := &Server{
-		ctx:        context.Background(),
+		baseCtx:    context.Background(),
 		endpoint:   u,
 		middleware: []middleware.Middleware{EmptyMiddleware()},
 		timeout:    time.Duration(10),
diff --git a/transport/http/server_test.go b/transport/http/server_test.go
index 1d84d8eb..f32a459d 100644
--- a/transport/http/server_test.go
+++ b/transport/http/server_test.go
@@ -45,7 +45,7 @@ func TestServer(t *testing.T) {
 	}()
 	time.Sleep(time.Second)
 	testClient(t, srv)
-	_ = srv.Stop(ctx)
+	assert.NoError(t, srv.Stop(ctx))
 }
 
 func testClient(t *testing.T, srv *Server) {