Repository: go-kratos/kratos
Pull request #1339 (2021-08-15 10:20:45)

refactor(encoding): optimize json encoding
优化如下：
1、支持使用easyjson或定制实现方法，来提升序列化特别是反序列化的性能
2、api接口提交的body为空json体时，不需要进行反序列化，可以大幅提升性能
3、原先的反序列化方法中先进行反射，调整顺序到后面，避免每次都应用反射

aiscrm: > import 没排序

谢谢，没留意，重新排序了
shenqidebaozi: 单元测试貌似也是失败的
aiscrm: > 单元测试貌似也是失败的，提交之前应该跑一下单测

你可以看一下单元测试失败的记录，不是我的代码一起的，是 `config/reader_test.go` 的代码引起的。
![image](https://user-images.githubusercontent.com/30600079/129486602-497aa535-cda8-4bb9-bf2e-501c17543315.png)

shenqidebaozi: > > 单元测试貌似也是失败的，提交之前应该跑一下单测
> 
> 你可以看一下单元测试失败的记录，不是我的代码一起的，是 `config/reader_test.go` 的代码引起的。
> ![image](https://user-images.githubusercontent.com/30600079/129486602-497aa535-cda8-4bb9-bf2e-501c17543315.png)

这个单元测试有用到 encoding json 吧？你看一下吧。

Patch:
diff --git a/.golangci.yml b/.golangci.yml
index 73ca1662..8081ca1f 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -10,4 +10,5 @@ linters:
     - staticcheck
     - govet
     - gosimple
-    - gofmt
\ No newline at end of file
+    - gofmt
+    - errcheck
\ No newline at end of file
diff --git a/app.go b/app.go
index 7ef71ce3..9d5657f0 100644
--- a/app.go
+++ b/app.go
@@ -38,7 +38,7 @@ type App struct {
 func New(opts ...Option) *App {
 	options := options{
 		ctx:              context.Background(),
-		logger:           log.DefaultLogger,
+		logger:           log.NewHelper(log.DefaultLogger),
 		sigs:             []os.Signal{syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT},
 		registrarTimeout: 10 * time.Second,
 	}
@@ -114,7 +114,10 @@ func (a *App) Run() error {
 			case <-ctx.Done():
 				return ctx.Err()
 			case <-c:
-				a.Stop()
+				err := a.Stop()
+				if err != nil {
+					a.opts.logger.Errorf("failed to app stop: %v", err)
+				}
 			}
 		}
 	})
diff --git a/cmd/kratos/internal/proto/add/template.go b/cmd/kratos/internal/proto/add/template.go
index 80559677..c518a176 100644
--- a/cmd/kratos/internal/proto/add/template.go
+++ b/cmd/kratos/internal/proto/add/template.go
@@ -16,11 +16,11 @@ option java_multiple_files = true;
 option java_package = "{{.JavaPackage}}";
 
 service {{.Service}} {
-    rpc Create{{.Service}} (Create{{.Service}}Request) returns (Create{{.Service}}Reply);
-    rpc Update{{.Service}} (Update{{.Service}}Request) returns (Update{{.Service}}Reply);
-    rpc Delete{{.Service}} (Delete{{.Service}}Request) returns (Delete{{.Service}}Reply);
-    rpc Get{{.Service}} (Get{{.Service}}Request) returns (Get{{.Service}}Reply);
-    rpc List{{.Service}} (List{{.Service}}Request) returns (List{{.Service}}Reply);
+	rpc Create{{.Service}} (Create{{.Service}}Request) returns (Create{{.Service}}Reply);
+	rpc Update{{.Service}} (Update{{.Service}}Request) returns (Update{{.Service}}Reply);
+	rpc Delete{{.Service}} (Delete{{.Service}}Request) returns (Delete{{.Service}}Reply);
+	rpc Get{{.Service}} (Get{{.Service}}Request) returns (Get{{.Service}}Reply);
+	rpc List{{.Service}} (List{{.Service}}Request) returns (List{{.Service}}Reply);
 }
 
 message Create{{.Service}}Request {}
diff --git a/cmd/kratos/internal/proto/server/template.go b/cmd/kratos/internal/proto/server/template.go
index 54ca8169..f394322d 100644
--- a/cmd/kratos/internal/proto/server/template.go
+++ b/cmd/kratos/internal/proto/server/template.go
@@ -11,16 +11,16 @@ package service
 
 import (
 	{{- if .UseContext }}
-    "context"
+	"context"
 	{{- end }}
 	{{- if .UseIO }}
 	"io"
 	{{- end }}
 
-    pb "{{ .Package }}"
-    {{- if .GoogleEmpty }}
-    "google.golang.org/protobuf/types/known/emptypb"
-    {{- end }}
+	pb "{{ .Package }}"
+	{{- if .GoogleEmpty }}
+	"google.golang.org/protobuf/types/known/emptypb"
+	{{- end }}
 )
 
 type {{ .Service }}Service struct {
diff --git a/config/env/watcher.go b/config/env/watcher.go
index fa7c7682..2ea02931 100644
--- a/config/env/watcher.go
+++ b/config/env/watcher.go
@@ -7,8 +7,6 @@ import (
 )
 
 type watcher struct {
-	exit chan struct{}
-
 	ctx    context.Context
 	cancel context.CancelFunc
 }
@@ -17,21 +15,16 @@ var _ config.Watcher = (*watcher)(nil)
 
 func NewWatcher() (config.Watcher, error) {
 	ctx, cancel := context.WithCancel(context.Background())
-	return &watcher{exit: make(chan struct{}), ctx: ctx, cancel: cancel}, nil
+	return &watcher{ctx: ctx, cancel: cancel}, nil
 }
 
 // Next will be blocked until the Stop method is called
 func (w *watcher) Next() ([]*config.KeyValue, error) {
-	select {
-	case <-w.ctx.Done():
-		return nil, w.ctx.Err()
-	case <-w.exit:
-		return nil, nil
-	}
+	<-w.ctx.Done()
+	return nil, w.ctx.Err()
 }
 
 func (w *watcher) Stop() error {
-	close(w.exit)
 	w.cancel()
 	return nil
 }
diff --git a/encoding/json/json.go b/encoding/json/json.go
index a2a45f09..d5e020b7 100644
--- a/encoding/json/json.go
+++ b/encoding/json/json.go
@@ -31,26 +31,35 @@ func init() {
 type codec struct{}
 
 func (codec) Marshal(v interface{}) ([]byte, error) {
-	if m, ok := v.(proto.Message); ok {
+	switch m := v.(type) {
+	case json.Marshaler:
+		return m.MarshalJSON()
+	case proto.Message:
 		return MarshalOptions.Marshal(m)
+	default:
+		return json.Marshal(m)
 	}
-	return json.Marshal(v)
 }
 
 func (codec) Unmarshal(data []byte, v interface{}) error {
-	rv := reflect.ValueOf(v)
-	for rv := rv; rv.Kind() == reflect.Ptr; {
-		if rv.IsNil() {
-			rv.Set(reflect.New(rv.Type().Elem()))
-		}
-		rv = rv.Elem()
-	}
-	if m, ok := v.(proto.Message); ok {
-		return UnmarshalOptions.Unmarshal(data, m)
-	} else if m, ok := reflect.Indirect(rv).Interface().(proto.Message); ok {
+	switch m := v.(type) {
+	case json.Unmarshaler:
+		return m.UnmarshalJSON(data)
+	case proto.Message:
 		return UnmarshalOptions.Unmarshal(data, m)
+	default:
+		rv := reflect.ValueOf(v)
+		for rv := rv; rv.Kind() == reflect.Ptr; {
+			if rv.IsNil() {
+				rv.Set(reflect.New(rv.Type().Elem()))
+			}
+			rv = rv.Elem()
+		}
+		if m, ok := reflect.Indirect(rv).Interface().(proto.Message); ok {
+			return UnmarshalOptions.Unmarshal(data, m)
+		}
+		return json.Unmarshal(data, m)
 	}
-	return json.Unmarshal(data, v)
 }
 
 func (codec) Name() string {
diff --git a/errors/errors.pb.go b/errors/errors.pb.go
index ab9f3147..d09b70fa 100644
--- a/errors/errors.pb.go
+++ b/errors/errors.pb.go
@@ -1,8 +1,8 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.26.0
+// 	protoc-gen-go v1.27.1
 // 	protoc        v3.17.3
-// source: errors.proto
+// source: errors/errors.proto
 
 package errors
 
@@ -35,7 +35,7 @@ type Error struct {
 func (x *Error) Reset() {
 	*x = Error{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_errors_proto_msgTypes[0]
+		mi := &file_errors_errors_proto_msgTypes[0]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -48,7 +48,7 @@ func (x *Error) String() string {
 func (*Error) ProtoMessage() {}
 
 func (x *Error) ProtoReflect() protoreflect.Message {
-	mi := &file_errors_proto_msgTypes[0]
+	mi := &file_errors_errors_proto_msgTypes[0]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -61,7 +61,7 @@ func (x *Error) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use Error.ProtoReflect.Descriptor instead.
 func (*Error) Descriptor() ([]byte, []int) {
-	return file_errors_proto_rawDescGZIP(), []int{0}
+	return file_errors_errors_proto_rawDescGZIP(), []int{0}
 }
 
 func (x *Error) GetCode() int32 {
@@ -92,14 +92,14 @@ func (x *Error) GetMetadata() map[string]string {
 	return nil
 }
 
-var file_errors_proto_extTypes = []protoimpl.ExtensionInfo{
+var file_errors_errors_proto_extTypes = []protoimpl.ExtensionInfo{
 	{
 		ExtendedType:  (*descriptorpb.EnumOptions)(nil),
 		ExtensionType: (*int32)(nil),
 		Field:         1108,
 		Name:          "errors.default_code",
 		Tag:           "varint,1108,opt,name=default_code",
-		Filename:      "errors.proto",
+		Filename:      "errors/errors.proto",
 	},
 	{
 		ExtendedType:  (*descriptorpb.EnumValueOptions)(nil),
@@ -107,77 +107,78 @@ var file_errors_proto_extTypes = []protoimpl.ExtensionInfo{
 		Field:         1109,
 		Name:          "errors.code",
 		Tag:           "varint,1109,opt,name=code",
-		Filename:      "errors.proto",
+		Filename:      "errors/errors.proto",
 	},
 }
 
 // Extension fields to descriptorpb.EnumOptions.
 var (
 	// optional int32 default_code = 1108;
-	E_DefaultCode = &file_errors_proto_extTypes[0]
+	E_DefaultCode = &file_errors_errors_proto_extTypes[0]
 )
 
 // Extension fields to descriptorpb.EnumValueOptions.
 var (
 	// optional int32 code = 1109;
-	E_Code = &file_errors_proto_extTypes[1]
+	E_Code = &file_errors_errors_proto_extTypes[1]
 )
 
-var File_errors_proto protoreflect.FileDescriptor
-
-var file_errors_proto_rawDesc = []byte{
-	0x0a, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06,
-	0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70,
-	0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
-	0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc3, 0x01, 0x0a, 0x05, 0x45, 0x72, 0x72,
-	0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05,
-	0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e,
-	0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x18,
-	0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x37, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61,
-	0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x72, 0x72,
-	0x6f, 0x72, 0x73, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
-	0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
-	0x61, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74,
-	0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x3a, 0x40,
-	0x0a, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x1c,
-	0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
-	0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd4, 0x08, 0x20,
-	0x01, 0x28, 0x05, 0x52, 0x0b, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x64, 0x65,
-	0x3a, 0x36, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
-	0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x56,
-	0x61, 0x6c, 0x75, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd5, 0x08, 0x20, 0x01,
-	0x28, 0x05, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x42, 0x59, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e,
-	0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x65, 0x72,
-	0x72, 0x6f, 0x72, 0x73, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
-	0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x2d, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f, 0x6b, 0x72, 0x61,
-	0x74, 0x6f, 0x73, 0x2f, 0x76, 0x32, 0x2f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x3b, 0x65, 0x72,
-	0x72, 0x6f, 0x72, 0x73, 0xa2, 0x02, 0x0c, 0x4b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x45, 0x72, 0x72,
-	0x6f, 0x72, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+var File_errors_errors_proto protoreflect.FileDescriptor
+
+var file_errors_errors_proto_rawDesc = []byte{
+	0x0a, 0x13, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2e,
+	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x1a, 0x20, 0x67,
+	0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64,
+	0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22,
+	0xc3, 0x01, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64,
+	0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x16, 0x0a,
+	0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72,
+	0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
+	0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12,
+	0x37, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x03, 0x28,
+	0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72,
+	0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08,
+	0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61,
+	0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76,
+	0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
+	0x65, 0x3a, 0x02, 0x38, 0x01, 0x3a, 0x40, 0x0a, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74,
+	0x5f, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x4f, 0x70, 0x74, 0x69,
+	0x6f, 0x6e, 0x73, 0x18, 0xd4, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x64, 0x65, 0x66, 0x61,
+	0x75, 0x6c, 0x74, 0x43, 0x6f, 0x64, 0x65, 0x3a, 0x36, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12,
+	0x21, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
+	0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f,
+	0x6e, 0x73, 0x18, 0xd5, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x42,
+	0x59, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x6b, 0x72,
+	0x61, 0x74, 0x6f, 0x73, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x50, 0x01, 0x5a, 0x2c, 0x67,
+	0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x2d, 0x6b, 0x72, 0x61,
+	0x74, 0x6f, 0x73, 0x2f, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f, 0x76, 0x32, 0x2f, 0x65, 0x72,
+	0x72, 0x6f, 0x72, 0x73, 0x3b, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0xa2, 0x02, 0x0c, 0x4b, 0x72,
+	0x61, 0x74, 0x6f, 0x73, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x33,
 }
 
 var (
-	file_errors_proto_rawDescOnce sync.Once
-	file_errors_proto_rawDescData = file_errors_proto_rawDesc
+	file_errors_errors_proto_rawDescOnce sync.Once
+	file_errors_errors_proto_rawDescData = file_errors_errors_proto_rawDesc
 )
 
-func file_errors_proto_rawDescGZIP() []byte {
-	file_errors_proto_rawDescOnce.Do(func() {
-		file_errors_proto_rawDescData = protoimpl.X.CompressGZIP(file_errors_proto_rawDescData)
+func file_errors_errors_proto_rawDescGZIP() []byte {
+	file_errors_errors_proto_rawDescOnce.Do(func() {
+		file_errors_errors_proto_rawDescData = protoimpl.X.CompressGZIP(file_errors_errors_proto_rawDescData)
 	})
-	return file_errors_proto_rawDescData
+	return file_errors_errors_proto_rawDescData
 }
 
-var file_errors_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
-var file_errors_proto_goTypes = []interface{}{
+var file_errors_errors_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_errors_errors_proto_goTypes = []interface{}{
 	(*Error)(nil),                         // 0: errors.Error
 	nil,                                   // 1: errors.Error.MetadataEntry
 	(*descriptorpb.EnumOptions)(nil),      // 2: google.protobuf.EnumOptions
 	(*descriptorpb.EnumValueOptions)(nil), // 3: google.protobuf.EnumValueOptions
 }
-var file_errors_proto_depIdxs = []int32{
+var file_errors_errors_proto_depIdxs = []int32{
 	1, // 0: errors.Error.metadata:type_name -> errors.Error.MetadataEntry
 	2, // 1: errors.default_code:extendee -> google.protobuf.EnumOptions
 	3, // 2: errors.code:extendee -> google.protobuf.EnumValueOptions
@@ -188,13 +189,13 @@ var file_errors_proto_depIdxs = []int32{
 	0, // [0:1] is the sub-list for field type_name
 }
 
-func init() { file_errors_proto_init() }
-func file_errors_proto_init() {
-	if File_errors_proto != nil {
+func init() { file_errors_errors_proto_init() }
+func file_errors_errors_proto_init() {
+	if File_errors_errors_proto != nil {
 		return
 	}
 	if !protoimpl.UnsafeEnabled {
-		file_errors_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_errors_errors_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*Error); i {
 			case 0:
 				return &v.state
@@ -211,19 +212,19 @@ func file_errors_proto_init() {
 	out := protoimpl.TypeBuilder{
 		File: protoimpl.DescBuilder{
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
-			RawDescriptor: file_errors_proto_rawDesc,
+			RawDescriptor: file_errors_errors_proto_rawDesc,
 			NumEnums:      0,
 			NumMessages:   2,
 			NumExtensions: 2,
 			NumServices:   0,
 		},
-		GoTypes:           file_errors_proto_goTypes,
-		DependencyIndexes: file_errors_proto_depIdxs,
-		MessageInfos:      file_errors_proto_msgTypes,
-		ExtensionInfos:    file_errors_proto_extTypes,
+		GoTypes:           file_errors_errors_proto_goTypes,
+		DependencyIndexes: file_errors_errors_proto_depIdxs,
+		MessageInfos:      file_errors_errors_proto_msgTypes,
+		ExtensionInfos:    file_errors_errors_proto_extTypes,
 	}.Build()
-	File_errors_proto = out.File
-	file_errors_proto_rawDesc = nil
-	file_errors_proto_goTypes = nil
-	file_errors_proto_depIdxs = nil
+	File_errors_errors_proto = out.File
+	file_errors_errors_proto_rawDesc = nil
+	file_errors_errors_proto_goTypes = nil
+	file_errors_errors_proto_depIdxs = nil
 }
diff --git a/internal/context/context.go b/internal/context/context.go
index 0b2c3388..3c15f7a6 100644
--- a/internal/context/context.go
+++ b/internal/context/context.go
@@ -29,9 +29,9 @@ func Merge(parent1, parent2 context.Context) (context.Context, context.CancelFun
 	}
 	select {
 	case <-parent1.Done():
-		mc.finish(parent1.Err())
+		_ = mc.finish(parent1.Err())
 	case <-parent2.Done():
-		mc.finish(parent2.Err())
+		_ = mc.finish(parent2.Err())
 	default:
 		go mc.wait()
 	}
@@ -57,7 +57,7 @@ func (mc *mergeCtx) wait() {
 	case <-mc.cancelCh:
 		err = context.Canceled
 	}
-	mc.finish(err)
+	_ = mc.finish(err)
 }
 
 func (mc *mergeCtx) cancel() {
diff --git a/log/helper.go b/log/helper.go
index dea23c76..6c7f3a57 100644
--- a/log/helper.go
+++ b/log/helper.go
@@ -28,83 +28,83 @@ func (h *Helper) WithContext(ctx context.Context) *Helper {
 
 // Log Print log by level and keyvals.
 func (h *Helper) Log(level Level, keyvals ...interface{}) {
-	h.logger.Log(level, keyvals...)
+	_ = h.logger.Log(level, keyvals...)
 }
 
 // Debug logs a message at debug level.
 func (h *Helper) Debug(a ...interface{}) {
-	h.logger.Log(LevelDebug, "msg", fmt.Sprint(a...))
+	h.Log(LevelDebug, "msg", fmt.Sprint(a...))
 }
 
 // Debugf logs a message at debug level.
 func (h *Helper) Debugf(format string, a ...interface{}) {
-	h.logger.Log(LevelDebug, "msg", fmt.Sprintf(format, a...))
+	h.Log(LevelDebug, "msg", fmt.Sprintf(format, a...))
 }
 
 // Debugw logs a message at debug level.
 func (h *Helper) Debugw(keyvals ...interface{}) {
-	h.logger.Log(LevelDebug, keyvals...)
+	h.Log(LevelDebug, keyvals...)
 }
 
 // Info logs a message at info level.
 func (h *Helper) Info(a ...interface{}) {
-	h.logger.Log(LevelInfo, "msg", fmt.Sprint(a...))
+	h.Log(LevelInfo, "msg", fmt.Sprint(a...))
 }
 
 // Infof logs a message at info level.
 func (h *Helper) Infof(format string, a ...interface{}) {
-	h.logger.Log(LevelInfo, "msg", fmt.Sprintf(format, a...))
+	h.Log(LevelInfo, "msg", fmt.Sprintf(format, a...))
 }
 
 // Infow logs a message at info level.
 func (h *Helper) Infow(keyvals ...interface{}) {
-	h.logger.Log(LevelInfo, keyvals...)
+	h.Log(LevelInfo, keyvals...)
 }
 
 // Warn logs a message at warn level.
 func (h *Helper) Warn(a ...interface{}) {
-	h.logger.Log(LevelWarn, "msg", fmt.Sprint(a...))
+	h.Log(LevelWarn, "msg", fmt.Sprint(a...))
 }
 
 // Warnf logs a message at warnf level.
 func (h *Helper) Warnf(format string, a ...interface{}) {
-	h.logger.Log(LevelWarn, "msg", fmt.Sprintf(format, a...))
+	h.Log(LevelWarn, "msg", fmt.Sprintf(format, a...))
 }
 
 // Warnw logs a message at warnf level.
 func (h *Helper) Warnw(keyvals ...interface{}) {
-	h.logger.Log(LevelWarn, keyvals...)
+	h.Log(LevelWarn, keyvals...)
 }
 
 // Error logs a message at error level.
 func (h *Helper) Error(a ...interface{}) {
-	h.logger.Log(LevelError, "msg", fmt.Sprint(a...))
+	h.Log(LevelError, "msg", fmt.Sprint(a...))
 }
 
 // Errorf logs a message at error level.
 func (h *Helper) Errorf(format string, a ...interface{}) {
-	h.logger.Log(LevelError, "msg", fmt.Sprintf(format, a...))
+	h.Log(LevelError, "msg", fmt.Sprintf(format, a...))
 }
 
 // Errorw logs a message at error level.
 func (h *Helper) Errorw(keyvals ...interface{}) {
-	h.logger.Log(LevelError, keyvals...)
+	h.Log(LevelError, keyvals...)
 }
 
 // Fatal logs a message at fatal level.
 func (h *Helper) Fatal(a ...interface{}) {
-	h.logger.Log(LevelFatal, "msg", fmt.Sprint(a...))
+	h.Log(LevelFatal, "msg", fmt.Sprint(a...))
 	os.Exit(1)
 }
 
 // Fatalf logs a message at fatal level.
 func (h *Helper) Fatalf(format string, a ...interface{}) {
-	h.logger.Log(LevelFatal, "msg", fmt.Sprintf(format, a...))
+	h.Log(LevelFatal, "msg", fmt.Sprintf(format, a...))
 	os.Exit(1)
 }
 
 // Fatalw logs a message at fatal level.
 func (h *Helper) Fatalw(keyvals ...interface{}) {
-	h.logger.Log(LevelFatal, keyvals...)
+	h.Log(LevelFatal, keyvals...)
 	os.Exit(1)
 }
diff --git a/log/std.go b/log/std.go
index 1aab1929..a26258d0 100644
--- a/log/std.go
+++ b/log/std.go
@@ -38,9 +38,9 @@ func (l *stdLogger) Log(level Level, keyvals ...interface{}) error {
 	buf := l.pool.Get().(*bytes.Buffer)
 	buf.WriteString(level.String())
 	for i := 0; i < len(keyvals); i += 2 {
-		fmt.Fprintf(buf, " %s=%v", keyvals[i], keyvals[i+1])
+		_, _ = fmt.Fprintf(buf, " %s=%v", keyvals[i], keyvals[i+1])
 	}
-	l.log.Output(4, buf.String())
+	_ = l.log.Output(4, buf.String())
 	buf.Reset()
 	l.pool.Put(buf)
 	return nil
diff --git a/middleware/logging/logging.go b/middleware/logging/logging.go
index d985e9bb..d050da9a 100644
--- a/middleware/logging/logging.go
+++ b/middleware/logging/logging.go
@@ -32,7 +32,7 @@ func Server(logger log.Logger) middleware.Middleware {
 				reason = se.Reason
 			}
 			level, stack := extractError(err)
-			log.WithContext(ctx, logger).Log(level,
+			_ = log.WithContext(ctx, logger).Log(level,
 				"kind", "server",
 				"component", kind,
 				"operation", operation,
@@ -68,7 +68,7 @@ func Client(logger log.Logger) middleware.Middleware {
 				reason = se.Reason
 			}
 			level, stack := extractError(err)
-			log.WithContext(ctx, logger).Log(level,
+			_ = log.WithContext(ctx, logger).Log(level,
 				"kind", "client",
 				"component", kind,
 				"operation", operation,
diff --git a/options.go b/options.go
index 39047d13..29349f60 100644
--- a/options.go
+++ b/options.go
@@ -25,7 +25,7 @@ type options struct {
 	ctx  context.Context
 	sigs []os.Signal
 
-	logger           log.Logger
+	logger           *log.Helper
 	registrar        registry.Registrar
 	registrarTimeout time.Duration
 	servers          []transport.Server
@@ -63,7 +63,9 @@ func Context(ctx context.Context) Option {
 
 // Logger with service logger.
 func Logger(logger log.Logger) Option {
-	return func(o *options) { o.logger = logger }
+	return func(o *options) {
+		o.logger = log.NewHelper(logger)
+	}
 }
 
 // Server with transport servers.
diff --git a/transport/grpc/client.go b/transport/grpc/client.go
index c4746a7a..f2495e80 100644
--- a/transport/grpc/client.go
+++ b/transport/grpc/client.go
@@ -3,6 +3,7 @@ package grpc
 import (
 	"context"
 	"crypto/tls"
+	"fmt"
 	"time"
 
 	"github.com/go-kratos/kratos/v2/middleware"
@@ -106,8 +107,7 @@ func dial(ctx context.Context, insecure bool, opts ...ClientOption) (*grpc.Clien
 		ints = append(ints, options.ints...)
 	}
 	var grpcOpts = []grpc.DialOption{
-		//todo: grpc.WithBalancerName is deprecated.
-		grpc.WithBalancerName(roundrobin.Name), //nolint:staticcheck
+		grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, roundrobin.Name)),
 		grpc.WithChainUnaryInterceptor(ints...),
 	}
 	if options.discovery != nil {
diff --git a/transport/grpc/resolver/direct/builder.go b/transport/grpc/resolver/direct/builder.go
index fefbe747..dd1ba114 100644
--- a/transport/grpc/resolver/direct/builder.go
+++ b/transport/grpc/resolver/direct/builder.go
@@ -24,9 +24,12 @@ func (d *directBuilder) Build(target resolver.Target, cc resolver.ClientConn, op
 	for _, addr := range strings.Split(target.Endpoint, ",") {
 		addrs = append(addrs, resolver.Address{Addr: addr})
 	}
-	cc.UpdateState(resolver.State{
+	err := cc.UpdateState(resolver.State{
 		Addresses: addrs,
 	})
+	if err != nil {
+		return nil, err
+	}
 	return newDirectResolver(), nil
 }
 
diff --git a/transport/grpc/resolver/discovery/resolver.go b/transport/grpc/resolver/discovery/resolver.go
index f801e0d2..c7eeb32e 100644
--- a/transport/grpc/resolver/discovery/resolver.go
+++ b/transport/grpc/resolver/discovery/resolver.go
@@ -66,14 +66,20 @@ func (r *discoveryResolver) update(ins []*registry.ServiceInstance) {
 		r.log.Warnf("[resolver] Zero endpoint found,refused to write, instances: %v", ins)
 		return
 	}
-	r.cc.UpdateState(resolver.State{Addresses: addrs})
+	err := r.cc.UpdateState(resolver.State{Addresses: addrs})
+	if err != nil {
+		r.log.Errorf("[resolver] failed to update state: %s", err)
+	}
 	b, _ := json.Marshal(ins)
 	r.log.Infof("[resolver] update instances: %s", b)
 }
 
 func (r *discoveryResolver) Close() {
 	r.cancel()
-	r.w.Stop()
+	err := r.w.Stop()
+	if err != nil {
+		r.log.Errorf("[resolver] failed to watch top: %s", err)
+	}
 }
 
 func (r *discoveryResolver) ResolveNow(options resolver.ResolveNowOptions) {}
diff --git a/transport/grpc/server.go b/transport/grpc/server.go
index f94c9dfc..c2aaf2fb 100644
--- a/transport/grpc/server.go
+++ b/transport/grpc/server.go
@@ -155,7 +155,7 @@ func (s *Server) Endpoint() (*url.URL, error) {
 		}
 		addr, err := host.Extract(s.address, lis)
 		if err != nil {
-			lis.Close()
+			_ = lis.Close()
 			s.err = err
 			return
 		}
@@ -211,7 +211,7 @@ func (s *Server) unaryServerInterceptor() grpc.UnaryServerInterceptor {
 		}
 		reply, err := h(ctx, req)
 		if len(replyHeader) > 0 {
-			grpc.SetHeader(ctx, replyHeader)
+			_ = grpc.SetHeader(ctx, replyHeader)
 		}
 		return reply, err
 	}
diff --git a/transport/http/codec.go b/transport/http/codec.go
index 58db62e1..7eb62872 100644
--- a/transport/http/codec.go
+++ b/transport/http/codec.go
@@ -45,7 +45,10 @@ func DefaultResponseEncoder(w http.ResponseWriter, r *http.Request, v interface{
 		return err
 	}
 	w.Header().Set("Content-Type", httputil.ContentType(codec.Name()))
-	w.Write(data)
+	_, err = w.Write(data)
+	if err != nil {
+		return err
+	}
 	return nil
 }
 
@@ -60,7 +63,7 @@ func DefaultErrorEncoder(w http.ResponseWriter, r *http.Request, err error) {
 	}
 	w.Header().Set("Content-Type", httputil.ContentType(codec.Name()))
 	w.WriteHeader(int(se.Code))
-	w.Write(body)
+	_, _ = w.Write(body)
 }
 
 // CodecForRequest get encoding.Codec via http.Request
diff --git a/transport/http/context.go b/transport/http/context.go
index 5e2079f8..0c68329c 100644
--- a/transport/http/context.go
+++ b/transport/http/context.go
@@ -120,14 +120,20 @@ func (c *wrapper) XML(code int, v interface{}) error {
 func (c *wrapper) String(code int, text string) error {
 	c.res.Header().Set("Content-Type", "text/plain")
 	c.res.WriteHeader(code)
-	c.res.Write([]byte(text))
+	_, err := c.res.Write([]byte(text))
+	if err != nil {
+		return err
+	}
 	return nil
 }
 
 func (c *wrapper) Blob(code int, contentType string, data []byte) error {
 	c.res.Header().Set("Content-Type", contentType)
 	c.res.WriteHeader(code)
-	c.res.Write(data)
+	_, err := c.res.Write(data)
+	if err != nil {
+		return err
+	}
 	return nil
 }
 
diff --git a/transport/http/resolver.go b/transport/http/resolver.go
index 1e553fa1..ebb42f72 100644
--- a/transport/http/resolver.go
+++ b/transport/http/resolver.go
@@ -87,12 +87,18 @@ func newResolver(ctx context.Context, discovery registry.Discovery, target *Targ
 		select {
 		case err := <-done:
 			if err != nil {
-				watcher.Stop()
+				err := watcher.Stop()
+				if err != nil {
+					r.logger.Errorf("failed to http client watch stop: %v", target)
+				}
 				return nil, err
 			}
 		case <-ctx.Done():
 			r.logger.Errorf("http client watch service %v reaching context deadline!", target)
-			watcher.Stop()
+			err := watcher.Stop()
+			if err != nil {
+				r.logger.Errorf("failed to http client watch stop: %v", target)
+			}
 			return nil, ctx.Err()
 		}
 	}
@@ -116,12 +122,12 @@ func newResolver(ctx context.Context, discovery registry.Discovery, target *Targ
 func (r *resolver) update(services []*registry.ServiceInstance) {
 	var nodes []*registry.ServiceInstance
 	for _, in := range services {
-		endpoint, err := endpoint.ParseEndpoint(in.Endpoints, "http", !r.insecure)
+		ept, err := endpoint.ParseEndpoint(in.Endpoints, "http", !r.insecure)
 		if err != nil {
 			r.logger.Errorf("Failed to parse (%v) discovery endpoint: %v error %v", r.target, in.Endpoints, err)
 			continue
 		}
-		if endpoint == "" {
+		if ept == "" {
 			continue
 		}
 		nodes = append(nodes, in)
diff --git a/transport/transport.go b/transport/transport.go
index 2c06efff..45f2f549 100644
--- a/transport/transport.go
+++ b/transport/transport.go
@@ -32,20 +32,22 @@ type Header interface {
 
 // Transporter is transport context value interface.
 type Transporter interface {
+	// Kind transporter
 	// grpc
 	// http
 	Kind() Kind
+	// Endpoint return server or client endpoint
 	// Server Transport: grpc://127.0.0.1:9000
 	// Client Transport: discovery:///provider-demo
 	Endpoint() string
-	// Service full method selector generated by protobuf
+	// Operation Service full method selector generated by protobuf
 	// example: /helloworld.Greeter/SayHello
 	Operation() string
-	// request header
+	// RequestHeader return transport request header
 	// http: http.Header
 	// grpc: metadata.MD
 	RequestHeader() Header
-	// reply header
+	// ReplyHeader return transport reply/response header
 	// only valid for server transport
 	// http: http.Header
 	// grpc: metadata.MD
diff --git a/version.go b/version.go
index 103678f1..0f7638cd 100644
--- a/version.go
+++ b/version.go
@@ -1,4 +1,4 @@
 package kratos
 
 // Release is the current kratos version.
-const Release = "v2.0.4"
+const Release = "v2.0.5"


Test patch:
diff --git a/config/env/watcher_test.go b/config/env/watcher_test.go
new file mode 100644
index 00000000..bea14947
--- /dev/null
+++ b/config/env/watcher_test.go
@@ -0,0 +1,29 @@
+package env
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func Test_watcher_next(t *testing.T) {
+	t.Run("next after stop should return err", func(t *testing.T) {
+		w, err := NewWatcher()
+		require.NoError(t, err)
+
+		_ = w.Stop()
+		_, err = w.Next()
+		assert.Error(t, err)
+	})
+}
+
+func Test_watcher_stop(t *testing.T) {
+	t.Run("stop multiple times should not panic", func(t *testing.T) {
+		w, err := NewWatcher()
+		require.NoError(t, err)
+
+		_ = w.Stop()
+		_ = w.Stop()
+	})
+}
diff --git a/options_test.go b/options_test.go
index 34bce67c..c7f34287 100644
--- a/options_test.go
+++ b/options_test.go
@@ -66,7 +66,7 @@ func TestLogger(t *testing.T) {
 	o := &options{}
 	v := xlog.NewStdLogger(log.Writer())
 	Logger(v)(o)
-	assert.Equal(t, v, o.logger)
+	assert.Equal(t, xlog.NewHelper(v), o.logger)
 }
 
 type mockServer struct{}