Repository: go-kratos/kratos
Pull request #606 (2020-07-09 06:55:22)

修复container/group在相同的key并发get时，可能会初始化多次的bug


Patch:
diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
index 73371ac3..9a2ebaa6 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/go.yml
@@ -9,24 +9,106 @@ on:
 jobs:
 
   build:
-    name: Build
-    runs-on: ubuntu-latest
+    name: Build on ${{ matrix.os }} - Go${{ matrix.go_version }}
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        go_version:
+          - 1.13
+        os:
+          - ubuntu-latest
+
     steps:
+  
+    - name: Set up Go ${{ matrix.go_version }}
+      uses: actions/setup-go@v1
+      with:
+        go-version: ${{ matrix.go_version }}
+      id: go
+
+    - name: Check out code into the Go module directory
+      uses: actions/checkout@v2
+
+    - name: Cache dependencies
+      uses: actions/cache@v2
+      with:
+        # Cache
+        path: ~/go/pkg/mod
+        # Cache key
+        key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
+        # An ordered list of keys to use for restoring the cache if no cache hit occurred for key
+        restore-keys: |
+          ${{ runner.os }}-go-
 
-    - name: Set up Go 1.13
+    - name: Get dependencies
+      run: |
+        go get -v -t -d ./...
+        if [ -f Gopkg.toml ]; then
+            curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
+            dep ensure
+        fi
+
+    - name: Build
+      run: go build ./...
+
+    - name: Test
+      run: go test ./... -coverprofile=coverage.txt -covermode=atomic
+    
+    - name: Coverage
+      run: bash <(curl -s https://codecov.io/bash)
+
+  scaffold:
+  
+    name: Scaffold Test on ${{ matrix.os }} - Go${{ matrix.go_version }}
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        go_version:
+          - 1.13
+        os:
+          - ubuntu-latest
+  
+    steps:
+
+    - name: Set up Go ${{ matrix.go_version }}
       uses: actions/setup-go@v1
       with:
-        go-version: 1.13
+        go-version: ${{ matrix.go_version }}
       id: go
 
+    - name: Check out code into the Go module directory
+      uses: actions/checkout@v2
+
+    - name: Cache dependencies
+      uses: actions/cache@v2
+      with:
+        # Cache
+        path: ~/go/pkg/mod
+        # Cache key
+        key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
+        # An ordered list of keys to use for restoring the cache if no cache hit occurred for key
+        restore-keys: |
+          ${{ runner.os }}-go-
+
     - name: Set up Env
       run: |
         echo "::set-env name=GOPATH::$(go env GOPATH)"
         echo "::add-path::$(go env GOPATH)/bin"
-
+  
     - name: Check out code into the Go module directory
       uses: actions/checkout@v2
 
+    - name: Cache dependencies
+      uses: actions/cache@v2
+      with:
+        # Cache
+        path: ~/go/pkg/mod
+        # Cache key
+        key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
+        # An ordered list of keys to use for restoring the cache if no cache hit occurred for key
+        restore-keys: |
+          ${{ runner.os }}-go-
+
     - name: Get dependencies
       run: |
         go get -v -t -d ./...
@@ -34,10 +116,6 @@ jobs:
             curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
             dep ensure
         fi
-        sudo rm /usr/local/bin/docker-compose
-        curl -L https://github.com/docker/compose/releases/download/1.25.5/docker-compose-`uname -s`-`uname -m` > docker-compose
-        chmod +x docker-compose
-        sudo mv docker-compose /usr/local/bin
         wget https://github.com/google/protobuf/releases/download/v3.11.4/protoc-3.11.4-linux-x86_64.zip
         unzip protoc-3.11.4-linux-x86_64.zip
         chmod +x bin/protoc
@@ -46,10 +124,6 @@ jobs:
         go get -u github.com/golang/protobuf/protoc-gen-go
         go get -u github.com/gogo/protobuf/protoc-gen-gofast
 
-    - name: Build
-      run: go build ./...
-    - name: Test
-      run: go test ./...
     - name: Tool
       run: |
         go install ./...
diff --git a/README.md b/README.md
index 73184b86..ba62eb99 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,10 @@
-![kratos](doc/img/kratos3.png)
+![kratos](docs/img/kratos3.png)
 
 [![Language](https://img.shields.io/badge/Language-Go-blue.svg)](https://golang.org/)
 [![Build Status](https://github.com/go-kratos/kratos/workflows/Go/badge.svg)](https://github.com/go-kratos/kratos/actions)
 [![GoDoc](https://godoc.org/github.com/go-kratos/kratos?status.svg)](https://godoc.org/github.com/go-kratos/kratos)
 [![Go Report Card](https://goreportcard.com/badge/github.com/go-kratos/kratos)](https://goreportcard.com/report/github.com/go-kratos/kratos)
+[![Discord](https://img.shields.io/discord/766619759214854164?label=chat&logo=discord)](https://discord.gg/BWzJsUJ)
 
 # Kratos
 
@@ -20,7 +21,7 @@ Kratos是[bilibili](https://www.bilibili.com)开源的一套Go微服务框架，
 * GRPC Warden：基于官方gRPC开发，集成[discovery](https://github.com/bilibili/discovery)服务发现，并融合P2C负载均衡；
 * Cache：优雅的接口化设计，非常方便的缓存序列化，推荐结合代理模式[overlord](https://github.com/bilibili/overlord)；
 * Database：集成MySQL/HBase/TiDB，添加熔断保护和统计支持，可快速发现数据层压力；
-* Config：方便易用的[paladin sdk](doc/wiki-cn/config.md)，可配合远程配置中心，实现配置版本管理和更新；
+* Config：方便易用的[paladin sdk](https://go-kratos.github.io/kratos/#/config)，可配合远程配置中心，实现配置版本管理和更新；
 * Log：类似[zap](https://github.com/uber-go/zap)的field实现高性能日志库，并结合log-agent实现远程日志管理；
 * Trace：基于opentracing，集成了全链路trace支持（gRPC/HTTP/MySQL/Redis/Memcached）；
 * Kratos Tool：工具链，可快速生成标准项目，或者通过Protobuf生成代码，非常便捷使用gRPC、HTTP、swagger文档；
@@ -33,12 +34,20 @@ Go version>=1.13
 
 ### Installation
 ```shell
+# Linux/macOS
 GO111MODULE=on && go get -u github.com/go-kratos/kratos/tool/kratos
+
+# Windows (Powershell)
+go env -w GO111MODULE=on ; go get -u github.com/go-kratos/kratos/tool/kratos
+
+# Windows (CMD)
+go env -w GO111MODULE=on && go get -u github.com/go-kratos/kratos/tool/kratos
+
 cd $GOPATH/src
 kratos new kratos-demo
 ```
 
-通过 `kratos new` 会快速生成基于kratos库的脚手架代码，如生成 [kratos-demo](https://github.com/go-kratos/kratos-demo) 
+通过 `kratos new` 会快速生成基于kratos库的脚手架代码，如生成 [kratos-demo](https://github.com/bilibili/kratos-demo) 
 
 ### Build & Run
 
@@ -50,16 +59,18 @@ go build
 
 打开浏览器访问：[http://localhost:8000/kratos-demo/start](http://localhost:8000/kratos-demo/start)，你会看到输出了`Golang 大法好 ！！！`
 
-[快速开始](doc/wiki-cn/quickstart.md)  [kratos工具](doc/wiki-cn/kratos-tool.md)
+[快速开始](https://go-kratos.github.io/kratos/#/quickstart)  [kratos工具](https://go-kratos.github.io/kratos/#/kratos-tool)
 
 ## Documentation
 
-> [简体中文](doc/wiki-cn/summary.md)  
-> [FAQ](doc/wiki-cn/FAQ.md)  
+> [简体中文](https://go-kratos.github.io/kratos)  
+> [简体中文（国内镜像）](https://go-kratos.gitee.io/kratos/)  
+> [FAQ](https://go-kratos.github.io/kratos/#/FAQ)  
 
 ## License
 Kratos is under the MIT license. See the [LICENSE](./LICENSE) file for details.
 
 -------------
 
-*Please report bugs, concerns, suggestions by issues, or join QQ-group 716486124 to discuss problems around source code.*
+*Please report bugs, concerns, suggestions by issues, on [Discord Group](https://discord.gg/BWzJsUJ)
+ or join QQ-group 716486124 to discuss problems around source code.*
diff --git a/doc/wiki-cn/database-mysql-orm.md b/doc/wiki-cn/database-mysql-orm.md
deleted file mode 100644
index 1e16fc77..00000000
--- a/doc/wiki-cn/database-mysql-orm.md
+++ /dev/null
@@ -1,95 +0,0 @@
-# 准备工作
-
-推荐使用[kratos工具](kratos-tool.md)快速生成项目，如我们生成一个叫`kratos-demo`的项目。目录结构如下：
-
-```
-├── CHANGELOG.md
-├── OWNERS
-├── README.md
-├── api
-│   ├── api.bm.go
-│   ├── api.pb.go
-│   ├── api.proto
-│   └── client.go
-├── cmd
-│   ├── cmd
-│   └── main.go
-├── configs
-│   ├── application.toml
-│   ├── db.toml
-│   ├── grpc.toml
-│   ├── http.toml
-│   ├── memcache.toml
-│   └── redis.toml
-├── go.mod
-├── go.sum
-├── internal
-│   ├── dao
-│   │   ├── dao.bts.go
-│   │   ├── dao.go
-│   │   ├── db.go
-│   │   ├── mc.cache.go
-│   │   ├── mc.go
-│   │   └── redis.go
-│   ├── di
-│   │   ├── app.go
-│   │   ├── wire.go
-│   │   └── wire_gen.go
-│   ├── model
-│   │   └── model.go
-│   ├── server
-│   │   ├── grpc
-│   │   │   └── server.go
-│   │   └── http
-│   │       └── server.go
-│   └── service
-│       └── service.go
-└── test
-    └── docker-compose.yaml
-```
-
-# 开始使用
-
-## 配置
-
-创建项目成功后，进入项目中的configs目录，mysql.toml，我们可以看到：
-
-```toml
-[demo]
-	addr = "127.0.0.1:3306"
-	dsn = "{user}:{password}@tcp(127.0.0.1:3306)/{database}?timeout=1s&readTimeout=1s&writeTimeout=1s&parseTime=true&loc=Local&charset=utf8mb4,utf8"
-	readDSN = ["{user}:{password}@tcp(127.0.0.2:3306)/{database}?timeout=1s&readTimeout=1s&writeTimeout=1s&parseTime=true&loc=Local&charset=utf8mb4,utf8","{user}:{password}@tcp(127.0.0.3:3306)/{database}?timeout=1s&readTimeout=1s&writeTimeout=1s&parseTime=true&loc=Local&charset=utf8,utf8mb4"]
-	active = 20
-	idle = 10
-	idleTimeout ="4h"
-	queryTimeout = "200ms"
-	execTimeout = "300ms"
-	tranTimeout = "400ms"
-```
-
-在该配置文件中我们可以配置mysql的读和写的dsn、连接地址addr、连接池的闲置连接数idle、最大连接数active以及各类超时。
-
-如果配置了readDSN，在进行读操作的时候会优先使用readDSN的连接。
-
-## 初始化
-
-进入项目的internal/dao目录，打开db.go，其中：
-
-```go
-var cfg struct {
-    Client *sql.Config
-}
-checkErr(paladin.Get("db.toml").UnmarshalTOML(&dc))
-```
-使用paladin配置管理工具将上文中的db.toml中的配置解析为我们需要使用db的相关配置。
-
-# TODO：补充常用方法
-
-# 扩展阅读
-
-[tidb模块说明](database-tidb.md)
-[hbase模块说明](database-hbase.md)
-
--------------
-
-[文档目录树](summary.md)
diff --git a/doc/wiki-cn/database-tidb.md b/docs/.nojekyll
similarity index 100%
rename from doc/wiki-cn/database-tidb.md
rename to docs/.nojekyll
diff --git a/doc/wiki-cn/FAQ.md b/docs/FAQ.md
similarity index 100%
rename from doc/wiki-cn/FAQ.md
rename to docs/FAQ.md
diff --git a/doc/wiki-cn/README.md b/docs/README.md
similarity index 96%
rename from doc/wiki-cn/README.md
rename to docs/README.md
index 45e7e4a4..13c1d0a7 100644
--- a/doc/wiki-cn/README.md
+++ b/docs/README.md
@@ -1,40 +1,36 @@
-# Kratos
-
-Kratos是bilibili开源的一套Go微服务框架，包含大量微服务相关框架及工具。  
-
-### Goals
-
-我们致力于提供完整的微服务研发体验，整合相关框架及工具后，微服务治理相关部分可对整体业务开发周期无感，从而更加聚焦于业务交付。对每位开发者而言，整套Kratos框架也是不错的学习仓库，可以了解和参考到bilibili在微服务方面的技术积累和经验。
-
-### Principles
-
-* 简单：不过度设计，代码平实简单
-* 通用：通用业务开发所需要的基础库的功能
-* 高效：提高业务迭代的效率
-* 稳定：基础库可测试性高，覆盖率高，有线上实践安全可靠
-* 健壮：通过良好的基础库设计，减少错用
-* 高性能：性能高，但不特定为了性能做hack优化，引入unsafe
-* 扩展性：良好的接口设计，来扩展实现，或者通过新增基础库目录来扩展功能
-* 容错性：为失败设计，大量引入对SRE的理解，鲁棒性高
-* 工具链：包含大量工具链，比如cache代码生成，lint工具等等
-
-### Features
-* HTTP Blademaster：核心基于[gin](https://github.com/gin-gonic/gin)进行模块化设计，简单易用、核心足够轻量；
-* GRPC Warden：基于官方gRPC开发，集成[discovery](https://github.com/bilibili/discovery)服务发现，并融合P2C负载均衡；
-* Cache：优雅的接口化设计，非常方便的缓存序列化，推荐结合代理模式[overlord](https://github.com/bilibili/overlord)；
-* Database：集成MySQL/HBase/TiDB，添加熔断保护和统计支持，可快速发现数据层压力；
-* Config：方便易用的[paladin sdk](config-paladin.md)，可配合远程配置中心，实现配置版本管理和更新；
-* Log：类似[zap](https://github.com/uber-go/zap)的field实现高性能日志库，并结合log-agent实现远程日志管理；
-* Trace：基于opentracing，集成了全链路trace支持（gRPC/HTTP/MySQL/Redis/Memcached）；
-* Kratos Tool：工具链，可快速生成标准项目，或者通过Protobuf生成代码，非常便捷使用gRPC、HTTP、swagger文档；
-
-
-<br>  
-
--------------
-
-> 名字来源于:《战神》游戏以希腊神话为背景，讲述由凡人成为战神的奎托斯（Kratos）成为战神并展开弑神屠杀的冒险历程。
-
--------------
-
-[文档目录树](summary.md)
+![kratos](img/kratos3.png)
+# Kratos
+
+Kratos是bilibili开源的一套Go微服务框架，包含大量微服务相关框架及工具。  
+
+### Goals
+
+我们致力于提供完整的微服务研发体验，整合相关框架及工具后，微服务治理相关部分可对整体业务开发周期无感，从而更加聚焦于业务交付。对每位开发者而言，整套Kratos框架也是不错的学习仓库，可以了解和参考到bilibili在微服务方面的技术积累和经验。
+
+### Principles
+
+* 简单：不过度设计，代码平实简单
+* 通用：通用业务开发所需要的基础库的功能
+* 高效：提高业务迭代的效率
+* 稳定：基础库可测试性高，覆盖率高，有线上实践安全可靠
+* 健壮：通过良好的基础库设计，减少错用
+* 高性能：性能高，但不特定为了性能做hack优化，引入unsafe
+* 扩展性：良好的接口设计，来扩展实现，或者通过新增基础库目录来扩展功能
+* 容错性：为失败设计，大量引入对SRE的理解，鲁棒性高
+* 工具链：包含大量工具链，比如cache代码生成，lint工具等等
+
+### Features
+* HTTP Blademaster：核心基于[gin](https://github.com/gin-gonic/gin)进行模块化设计，简单易用、核心足够轻量；
+* GRPC Warden：基于官方gRPC开发，集成[discovery](https://github.com/bilibili/discovery)服务发现，并融合P2C负载均衡；
+* Cache：优雅的接口化设计，非常方便的缓存序列化，推荐结合代理模式[overlord](https://github.com/bilibili/overlord)；
+* Database：集成MySQL/HBase/TiDB，添加熔断保护和统计支持，可快速发现数据层压力；
+* Config：方便易用的[paladin sdk](config-paladin.md)，可配合远程配置中心，实现配置版本管理和更新；
+* Log：类似[zap](https://github.com/uber-go/zap)的field实现高性能日志库，并结合log-agent实现远程日志管理；
+* Trace：基于opentracing，集成了全链路trace支持（gRPC/HTTP/MySQL/Redis/Memcached）；
+* Kratos Tool：工具链，可快速生成标准项目，或者通过Protobuf生成代码，非常便捷使用gRPC、HTTP、swagger文档；
+
+
+-------------
+
+> 名字来源于:《战神》游戏以希腊神话为背景，讲述由凡人成为战神的奎托斯（Kratos）成为战神并展开弑神屠杀的冒险历程。
+
diff --git a/doc/wiki-cn/summary.md b/docs/_sidebar.md
similarity index 88%
rename from doc/wiki-cn/summary.md
rename to docs/_sidebar.md
index 3986cd8c..84477b09 100644
--- a/doc/wiki-cn/summary.md
+++ b/docs/_sidebar.md
@@ -1,8 +1,6 @@
-# Summary
-
 * [介绍](README.md)
-  * [快速开始](quickstart.md)
-  * [案例](https://github.com/go-kratos/kratos-demo)
+  * [快速开始 - 项目初始化](quickstart.md)
+* [FAQ](FAQ.md)
 * [http blademaster](blademaster.md)
   * [bm quickstart](blademaster-quickstart.md) 
   * [bm module](blademaster-mod.md) 
diff --git a/doc/wiki-cn/blademaster-mid.md b/docs/blademaster-mid.md
similarity index 98%
rename from doc/wiki-cn/blademaster-mid.md
rename to docs/blademaster-mid.md
index 204b6b87..e379b97d 100644
--- a/doc/wiki-cn/blademaster-mid.md
+++ b/docs/blademaster-mid.md
@@ -158,7 +158,7 @@ e.POST("/api", cors, myHandler)
 
 ## 自适应限流
 
-更多关于自适应限流的信息可参考：[kratos 自适应限流](/doc/wiki-cn/ratelimit.md)。如要使用如下：
+更多关于自适应限流的信息可参考：[kratos 自适应限流](ratelimit.md)。如要使用如下：
 
 ```go
 e := bm.DefaultServer(nil)
@@ -175,6 +175,3 @@ e.GET("/api", csrf, myHandler)
 [bm模块说明](blademaster-mod.md)  
 [bm基于pb生成](blademaster-pb.md)  
 
--------------
-
-[文档目录树](summary.md)
diff --git a/doc/wiki-cn/blademaster-mod.md b/docs/blademaster-mod.md
similarity index 97%
rename from doc/wiki-cn/blademaster-mod.md
rename to docs/blademaster-mod.md
index e72f1eb5..e8c81d70 100644
--- a/doc/wiki-cn/blademaster-mod.md
+++ b/docs/blademaster-mod.md
@@ -72,7 +72,7 @@ func (c *Context) Protobuf(data proto.Message, err error)
 
 # Handler
 
-![handler](/doc/img/bm-handlers.png)
+![handler](img/bm-handlers.png)
 
 初次接触`blademaster`的用户可能会对其`Handler`的流程处理产生不小的疑惑，实际上`bm`对`Handler`对处理非常简单：
 
@@ -86,6 +86,3 @@ func (c *Context) Protobuf(data proto.Message, err error)
 [bm中间件](blademaster-mid.md)  
 [bm基于pb生成](blademaster-pb.md)  
 
--------------
-
-[文档目录树](summary.md)
diff --git a/doc/wiki-cn/blademaster-pb.md b/docs/blademaster-pb.md
similarity index 98%
rename from doc/wiki-cn/blademaster-pb.md
rename to docs/blademaster-pb.md
index bf368b84..0b693af1 100644
--- a/doc/wiki-cn/blademaster-pb.md
+++ b/docs/blademaster-pb.md
@@ -81,6 +81,3 @@ kratos tool swagger serve api/api.swagger.json
 [bm模块说明](blademaster-mod.md)  
 [bm中间件](blademaster-mid.md)  
 
--------------
-
-[文档目录树](summary.md)
diff --git a/doc/wiki-cn/blademaster-quickstart.md b/docs/blademaster-quickstart.md
similarity index 71%
rename from doc/wiki-cn/blademaster-quickstart.md
rename to docs/blademaster-quickstart.md
index 7e842f96..d49064b5 100644
--- a/doc/wiki-cn/blademaster-quickstart.md
+++ b/docs/blademaster-quickstart.md
@@ -1,56 +1,6 @@
-# 准备工作
-
-推荐使用[kratos工具](kratos-tool.md)快速生成项目，如我们生成一个叫`kratos-demo`的项目。目录结构如下：
-
-```
-├── CHANGELOG.md
-├── OWNERS
-├── README.md
-├── api
-│   ├── api.bm.go
-│   ├── api.pb.go
-│   ├── api.proto
-│   └── client.go
-├── cmd
-│   ├── cmd
-│   └── main.go
-├── configs
-│   ├── application.toml
-│   ├── db.toml
-│   ├── grpc.toml
-│   ├── http.toml
-│   ├── memcache.toml
-│   └── redis.toml
-├── go.mod
-├── go.sum
-├── internal
-│   ├── dao
-│   │   ├── dao.bts.go
-│   │   ├── dao.go
-│   │   ├── db.go
-│   │   ├── mc.cache.go
-│   │   ├── mc.go
-│   │   └── redis.go
-│   ├── di
-│   │   ├── app.go
-│   │   ├── wire.go
-│   │   └── wire_gen.go
-│   ├── model
-│   │   └── model.go
-│   ├── server
-│   │   ├── grpc
-│   │   │   └── server.go
-│   │   └── http
-│   │       └── server.go
-│   └── service
-│       └── service.go
-└── test
-    └── docker-compose.yaml
-```
-
 # 路由
 
-创建项目成功后，进入`internal/server/http`目录下，打开`http.go`文件，其中有默认生成的`blademaster`模板。其中：
+进入`internal/server/http`目录下，打开`http.go`文件，其中有默认生成的`blademaster`模板。其中：
 
 ```go
 engine = bm.DefaultServer(hc.Server)
@@ -190,6 +140,3 @@ go tool pprof http://127.0.0.1:8000/debug/pprof/profile
 [bm中间件](blademaster-mid.md)  
 [bm基于pb生成](blademaster-pb.md)  
 
--------------
-
-[文档目录树](summary.md)
diff --git a/doc/wiki-cn/blademaster.md b/docs/blademaster.md
similarity index 96%
rename from doc/wiki-cn/blademaster.md
rename to docs/blademaster.md
index c656045e..1915c3ed 100644
--- a/doc/wiki-cn/blademaster.md
+++ b/docs/blademaster.md
@@ -21,12 +21,12 @@
 
 # blademaster架构
 
-![bm-arch](/doc/img/bm-arch-2-2.png)
+![bm-arch](img/bm-arch-2-2.png)
 
 `blademaster`由几个非常精简的内部模块组成。其中`Router`用于根据请求的路径分发请求，`Context`包含了一个完整的请求信息，`Handler`则负责处理传入的`Context`，`Handlers`为一个列表，一个串一个地执行。  
 所有的`middlerware`均以`Handler`的形式存在，这样可以保证`blademaster`自身足够精简且扩展性足够强。
 
-![bm-arch](/doc/img/bm-arch-2-3.png)
+![bm-arch](img/bm-arch-2-3.png)
 
 `blademaster`处理请求的模式非常简单，大部分的逻辑都被封装在了各种`Handler`中。一般而言，业务逻辑作为最后一个`Handler`。
 
@@ -41,6 +41,3 @@
 [bm中间件](blademaster-mid.md)  
 [bm基于pb生成](blademaster-pb.md)  
 
--------------
-
-[文档目录树](summary.md)
diff --git a/doc/wiki-cn/breaker.md b/docs/breaker.md
similarity index 94%
rename from doc/wiki-cn/breaker.md
rename to docs/breaker.md
index cef56fbc..2a4ff474 100644
--- a/doc/wiki-cn/breaker.md
+++ b/docs/breaker.md
@@ -14,7 +14,7 @@
  //初始化熔断器组
  //一组熔断器公用同一个配置项，可从分组内取出单个熔断器使用。可用在比如mysql主从分离等场景。
  brkGroup := breaker.NewGroup(&breaker.Config{}) 
- //为每一个连接指定一个brekaker
+ //为每一个连接指定一个breaker
  //此处假设一个客户端连接对象实例为conn
  //breakName定义熔断器名称 一般可以使用连接地址
  breakName = conn.Addr
@@ -25,7 +25,7 @@
 		return
   }
  
- //连接执行成功或失败将结果告知braker
+ //连接执行成功或失败将结果告知breaker
  if(respErr != nil){
       conn.breaker.MarkFailed()
  }else{
diff --git a/doc/wiki-cn/cache-mc.md b/docs/cache-mc.md
similarity index 78%
rename from doc/wiki-cn/cache-mc.md
rename to docs/cache-mc.md
index 0ef73af2..04f60f96 100644
--- a/doc/wiki-cn/cache-mc.md
+++ b/docs/cache-mc.md
@@ -1,58 +1,8 @@
-# 准备工作
-
-推荐使用[kratos工具](kratos-tool.md)快速生成项目，如我们生成一个叫`kratos-demo`的项目。目录结构如下：
-
-```
-├── CHANGELOG.md
-├── OWNERS
-├── README.md
-├── api
-│   ├── api.bm.go
-│   ├── api.pb.go
-│   ├── api.proto
-│   └── client.go
-├── cmd
-│   ├── cmd
-│   └── main.go
-├── configs
-│   ├── application.toml
-│   ├── db.toml
-│   ├── grpc.toml
-│   ├── http.toml
-│   ├── memcache.toml
-│   └── redis.toml
-├── go.mod
-├── go.sum
-├── internal
-│   ├── dao
-│   │   ├── dao.bts.go
-│   │   ├── dao.go
-│   │   ├── db.go
-│   │   ├── mc.cache.go
-│   │   ├── mc.go
-│   │   └── redis.go
-│   ├── di
-│   │   ├── app.go
-│   │   ├── wire.go
-│   │   └── wire_gen.go
-│   ├── model
-│   │   └── model.go
-│   ├── server
-│   │   ├── grpc
-│   │   │   └── server.go
-│   │   └── http
-│   │       └── server.go
-│   └── service
-│       └── service.go
-└── test
-    └── docker-compose.yaml
-```
-
 # 开始使用
 
 ## 配置
 
-创建项目成功后，进入项目中的configs目录，打开memcache.toml，我们可以看到：
+进入项目中的configs目录，打开memcache.toml，我们可以看到：
 
 ```toml
 [Client]
@@ -243,6 +193,3 @@ func (d *Dao) DelCacheDemo(c context.Context, id int64) (err error) {
 [memcache代码生成器](kratos-genmc.md)  
 [redis模块说明](cache-redis.md)  
 
--------------
-
-[文档目录树](summary.md)
diff --git a/doc/wiki-cn/cache-redis.md b/docs/cache-redis.md
similarity index 76%
rename from doc/wiki-cn/cache-redis.md
rename to docs/cache-redis.md
index 34ce9685..898e419d 100644
--- a/doc/wiki-cn/cache-redis.md
+++ b/docs/cache-redis.md
@@ -1,58 +1,8 @@
-# 准备工作
-
-推荐使用[kratos工具](kratos-tool.md)快速生成项目，如我们生成一个叫`kratos-demo`的项目。目录结构如下：
-
-```
-├── CHANGELOG.md
-├── OWNERS
-├── README.md
-├── api
-│   ├── api.bm.go
-│   ├── api.pb.go
-│   ├── api.proto
-│   └── client.go
-├── cmd
-│   ├── cmd
-│   └── main.go
-├── configs
-│   ├── application.toml
-│   ├── db.toml
-│   ├── grpc.toml
-│   ├── http.toml
-│   ├── memcache.toml
-│   └── redis.toml
-├── go.mod
-├── go.sum
-├── internal
-│   ├── dao
-│   │   ├── dao.bts.go
-│   │   ├── dao.go
-│   │   ├── db.go
-│   │   ├── mc.cache.go
-│   │   ├── mc.go
-│   │   └── redis.go
-│   ├── di
-│   │   ├── app.go
-│   │   ├── wire.go
-│   │   └── wire_gen.go
-│   ├── model
-│   │   └── model.go
-│   ├── server
-│   │   ├── grpc
-│   │   │   └── server.go
-│   │   └── http
-│   │       └── server.go
-│   └── service
-│       └── service.go
-└── test
-    └── docker-compose.yaml
-```
-
 # 开始使用
 
 ## 配置
 
-创建项目成功后，进入项目中的configs目录，打开redis.toml，我们可以看到：
+进入项目中的configs目录，打开redis.toml，我们可以看到：
 
 ```toml
 [Client]
@@ -229,6 +179,3 @@ func (d *Dao) HGETALLDemo(c context.Context, pid int64) (res map[string]int64, e
 
 [memcache模块说明](cache-mc.md)  
 
--------------
-
-[文档目录树](summary.md)
diff --git a/doc/wiki-cn/cache.md b/docs/cache.md
similarity index 91%
rename from doc/wiki-cn/cache.md
rename to docs/cache.md
index 41f87d18..a1e48403 100644
--- a/doc/wiki-cn/cache.md
+++ b/docs/cache.md
@@ -18,6 +18,3 @@
 
 [redis模块说明](cache-redis.md)
 
--------------
-
-[文档目录树](summary.md)
diff --git a/doc/wiki-cn/config-paladin.md b/docs/config-paladin.md
similarity index 100%
rename from doc/wiki-cn/config-paladin.md
rename to docs/config-paladin.md
diff --git a/doc/wiki-cn/config.md b/docs/config.md
similarity index 98%
rename from doc/wiki-cn/config.md
rename to docs/config.md
index b218b87b..f27244ab 100644
--- a/doc/wiki-cn/config.md
+++ b/docs/config.md
@@ -46,6 +46,3 @@ Redis、MySQL等业务组件，可以使用静态的配置文件来初始化，
 
 [paladin配置sdk](config-paladin.md)  
 
--------------
-
-[文档目录树](summary.md)
diff --git a/doc/wiki-cn/database-hbase.md b/docs/database-hbase.md
similarity index 95%
rename from doc/wiki-cn/database-hbase.md
rename to docs/database-hbase.md
index 854542b2..327092b7 100644
--- a/doc/wiki-cn/database-hbase.md
+++ b/docs/database-hbase.md
@@ -49,6 +49,3 @@ func main() {
 }
 ```
 
--------------
-
-[文档目录树](summary.md)
diff --git a/docs/database-mysql-orm.md b/docs/database-mysql-orm.md
new file mode 100644
index 00000000..e29d5b1b
--- /dev/null
+++ b/docs/database-mysql-orm.md
@@ -0,0 +1,42 @@
+# 开始使用
+
+## 配置
+
+进入项目中的configs目录，mysql.toml，我们可以看到：
+
+```toml
+[demo]
+	addr = "127.0.0.1:3306"
+	dsn = "{user}:{password}@tcp(127.0.0.1:3306)/{database}?timeout=1s&readTimeout=1s&writeTimeout=1s&parseTime=true&loc=Local&charset=utf8mb4,utf8"
+	readDSN = ["{user}:{password}@tcp(127.0.0.2:3306)/{database}?timeout=1s&readTimeout=1s&writeTimeout=1s&parseTime=true&loc=Local&charset=utf8mb4,utf8","{user}:{password}@tcp(127.0.0.3:3306)/{database}?timeout=1s&readTimeout=1s&writeTimeout=1s&parseTime=true&loc=Local&charset=utf8,utf8mb4"]
+	active = 20
+	idle = 10
+	idleTimeout ="4h"
+	queryTimeout = "200ms"
+	execTimeout = "300ms"
+	tranTimeout = "400ms"
+```
+
+在该配置文件中我们可以配置mysql的读和写的dsn、连接地址addr、连接池的闲置连接数idle、最大连接数active以及各类超时。
+
+如果配置了readDSN，在进行读操作的时候会优先使用readDSN的连接。
+
+## 初始化
+
+进入项目的internal/dao目录，打开db.go，其中：
+
+```go
+var cfg struct {
+    Client *sql.Config
+}
+checkErr(paladin.Get("db.toml").UnmarshalTOML(&dc))
+```
+使用paladin配置管理工具将上文中的db.toml中的配置解析为我们需要使用db的相关配置。
+
+# TODO：补充常用方法
+
+# 扩展阅读
+
+[tidb模块说明](database-tidb.md)
+[hbase模块说明](database-hbase.md)
+
diff --git a/doc/wiki-cn/database-mysql.md b/docs/database-mysql.md
similarity index 76%
rename from doc/wiki-cn/database-mysql.md
rename to docs/database-mysql.md
index 2eab493b..b907c9d6 100644
--- a/doc/wiki-cn/database-mysql.md
+++ b/docs/database-mysql.md
@@ -1,58 +1,8 @@
-# 准备工作
-
-推荐使用[kratos工具](kratos-tool.md)快速生成项目，如我们生成一个叫`kratos-demo`的项目。目录结构如下：
-
-```
-├── CHANGELOG.md
-├── OWNERS
-├── README.md
-├── api
-│   ├── api.bm.go
-│   ├── api.pb.go
-│   ├── api.proto
-│   └── client.go
-├── cmd
-│   ├── cmd
-│   └── main.go
-├── configs
-│   ├── application.toml
-│   ├── db.toml
-│   ├── grpc.toml
-│   ├── http.toml
-│   ├── memcache.toml
-│   └── redis.toml
-├── go.mod
-├── go.sum
-├── internal
-│   ├── dao
-│   │   ├── dao.bts.go
-│   │   ├── dao.go
-│   │   ├── db.go
-│   │   ├── mc.cache.go
-│   │   ├── mc.go
-│   │   └── redis.go
-│   ├── di
-│   │   ├── app.go
-│   │   ├── wire.go
-│   │   └── wire_gen.go
-│   ├── model
-│   │   └── model.go
-│   ├── server
-│   │   ├── grpc
-│   │   │   └── server.go
-│   │   └── http
-│   │       └── server.go
-│   └── service
-│       └── service.go
-└── test
-    └── docker-compose.yaml
-```
-
 # 开始使用
 
 ## 配置
 
-创建项目成功后，进入项目中的configs目录，mysql.toml，我们可以看到：
+进入项目中的configs目录，mysql.toml，我们可以看到：
 
 ```toml
 [demo]
@@ -243,6 +193,3 @@ if err = tx.Rollback().Error; err!=nil{
 [tidb模块说明](database-tidb.md)
 [hbase模块说明](database-hbase.md)
 
--------------
-
-[文档目录树](summary.md)
diff --git a/doc/wiki-cn/log-agent.md b/docs/database-tidb.md
similarity index 100%
rename from doc/wiki-cn/log-agent.md
rename to docs/database-tidb.md
diff --git a/doc/wiki-cn/database.md b/docs/database.md
similarity index 93%
rename from doc/wiki-cn/database.md
rename to docs/database.md
index 61d9f81d..f8cb3ebf 100644
--- a/doc/wiki-cn/database.md
+++ b/docs/database.md
@@ -17,6 +17,3 @@ HBase客户端，支持trace、slowlog和统计功能。
 TiDB客户端，支持服务发现和熔断功能。  
 [tidb client](database-tidb.md)
 
--------------
-
-[文档目录树](summary.md)
diff --git a/doc/wiki-cn/ecode.md b/docs/ecode.md
similarity index 100%
rename from doc/wiki-cn/ecode.md
rename to docs/ecode.md
diff --git a/doc/img/bm-arch-2-2.png b/docs/img/bm-arch-2-2.png
similarity index 100%
rename from doc/img/bm-arch-2-2.png
rename to docs/img/bm-arch-2-2.png
diff --git a/doc/img/bm-arch-2-3.png b/docs/img/bm-arch-2-3.png
similarity index 100%
rename from doc/img/bm-arch-2-3.png
rename to docs/img/bm-arch-2-3.png
diff --git a/doc/img/bm-handlers.png b/docs/img/bm-handlers.png
similarity index 100%
rename from doc/img/bm-handlers.png
rename to docs/img/bm-handlers.png
diff --git a/doc/img/kratos-log.jpg b/docs/img/kratos-log.jpg
similarity index 100%
rename from doc/img/kratos-log.jpg
rename to docs/img/kratos-log.jpg
diff --git a/doc/img/kratos.png b/docs/img/kratos.png
similarity index 100%
rename from doc/img/kratos.png
rename to docs/img/kratos.png
diff --git a/doc/img/kratos2.png b/docs/img/kratos2.png
similarity index 100%
rename from doc/img/kratos2.png
rename to docs/img/kratos2.png
diff --git a/doc/img/kratos3.png b/docs/img/kratos3.png
similarity index 100%
rename from doc/img/kratos3.png
rename to docs/img/kratos3.png
diff --git a/doc/img/ratelimit-benchmark-up-1.png b/docs/img/ratelimit-benchmark-up-1.png
similarity index 100%
rename from doc/img/ratelimit-benchmark-up-1.png
rename to docs/img/ratelimit-benchmark-up-1.png
diff --git a/doc/img/ratelimit-rolling-window.png b/docs/img/ratelimit-rolling-window.png
similarity index 100%
rename from doc/img/ratelimit-rolling-window.png
rename to docs/img/ratelimit-rolling-window.png
diff --git a/doc/img/zipkin.jpg b/docs/img/zipkin.jpg
similarity index 100%
rename from doc/img/zipkin.jpg
rename to docs/img/zipkin.jpg
diff --git a/docs/index.html b/docs/index.html
new file mode 100644
index 00000000..94fa7b46
--- /dev/null
+++ b/docs/index.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <title>Kratos Documentation</title>
+  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
+  <meta name="description" content="Description">
+  <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
+  <link rel="stylesheet" href="//cdn.staticfile.org/docsify/4.11.6/themes/vue.min.css">
+</head>
+<body>
+  <div id="app"></div>
+  <script src="//cdn.staticfile.org/docsify/4.11.6/docsify.min.js"></script>
+  <script src="//cdn.staticfile.org/docsify/4.11.6/plugins/search.min.js"></script>
+  <script src="//cdn.jsdelivr.net/npm/docsify-edit-on-github"></script>
+  <script>
+    window.$docsify = {
+      loadSidebar: true,
+      auto2top: true,
+      autoHeader: true,
+      name: 'go-kratos/kratos',
+      repo: 'https://github.com/go-kratos/kratos',
+      search: 'auto',
+      plugins: [
+        EditOnGithubPlugin.create('https://github.com/go-kratos/kratos/blob/master/docs/')
+      ]
+    }
+  </script>
+</body>
+</html>
diff --git a/doc/wiki-cn/install.md b/docs/install.md
similarity index 96%
rename from doc/wiki-cn/install.md
rename to docs/install.md
index 9c9e8f87..4ff3e205 100644
--- a/doc/wiki-cn/install.md
+++ b/docs/install.md
@@ -64,6 +64,3 @@ go build
 
 [kratos工具](kratos-tool.md)
 
--------------
-
-[文档目录树](summary.md)
diff --git a/doc/wiki-cn/kratos-genbts.md b/docs/kratos-genbts.md
similarity index 95%
rename from doc/wiki-cn/kratos-genbts.md
rename to docs/kratos-genbts.md
index 7c889ddc..ca3e7443 100644
--- a/doc/wiki-cn/kratos-genbts.md
+++ b/docs/kratos-genbts.md
@@ -25,6 +25,3 @@ type _bts interface {
 
 也可以参考完整的testdata例子：kratos/tool/kratos-gen-bts/testdata
 
--------------
-
-[文档目录树](summary.md)
diff --git a/doc/wiki-cn/kratos-genmc.md b/docs/kratos-genmc.md
similarity index 98%
rename from doc/wiki-cn/kratos-genmc.md
rename to docs/kratos-genmc.md
index 76a453bc..d83378c4 100644
--- a/doc/wiki-cn/kratos-genmc.md
+++ b/docs/kratos-genmc.md
@@ -66,6 +66,3 @@ func noneKey() string {
 
 也可以参考完整的testdata例子：kratos/tool/kratos-gen-mc/testdata
 
--------------
-
-[文档目录树](summary.md)
diff --git a/doc/wiki-cn/kratos-protoc.md b/docs/kratos-protoc.md
similarity index 96%
rename from doc/wiki-cn/kratos-protoc.md
rename to docs/kratos-protoc.md
index 02fe0d79..78273fa3 100644
--- a/doc/wiki-cn/kratos-protoc.md
+++ b/docs/kratos-protoc.md
@@ -26,6 +26,3 @@ protoc --proto_path=$GOPATH --proto_path=$GOPATH/github.com/go-kratos/kratos/thi
 protoc --proto_path=$GOPATH --proto_path=$GOPATH/github.com/go-kratos/kratos/third_party --proto_path=. --ecode_out=:. api.proto
 ```
 
--------------
-
-[文档目录树](summary.md)
diff --git a/doc/wiki-cn/kratos-swagger.md b/docs/kratos-swagger.md
similarity index 85%
rename from doc/wiki-cn/kratos-swagger.md
rename to docs/kratos-swagger.md
index 4c740196..6d28b3f2 100644
--- a/doc/wiki-cn/kratos-swagger.md
+++ b/docs/kratos-swagger.md
@@ -6,6 +6,3 @@ kratos tool swagger serve api/api.swagger.json
 同时也可以查看更多的 [go-swagger](https://github.com/go-swagger/go-swagger) 官方参数进行使用。
 
 
--------------
-
-[文档目录树](summary.md)
diff --git a/doc/wiki-cn/kratos-tool.md b/docs/kratos-tool.md
similarity index 96%
rename from doc/wiki-cn/kratos-tool.md
rename to docs/kratos-tool.md
index e7202823..3bb45a4f 100644
--- a/doc/wiki-cn/kratos-tool.md
+++ b/docs/kratos-tool.md
@@ -92,8 +92,6 @@ swagger(已安装): swagger api文档 Author(goswagger.io) [2019/05/05]
 执行工具: kratos tool demo
 安装全部工具: kratos tool install all
 全部升级: kratos tool upgrade all
-
-详细文档： https://github.com/go-kratos/kratos/blob/master/doc/wiki-cn/kratos-tool.md
 ```
 
 > 小小说明：如未安装工具，第一次运行也可自动安装，不需要特别执行install
@@ -106,6 +104,3 @@ swagger(已安装): swagger api文档 Author(goswagger.io) [2019/05/05]
 * [genmc](kratos-genmc.md) 用于自动生成memcached缓存代码；
 * [genbts](kratos-genbts.md) 用于生成缓存回源代码生成，如果miss则调用回源函数从数据源获取，然后塞入缓存；
 
--------------
-
-[文档目录树](summary.md)
diff --git a/docs/log-agent.md b/docs/log-agent.md
new file mode 100644
index 00000000..e69de29b
diff --git a/doc/wiki-cn/logger.md b/docs/logger.md
similarity index 94%
rename from doc/wiki-cn/logger.md
rename to docs/logger.md
index 105fd5f6..6c30bb1c 100644
--- a/doc/wiki-cn/logger.md
+++ b/docs/logger.md
@@ -32,6 +32,3 @@ func main() {
 ## 扩展阅读
 * [log-agent](log-agent.md)
 
--------------
-
-[文档目录树](summary.md)
diff --git a/doc/wiki-cn/protoc.md b/docs/protoc.md
similarity index 97%
rename from doc/wiki-cn/protoc.md
rename to docs/protoc.md
index 1d9ff92d..4676154c 100644
--- a/doc/wiki-cn/protoc.md
+++ b/docs/protoc.md
@@ -24,6 +24,3 @@ protoc -I$GOPATH/src:$KRATOS_HOME/tool/protobuf/pkg/extensions:$KRATOS_DEMO/api
 
 请注意替换`/Users/felix/work/go/src`目录为你本地开发环境对应GOPATH目录，其中`--gogofast_out`意味着告诉`protoc`工具需要使用`gogo protobuf`的工具生成代码。
 
--------------
-
-[文档目录树](summary.md)
diff --git a/doc/wiki-cn/quickstart.md b/docs/quickstart.md
similarity index 91%
rename from doc/wiki-cn/quickstart.md
rename to docs/quickstart.md
index e1da9a54..0cf5c0b4 100644
--- a/doc/wiki-cn/quickstart.md
+++ b/docs/quickstart.md
@@ -1,6 +1,8 @@
 # 快速开始
 
-快速使用kratos项目，可以使用`kratos`工具，如下：
+**在安装之前，请确认您的开发环境中正确安装了[golang](https://golang.org/), [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git), 和[protoc](https://grpc.io/docs/protoc-installation/)**
+
+创建kratos项目，可以使用`kratos`工具，如下：
 
 ```shell
 go get -u github.com/go-kratos/kratos/tool/kratos
@@ -67,6 +69,3 @@ go build
 
 [kratos工具](kratos-tool.md)
 
--------------
-
-[文档目录树](summary.md)
diff --git a/doc/wiki-cn/ratelimit.md b/docs/ratelimit.md
similarity index 93%
rename from doc/wiki-cn/ratelimit.md
rename to docs/ratelimit.md
index 51d28a76..17f58c6a 100644
--- a/doc/wiki-cn/ratelimit.md
+++ b/docs/ratelimit.md
@@ -25,7 +25,7 @@ kratos 借鉴了 Sentinel 项目的自适应限流系统，通过综合分析服
 
 在自适应限流保护中，采集到的指标的时效性非常强，系统只需要采集最近一小段时间内的 qps、rt 即可，对于较老的数据，会自动丢弃。为了实现这个效果，kratos 使用了滑动窗口来保存采样数据。
 
-![ratelimit-rolling-window](/doc/img/ratelimit-rolling-window.png)
+![ratelimit-rolling-window](img/ratelimit-rolling-window.png)
 
 如上图，展示了一个具有两个桶（bucket）的滑动窗口（rolling window）。整个滑动窗口用来保存最近 1s 的采样数据，每个小的桶用来保存 500ms 的采样数据。
 当时间流动之后，过期的桶会自动被新桶的数据覆盖掉，在图中，在 1000-1500ms 时，bucket 1 的数据因为过期而被丢弃，之后 bucket 3 的数据填到了窗口的头部。
@@ -45,7 +45,7 @@ windows 表示一秒内采样窗口的数量，默认配置中是 5s 50 个采
 
 场景1，请求以每秒增加1个的速度不停上升，压测效果如下：
 
-![ratelimit-benchmark-up-1](/doc/img/ratelimit-benchmark-up-1.png)
+![ratelimit-benchmark-up-1](img/ratelimit-benchmark-up-1.png)
 
 左测是没有限流的压测效果，右侧是带限流的压测效果。
 可以看到，没有限流的场景里，系统在 700qps 时开始抖动，在 1k qps 时被拖垮，几乎没有新的请求能被放行，然而在使用限流之后，系统请求能够稳定在 600 qps 左右，rt 没有暴增，服务也没有被打垮，可见，限流有效的保护了服务。
@@ -55,6 +55,3 @@ windows 表示一秒内采样窗口的数量，默认配置中是 5s 50 个采
 
 [Sentinel 系统自适应限流](https://github.com/alibaba/Sentinel/wiki/%E7%B3%BB%E7%BB%9F%E8%87%AA%E9%80%82%E5%BA%94%E9%99%90%E6%B5%81)
 
--------------
-
-[文档目录树](summary.md)
diff --git a/doc/wiki-cn/trace.md b/docs/trace.md
similarity index 98%
rename from doc/wiki-cn/trace.md
rename to docs/trace.md
index f486e584..34cd5e4a 100644
--- a/doc/wiki-cn/trace.md
+++ b/docs/trace.md
@@ -39,4 +39,4 @@ func main(){
 
 ### zipkin效果图
 
-![zipkin](/doc/img/zipkin.jpg)
+![zipkin](img/zipkin.jpg)
diff --git a/doc/wiki-cn/ut-support.md b/docs/ut-support.md
similarity index 98%
rename from doc/wiki-cn/ut-support.md
rename to docs/ut-support.md
index 48c0eff6..789d25f4 100644
--- a/doc/wiki-cn/ut-support.md
+++ b/docs/ut-support.md
@@ -146,7 +146,7 @@ func TestUsers(t *testing.T) {
 ```
 
 #### 使用建议
-强烈建议使用 [testgen](https://github.com/go-kratos/kratos/blob/master/doc/wiki-cn/ut-testgen.md) 进行测试用例的生成，生成后每个方法将包含一个符合以下规范的正向用例。
+强烈建议使用 [testgen](ut-testgen.md) 进行测试用例的生成，生成后每个方法将包含一个符合以下规范的正向用例。
 
 用例规范：
 1. 每个方法至少包含一个测试方法（命名为Test[PackageName][FunctionName]）
@@ -169,6 +169,7 @@ func TestUsers(t *testing.T) {
 #### 使用示例
 1. 上层包对下层包依赖示例
 Service层对Dao层依赖：
+
 ```GO
 // 原方法
 func (s *Service) realnameAlipayApply(c context.Context, mid int64) (info *model.RealnameAlipayApply, err error) {
@@ -178,7 +179,7 @@ func (s *Service) realnameAlipayApply(c context.Context, mid int64) (info *model
     ...
     return
 }
-  
+
 // 测试方法
 func TestServicerealnameAlipayApply(t *testing.T) {
     convey.Convey("realnameAlipayApply", t, func(ctx convey.C) {
@@ -198,8 +199,8 @@ func TestServicerealnameAlipayApply(t *testing.T) {
 }
 ```
 2. 基础库错误Mock示例
+
 ```Go
-  
 // 原方法（部分）
 func (d *Dao) BaseInfoCache(c context.Context, mid int64) (info *model.BaseInfo, err error) {
     ...
@@ -213,8 +214,7 @@ func (d *Dao) BaseInfoCache(c context.Context, mid int64) (info *model.BaseInfo,
     ...
     return
 }
- 
- 
+
 // 测试方法（错误Mock部分）
 func TestDaoBaseInfoCache(t *testing.T) {
     convey.Convey("BaseInfoCache", t, func(ctx convey.C) {
@@ -232,6 +232,7 @@ func TestDaoBaseInfoCache(t *testing.T) {
     })
 }
 ```
+
 #### 注意事项
 - Monkey非线程安全
 - Monkey无法针对Inline方法打补丁，在测试时可以使用go test -gcflags=-l来关闭inline编译的模式（一些简单的go inline介绍戳这里）
@@ -269,9 +270,10 @@ func TestDaoBaseInfoCache(t *testing.T) {
 
 使用示例：
 1. net/http 标准库 HTTP 请求Mock
+
 ```Go
 import  gock "gopkg.in/h2non/gock.v1"
-  
+
 // 原方法
  func (d *Dao) Upload(c context.Context, fileName, fileType string, expire int64, body io.Reader) (location string, err error) {
     ...
@@ -288,8 +290,7 @@ import  gock "gopkg.in/h2non/gock.v1"
     ...
     return
 }
- 
- 
+
 // 测试方法
 func TestDaoUpload(t *testing.T) {
     convey.Convey("Upload", t, func(ctx convey.C) {
@@ -348,6 +349,7 @@ func httpMock(method, url string) *gock.Request {
 }
 ```
 2. blademaster库HTTP请求Mock
+
 ```Go
 // 原方法
 func (d *Dao) SendWechatToGroup(c context.Context, chatid, msg string) (err error) {
@@ -360,7 +362,7 @@ func (d *Dao) SendWechatToGroup(c context.Context, chatid, msg string) (err erro
     }
     return
 }
-  
+
 // 测试方法
 func TestDaoSendWechatToGroup(t *testing.T) {
     convey.Convey("SendWechatToGroup", t, func(ctx convey.C) {
@@ -393,7 +395,7 @@ func TestDaoSendWechatToGroup(t *testing.T) {
         })
     })
 }
- 
+
 func httpMock(method, url string) *gock.Request {
     r := gock.New(url)
     r.Method = strings.ToUpper(method)
@@ -435,6 +437,7 @@ mockgen -destination=$GOPATH/kratos/app/xxx/dao/dao_mock.go -package=dao kratos/
 ./creater --m mock absolute/path/to/your/pkg
 ```
 - 测试代码内使用方法
+
 ```Go
 // 测试用例内直接使用
 // 需引入的包
@@ -464,7 +467,7 @@ func TestPkgFoo(t *testing.T) {
         ...
     })
 }
- 
+
 // 可以利用Convey执行顺序方式适当调整以简化代码
 func TestPkgFoo(t *testing.T) {
     convey.Convey("Foo", t, func(ctx convey.C) {
diff --git a/doc/wiki-cn/ut.md b/docs/ut.md
similarity index 87%
rename from doc/wiki-cn/ut.md
rename to docs/ut.md
index 9a73d15c..8907f051 100644
--- a/doc/wiki-cn/ut.md
+++ b/docs/ut.md
@@ -30,9 +30,3 @@ golang 的单元测试，既可以用官方自带的 testing 包，也有开源
 > - 基础组件，如mc、redis、mysql等，由 testcli(testing/lich) 起基础镜像支持（需要提供建表、INSERT语句）与本地开发环境一致，也保证了结果的一致性。
 > - rpc server，如 xxxx-service 需要定义 interface 供业务依赖方使用。所有rpc server 都必须要提供一个interface+mock代码(gomock)。
 > - http server则直接写mock代码gock。
-
-# 注意
-单元测试极其重要，良好的单元测试习惯能很大程度上避免代码变更引起的bug！  
-单元测试极其重要，良好的单元测试习惯能很大程度上避免代码变更引起的bug！  
-单元测试极其重要，良好的单元测试习惯能很大程度上避免代码变更引起的bug！  
-以为很重要所以重复 3 遍～
\ No newline at end of file
diff --git a/doc/wiki-cn/warden-balancer.md b/docs/warden-balancer.md
similarity index 95%
rename from doc/wiki-cn/warden-balancer.md
rename to docs/warden-balancer.md
index 7ca6e30b..5ffc9ee6 100644
--- a/doc/wiki-cn/warden-balancer.md
+++ b/docs/warden-balancer.md
@@ -37,7 +37,3 @@ func NewClient(conf *ClientConfig, opt ...grpc.DialOption) *Client {
 	return c
 }
 ```
-
--------------
-
-[文档目录树](summary.md)
diff --git a/doc/wiki-cn/warden-mid.md b/docs/warden-mid.md
similarity index 98%
rename from doc/wiki-cn/warden-mid.md
rename to docs/warden-mid.md
index 8afcfdb3..1fbd2ad8 100644
--- a/doc/wiki-cn/warden-mid.md
+++ b/docs/warden-mid.md
@@ -324,7 +324,7 @@ func serverLogging() grpc.UnaryServerInterceptor {
 
 ## 自适应限流拦截器
 
-更多关于自适应限流的信息，请参考：[kratos 自适应限流](/doc/wiki-cn/ratelimit.md)
+更多关于自适应限流的信息，请参考：[kratos 自适应限流](ratelimit.md)
 
 ```go
 package grpc
@@ -368,9 +368,7 @@ func New(svc *service.Service) *warden.Server {
 
 # 扩展阅读
 
-[warden快速开始](warden-quickstart.md) [warden基于pb生成](warden-pb.md) [warden负载均衡](warden-balancer.md) [warden服务发现](warden-resolver.md)
-
--------------
-
-[文档目录树](summary.md)
-
+[warden快速开始](warden-quickstart.md)  
+[warden基于pb生成](warden-pb.md)  
+[warden负载均衡](warden-balancer.md)   
+[warden服务发现](warden-resolver.md)  
diff --git a/doc/wiki-cn/warden-pb.md b/docs/warden-pb.md
similarity index 89%
rename from doc/wiki-cn/warden-pb.md
rename to docs/warden-pb.md
index ca8856de..a02cb1d0 100644
--- a/doc/wiki-cn/warden-pb.md
+++ b/docs/warden-pb.md
@@ -40,8 +40,9 @@ func (s *Service) SayHelloURL(ctx context.Context, req *pb.HelloReq) (reply *pb.
 
 # 扩展阅读
 
-[warden快速开始](warden-quickstart.md) [warden拦截器](warden-mid.md) [warden负载均衡](warden-balancer.md) [warden服务发现](warden-resolver.md)
+[warden快速开始](warden-quickstart.md)  
+[warden拦截器](warden-mid.md)  
+[warden负载均衡](warden-balancer.md)  
+[warden服务发现](warden-resolver.md)
 
--------------
 
-[文档目录树](summary.md)
diff --git a/doc/wiki-cn/warden-quickstart.md b/docs/warden-quickstart.md
similarity index 99%
rename from doc/wiki-cn/warden-quickstart.md
rename to docs/warden-quickstart.md
index 9ce395d3..6173ebd5 100644
--- a/doc/wiki-cn/warden-quickstart.md
+++ b/docs/warden-quickstart.md
@@ -169,7 +169,3 @@ func (d *Dao) SayHello(c context.Context, req *demoapi.HelloReq) (resp *grpcempt
 [warden基于pb生成](warden-pb.md)  
 [warden服务发现](warden-resolver.md)  
 [warden负载均衡](warden-balancer.md) 
-
--------------
-
-[文档目录树](summary.md)
diff --git a/doc/wiki-cn/warden-resolver.md b/docs/warden-resolver.md
similarity index 98%
rename from doc/wiki-cn/warden-resolver.md
rename to docs/warden-resolver.md
index 8304dca0..38232743 100644
--- a/doc/wiki-cn/warden-resolver.md
+++ b/docs/warden-resolver.md
@@ -248,8 +248,7 @@ etcd默认的全局keyPrefix为kratos_etcd,当该keyPrefix与项目中其他keyP
 
 # 扩展阅读
 
-[warden快速开始](warden-quickstart.md) [warden拦截器](warden-mid.md) [warden基于pb生成](warden-pb.md) [warden负载均衡](warden-balancer.md)
-
--------------
-
-[文档目录树](summary.md)
+[warden快速开始](warden-quickstart.md)  
+[warden拦截器](warden-mid.md)  
+[warden基于pb生成](warden-pb.md)  
+[warden负载均衡](warden-balancer.md)
diff --git a/doc/wiki-cn/warden.md b/docs/warden.md
similarity index 82%
rename from doc/wiki-cn/warden.md
rename to docs/warden.md
index 9c278256..a2dab99b 100644
--- a/doc/wiki-cn/warden.md
+++ b/docs/warden.md
@@ -33,8 +33,9 @@ gRPC暴露了两个拦截器接口，分别是：
 
 # 扩展阅读
 
-[warden快速开始](warden-quickstart.md) [warden拦截器](warden-mid.md) [warden负载均衡](warden-balancer.md) [warden基于pb生成](warden-pb.md) [warden服务发现](warden-resolver.md)
-
--------------
+[warden快速开始](warden-quickstart.md) 
+[warden拦截器](warden-mid.md) 
+[warden负载均衡](warden-balancer.md) 
+[warden基于pb生成](warden-pb.md) 
+[warden服务发现](warden-resolver.md)
 
-[文档目录树](summary.md)
diff --git a/misc/stat/dashboard/prometheus.json b/misc/stat/dashboard/prometheus.json
index 8641fc3b..d1529a8a 100644
--- a/misc/stat/dashboard/prometheus.json
+++ b/misc/stat/dashboard/prometheus.json
@@ -1604,7 +1604,7 @@
                 }
               ],
               "intervalFactor": 1,
-              "legendFormat": "{{caller}} => {{method}}",
+              "legendFormat": "{{caller}} => {{path}}",
               "orderByTime": "ASC",
               "policy": "default",
               "refId": "A",
@@ -5736,10 +5736,6 @@
   "templating": {
     "list": [
       {
-        "current": {
-          "text": "LIVE",
-          "value": "LIVE"
-        },
         "hide": 2,
         "includeAll": false,
         "label": null,
@@ -5748,7 +5744,6 @@
         "options": [],
         "query": "prometheus",
         "refresh": 1,
-        "regex": "LIVE",
         "skipUrlSync": false,
         "type": "datasource"
       },
@@ -5919,4 +5914,4 @@
   "title": "Go业务监控",
   "uid": "FitP_nDZz",
   "version": 10
-}
\ No newline at end of file
+}
diff --git a/pkg/cache/redis/errors.go b/pkg/cache/redis/errors.go
index e3bd5c33..6be483ff 100644
--- a/pkg/cache/redis/errors.go
+++ b/pkg/cache/redis/errors.go
@@ -20,6 +20,9 @@ func formatErr(err error, name, addr string) string {
 		case strings.HasPrefix(es, "read"):
 			return "read timeout"
 		case strings.HasPrefix(es, "dial"):
+			if strings.Contains(es, "connection refused") {
+				return "connection refused"
+			}
 			return "dial timeout"
 		case strings.HasPrefix(es, "write"):
 			return "write timeout"
@@ -29,6 +32,10 @@ func formatErr(err error, name, addr string) string {
 			return "reset"
 		case strings.Contains(es, "broken"):
 			return "broken pipe"
+		case strings.Contains(es, "pool exhausted"):
+			return "pool exhausted"
+		case strings.Contains(es, "pool closed"):
+			return "pool closed"
 		default:
 			return "unexpected err"
 		}
diff --git a/pkg/cache/redis/pool.go b/pkg/cache/redis/pool.go
index df2d0829..ecab41e2 100644
--- a/pkg/cache/redis/pool.go
+++ b/pkg/cache/redis/pool.go
@@ -58,6 +58,7 @@ func NewPool(c *Config, options ...DialOption) (p *Pool) {
 		DialReadTimeout(time.Duration(c.ReadTimeout)),
 		DialWriteTimeout(time.Duration(c.WriteTimeout)),
 		DialPassword(c.Auth),
+		DialDatabase(c.Db),
 	}
 	ops = append(ops, options...)
 	p1 := pool.NewSlice(c.Config)
diff --git a/pkg/cache/redis/redis.go b/pkg/cache/redis/redis.go
index a664cfe5..634dc1cd 100644
--- a/pkg/cache/redis/redis.go
+++ b/pkg/cache/redis/redis.go
@@ -34,6 +34,7 @@ type Config struct {
 	Proto        string
 	Addr         string
 	Auth         string
+	Db           int
 	DialTimeout  xtime.Duration
 	ReadTimeout  xtime.Duration
 	WriteTimeout xtime.Duration
diff --git a/pkg/cache/redis/trace.go b/pkg/cache/redis/trace.go
index 90144ce3..656b616c 100644
--- a/pkg/cache/redis/trace.go
+++ b/pkg/cache/redis/trace.go
@@ -22,13 +22,14 @@ var _internalTags = []trace.Tag{
 }
 
 type traceConn struct {
-	// tr for pipeline, if tr != nil meaning on pipeline
+	// tr parent trace.
 	tr trace.Trace
+	// trPipe for pipeline, if trPipe != nil meaning on pipeline.
+	trPipe trace.Trace
+
 	// connTag include e.g. ip,port
 	connTags []trace.Tag
 
-	ctx context.Context
-
 	// origin redis conn
 	Conn
 	pending int
@@ -39,9 +40,15 @@ type traceConn struct {
 func (t *traceConn) Do(commandName string, args ...interface{}) (reply interface{}, err error) {
 	statement := getStatement(commandName, args...)
 	defer t.slowLog(statement, time.Now())
+
 	// NOTE: ignored empty commandName
 	// current sdk will Do empty command after pipeline finished
-	if t.tr == nil || commandName == "" {
+	if commandName == "" {
+		t.pending = 0
+		t.trPipe = nil
+		return t.Conn.Do(commandName, args...)
+	}
+	if t.tr == nil {
 		return t.Conn.Do(commandName, args...)
 	}
 	tr := t.tr.Fork("", "Redis:"+commandName)
@@ -60,18 +67,19 @@ func (t *traceConn) Send(commandName string, args ...interface{}) (err error) {
 	if t.tr == nil {
 		return t.Conn.Send(commandName, args...)
 	}
-	if t.pending == 1 {
-		t.tr = t.tr.Fork("", "Redis:Pipeline")
-		t.tr.SetTag(_internalTags...)
-		t.tr.SetTag(t.connTags...)
+
+	if t.trPipe == nil {
+		t.trPipe = t.tr.Fork("", "Redis:Pipeline")
+		t.trPipe.SetTag(_internalTags...)
+		t.trPipe.SetTag(t.connTags...)
 	}
-	t.tr.SetLog(
+	t.trPipe.SetLog(
 		trace.Log(trace.LogEvent, "Send"),
 		trace.Log("db.statement", statement),
 	)
 	if err = t.Conn.Send(commandName, args...); err != nil {
-		t.tr.SetTag(trace.TagBool(trace.TagError, true))
-		t.tr.SetLog(
+		t.trPipe.SetTag(trace.TagBool(trace.TagError, true))
+		t.trPipe.SetLog(
 			trace.Log(trace.LogEvent, "Send Fail"),
 			trace.Log(trace.LogMessage, err.Error()),
 		)
@@ -81,14 +89,14 @@ func (t *traceConn) Send(commandName string, args ...interface{}) (err error) {
 
 func (t *traceConn) Flush() error {
 	defer t.slowLog("Flush", time.Now())
-	if t.tr == nil {
+	if t.trPipe == nil {
 		return t.Conn.Flush()
 	}
-	t.tr.SetLog(trace.Log(trace.LogEvent, "Flush"))
+	t.trPipe.SetLog(trace.Log(trace.LogEvent, "Flush"))
 	err := t.Conn.Flush()
 	if err != nil {
-		t.tr.SetTag(trace.TagBool(trace.TagError, true))
-		t.tr.SetLog(
+		t.trPipe.SetTag(trace.TagBool(trace.TagError, true))
+		t.trPipe.SetLog(
 			trace.Log(trace.LogEvent, "Flush Fail"),
 			trace.Log(trace.LogMessage, err.Error()),
 		)
@@ -98,14 +106,14 @@ func (t *traceConn) Flush() error {
 
 func (t *traceConn) Receive() (reply interface{}, err error) {
 	defer t.slowLog("Receive", time.Now())
-	if t.tr == nil {
+	if t.trPipe == nil {
 		return t.Conn.Receive()
 	}
-	t.tr.SetLog(trace.Log(trace.LogEvent, "Receive"))
+	t.trPipe.SetLog(trace.Log(trace.LogEvent, "Receive"))
 	reply, err = t.Conn.Receive()
 	if err != nil {
-		t.tr.SetTag(trace.TagBool(trace.TagError, true))
-		t.tr.SetLog(
+		t.trPipe.SetTag(trace.TagBool(trace.TagError, true))
+		t.trPipe.SetLog(
 			trace.Log(trace.LogEvent, "Receive Fail"),
 			trace.Log(trace.LogMessage, err.Error()),
 		)
@@ -114,8 +122,8 @@ func (t *traceConn) Receive() (reply interface{}, err error) {
 		t.pending--
 	}
 	if t.pending == 0 {
-		t.tr.Finish(nil)
-		t.tr = nil
+		t.trPipe.Finish(nil)
+		t.trPipe = nil
 	}
 	return reply, err
 }
@@ -123,6 +131,8 @@ func (t *traceConn) Receive() (reply interface{}, err error) {
 func (t *traceConn) WithContext(ctx context.Context) Conn {
 	t.Conn = t.Conn.WithContext(ctx)
 	t.tr, _ = trace.FromContext(ctx)
+	t.pending = 0
+	t.trPipe = nil
 	return t
 }
 
diff --git a/pkg/container/group/group.go b/pkg/container/group/group.go
index cf0b80e1..7385bdb5 100644
--- a/pkg/container/group/group.go
+++ b/pkg/container/group/group.go
@@ -8,7 +8,7 @@ import "sync"
 // Group is a lazy load container.
 type Group struct {
 	new  func() interface{}
-	objs sync.Map
+	objs map[string]interface{}
 	sync.RWMutex
 }
 
@@ -19,19 +19,29 @@ func NewGroup(new func() interface{}) *Group {
 	}
 	return &Group{
 		new: new,
+		objs: make(map[string]interface{}),
 	}
 }
 
 // Get gets the object by the given key.
 func (g *Group) Get(key string) interface{} {
 	g.RLock()
-	new := g.new
+	obj, ok := g.objs[key]
+	if ok {
+		g.RUnlock()
+		return obj
+	}
 	g.RUnlock()
-	obj, ok := g.objs.Load(key)
-	if !ok {
-		obj = new()
-		g.objs.Store(key, obj)
+
+	// double check
+	g.Lock()
+	defer g.Unlock()
+	obj, ok = g.objs[key]
+	if ok {
+		return obj
 	}
+	obj = g.new()
+	g.objs[key] = obj
 	return obj
 }
 
@@ -48,8 +58,7 @@ func (g *Group) Reset(new func() interface{}) {
 
 // Clear deletes all objects.
 func (g *Group) Clear() {
-	g.objs.Range(func(key, value interface{}) bool {
-		g.objs.Delete(key)
-		return true
-	})
+	g.Lock()
+	g.objs = make(map[string]interface{})
+	g.Unlock()
 }
diff --git a/pkg/log/file.go b/pkg/log/file.go
index 1a8d4e4c..173fdab9 100644
--- a/pkg/log/file.go
+++ b/pkg/log/file.go
@@ -11,16 +11,20 @@ import (
 
 // level idx
 const (
-	_infoIdx = iota
+	_debugIdx = iota
+	_infoIdx
 	_warnIdx
 	_errorIdx
+	_fatalIdx
 	_totalIdx
 )
 
 var _fileNames = map[int]string{
+	_debugIdx: "debug.log",
 	_infoIdx:  "info.log",
 	_warnIdx:  "warning.log",
 	_errorIdx: "error.log",
+	_fatalIdx: "fatal.log",
 }
 
 // FileHandler .
@@ -63,10 +67,14 @@ func (h *FileHandler) Log(ctx context.Context, lv Level, args ...D) {
 	d[_time] = time.Now().Format(_timeFormat)
 	var w io.Writer
 	switch lv {
+	case _debugLevel:
+		w = h.fws[_debugIdx]
 	case _warnLevel:
 		w = h.fws[_warnIdx]
 	case _errorLevel:
 		w = h.fws[_errorIdx]
+	case _fatalLevel:
+		w = h.fws[_fatalIdx]
 	default:
 		w = h.fws[_infoIdx]
 	}
diff --git a/pkg/log/log.go b/pkg/log/log.go
index 1778f93a..9d5c30f7 100644
--- a/pkg/log/log.go
+++ b/pkg/log/log.go
@@ -138,49 +138,109 @@ func Init(conf *Config) {
 	c = conf
 }
 
+// Debug logs a message at the debug log level.
+func Debug(format string, args ...interface{}) {
+	if int32(_debugLevel) >= c.V {
+		h.Log(context.Background(), _debugLevel, KVString(_log, fmt.Sprintf(format, args...)))
+	}
+}
+
 // Info logs a message at the info log level.
 func Info(format string, args ...interface{}) {
-	h.Log(context.Background(), _infoLevel, KVString(_log, fmt.Sprintf(format, args...)))
+	if int32(_infoLevel) >= c.V {
+		h.Log(context.Background(), _infoLevel, KVString(_log, fmt.Sprintf(format, args...)))
+	}
 }
 
 // Warn logs a message at the warning log level.
 func Warn(format string, args ...interface{}) {
-	h.Log(context.Background(), _warnLevel, KVString(_log, fmt.Sprintf(format, args...)))
+	if int32(_warnLevel) >= c.V {
+		h.Log(context.Background(), _warnLevel, KVString(_log, fmt.Sprintf(format, args...)))
+	}
 }
 
 // Error logs a message at the error log level.
 func Error(format string, args ...interface{}) {
-	h.Log(context.Background(), _errorLevel, KVString(_log, fmt.Sprintf(format, args...)))
+	if int32(_errorLevel) >= c.V {
+		h.Log(context.Background(), _errorLevel, KVString(_log, fmt.Sprintf(format, args...)))
+	}
+}
+
+// Fatal logs a message at the fatal log level.
+func Fatal(format string, args ...interface{}) {
+	if int32(_fatalLevel) >= c.V {
+		h.Log(context.Background(), _fatalLevel, KVString(_log, fmt.Sprintf(format, args...)))
+	}
+}
+
+// Debugc logs a message at the debug log level.
+func Debugc(ctx context.Context, format string, args ...interface{}) {
+	if int32(_debugLevel) >= c.V {
+		h.Log(ctx, _debugLevel, KVString(_log, fmt.Sprintf(format, args...)))
+	}
 }
 
 // Infoc logs a message at the info log level.
 func Infoc(ctx context.Context, format string, args ...interface{}) {
-	h.Log(ctx, _infoLevel, KVString(_log, fmt.Sprintf(format, args...)))
+	if int32(_infoLevel) >= c.V {
+		h.Log(ctx, _infoLevel, KVString(_log, fmt.Sprintf(format, args...)))
+	}
 }
 
 // Errorc logs a message at the error log level.
 func Errorc(ctx context.Context, format string, args ...interface{}) {
-	h.Log(ctx, _errorLevel, KVString(_log, fmt.Sprintf(format, args...)))
+	if int32(_errorLevel) >= c.V {
+		h.Log(ctx, _errorLevel, KVString(_log, fmt.Sprintf(format, args...)))
+	}
 }
 
 // Warnc logs a message at the warning log level.
 func Warnc(ctx context.Context, format string, args ...interface{}) {
-	h.Log(ctx, _warnLevel, KVString(_log, fmt.Sprintf(format, args...)))
+	if int32(_warnLevel) >= c.V {
+		h.Log(ctx, _warnLevel, KVString(_log, fmt.Sprintf(format, args...)))
+	}
+}
+
+// Fatalc logs a message at the fatal log level.
+func Fatalc(ctx context.Context, format string, args ...interface{}) {
+	if int32(_fatalLevel) >= c.V {
+		h.Log(ctx, _fatalLevel, KVString(_log, fmt.Sprintf(format, args...)))
+	}
+}
+
+// Debugv logs a message at the debug log level.
+func Debugv(ctx context.Context, args ...D) {
+	if int32(_debugLevel) >= c.V {
+		h.Log(ctx, _debugLevel, args...)
+	}
 }
 
 // Infov logs a message at the info log level.
 func Infov(ctx context.Context, args ...D) {
-	h.Log(ctx, _infoLevel, args...)
+	if int32(_infoLevel) >= c.V {
+		h.Log(ctx, _infoLevel, args...)
+	}
 }
 
 // Warnv logs a message at the warning log level.
 func Warnv(ctx context.Context, args ...D) {
-	h.Log(ctx, _warnLevel, args...)
+	if int32(_warnLevel) >= c.V {
+		h.Log(ctx, _warnLevel, args...)
+	}
 }
 
 // Errorv logs a message at the error log level.
 func Errorv(ctx context.Context, args ...D) {
-	h.Log(ctx, _errorLevel, args...)
+	if int32(_errorLevel) >= c.V {
+		h.Log(ctx, _errorLevel, args...)
+	}
+}
+
+// Fatalv logs a message at the error log level.
+func Fatalv(ctx context.Context, args ...D) {
+	if int32(_fatalLevel) >= c.V {
+		h.Log(ctx, _fatalLevel, args...)
+	}
 }
 
 func logw(args []interface{}) []D {
@@ -198,19 +258,39 @@ func logw(args []interface{}) []D {
 	return ds
 }
 
+// Debugw logs a message with some additional context. The variadic key-value pairs are treated as they are in With.
+func Debugw(ctx context.Context, args ...interface{}) {
+	if int32(_debugLevel) >= c.V {
+		h.Log(ctx, _debugLevel, logw(args)...)
+	}
+}
+
 // Infow logs a message with some additional context. The variadic key-value pairs are treated as they are in With.
 func Infow(ctx context.Context, args ...interface{}) {
-	h.Log(ctx, _infoLevel, logw(args)...)
+	if int32(_infoLevel) >= c.V {
+		h.Log(ctx, _infoLevel, logw(args)...)
+	}
 }
 
 // Warnw logs a message with some additional context. The variadic key-value pairs are treated as they are in With.
 func Warnw(ctx context.Context, args ...interface{}) {
-	h.Log(ctx, _warnLevel, logw(args)...)
+	if int32(_warnLevel) >= c.V {
+		h.Log(ctx, _warnLevel, logw(args)...)
+	}
 }
 
 // Errorw logs a message with some additional context. The variadic key-value pairs are treated as they are in With.
 func Errorw(ctx context.Context, args ...interface{}) {
-	h.Log(ctx, _errorLevel, logw(args)...)
+	if int32(_errorLevel) >= c.V {
+		h.Log(ctx, _errorLevel, logw(args)...)
+	}
+}
+
+// Fatalw logs a message with some additional context. The variadic key-value pairs are treated as they are in With.
+func Fatalw(ctx context.Context, args ...interface{}) {
+	if int32(_fatalLevel) >= c.V {
+		h.Log(ctx, _fatalLevel, logw(args)...)
+	}
 }
 
 // SetFormat only effective on stdout and file handler
diff --git a/pkg/net/rpc/warden/validate.go b/pkg/net/rpc/warden/validate.go
index ddd715ca..94d3d212 100644
--- a/pkg/net/rpc/warden/validate.go
+++ b/pkg/net/rpc/warden/validate.go
@@ -2,8 +2,8 @@ package warden
 
 import (
 	"context"
-
-	"github.com/go-kratos/kratos/pkg/ecode"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
 
 	"google.golang.org/grpc"
 	"gopkg.in/go-playground/validator.v9"
@@ -15,7 +15,7 @@ var validate = validator.New()
 func (s *Server) validate() grpc.UnaryServerInterceptor {
 	return func(ctx context.Context, req interface{}, args *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
 		if err = validate.Struct(req); err != nil {
-			err = ecode.Error(ecode.RequestErr, err.Error())
+			err = status.Error(codes.InvalidArgument, err.Error())
 			return
 		}
 		resp, err = handler(ctx, req)
@@ -30,7 +30,7 @@ func (s *Server) RegisterValidation(key string, fn validator.Func) error {
 	return validate.RegisterValidation(key, fn)
 }
 
-//GetValidate return the default validate
+// GetValidate return the default validate
 func (s *Server) GetValidate() *validator.Validate {
 	return validate
 }
diff --git a/tool/kratos-gen-project/new.go b/tool/kratos-gen-project/new.go
index cbe3280b..c6234986 100644
--- a/tool/kratos-gen-project/new.go
+++ b/tool/kratos-gen-project/new.go
@@ -26,7 +26,7 @@ func runNew(ctx *cli.Context) (err error) {
 		pwd, _ := os.Getwd()
 		p.path = filepath.Join(pwd, p.Name)
 	}
-	p.ModPrefix = modPath(p.path)
+	p.ModPrefix = strings.ReplaceAll(modPath(p.path), "\\", "/")
 	// creata a project
 	if err := create(); err != nil {
 		return err
diff --git a/tool/kratos-gen-project/templates/all/internal/dao/dao.go.tmpl b/tool/kratos-gen-project/templates/all/internal/dao/dao.go.tmpl
index a67d0a8e..81ea0b54 100644
--- a/tool/kratos-gen-project/templates/all/internal/dao/dao.go.tmpl
+++ b/tool/kratos-gen-project/templates/all/internal/dao/dao.go.tmpl
@@ -23,7 +23,7 @@ type Dao interface {
 	Close()
 	Ping(ctx context.Context) (err error)
 	// bts: -nullcache=&model.Article{ID:-1} -check_null_code=$!=nil&&$.ID==-1
-	Article(c context.Context, id int64) (*model.Article, error)
+	RawArticle(c context.Context, id int64) (*model.Article, error)
 }
 
 // dao dao.
diff --git a/tool/kratos-gen-project/templates/grpc/internal/dao/dao.go.tmpl b/tool/kratos-gen-project/templates/grpc/internal/dao/dao.go.tmpl
index a67d0a8e..81ea0b54 100644
--- a/tool/kratos-gen-project/templates/grpc/internal/dao/dao.go.tmpl
+++ b/tool/kratos-gen-project/templates/grpc/internal/dao/dao.go.tmpl
@@ -23,7 +23,7 @@ type Dao interface {
 	Close()
 	Ping(ctx context.Context) (err error)
 	// bts: -nullcache=&model.Article{ID:-1} -check_null_code=$!=nil&&$.ID==-1
-	Article(c context.Context, id int64) (*model.Article, error)
+	RawArticle(c context.Context, id int64) (*model.Article, error)
 }
 
 // dao dao.
diff --git a/tool/kratos-gen-project/templates/http/internal/dao/dao.go.tmpl b/tool/kratos-gen-project/templates/http/internal/dao/dao.go.tmpl
index a67d0a8e..81ea0b54 100644
--- a/tool/kratos-gen-project/templates/http/internal/dao/dao.go.tmpl
+++ b/tool/kratos-gen-project/templates/http/internal/dao/dao.go.tmpl
@@ -23,7 +23,7 @@ type Dao interface {
 	Close()
 	Ping(ctx context.Context) (err error)
 	// bts: -nullcache=&model.Article{ID:-1} -check_null_code=$!=nil&&$.ID==-1
-	Article(c context.Context, id int64) (*model.Article, error)
+	RawArticle(c context.Context, id int64) (*model.Article, error)
 }
 
 // dao dao.
diff --git a/tool/kratos/tool.go b/tool/kratos/tool.go
index 8138f016..0c38224c 100644
--- a/tool/kratos/tool.go
+++ b/tool/kratos/tool.go
@@ -17,7 +17,7 @@ import (
 )
 
 const (
-	toolDoc = "https://github.com/go-kratos/kratos/blob/master/doc/wiki-cn/kratos-tool.md"
+	toolDoc = "https://go-kratos.github.io/kratos/#/kratos-tool"
 )
 
 // Tool is kratos tool.


Test patch:
diff --git a/doc/wiki-cn/ut-testcli.md b/docs/ut-testcli.md
similarity index 98%
rename from doc/wiki-cn/ut-testcli.md
rename to docs/ut-testcli.md
index 7be62e3b..65490e11 100644
--- a/doc/wiki-cn/ut-testcli.md
+++ b/docs/ut-testcli.md
@@ -148,7 +148,5 @@ func TestMain(m *testing.M) {
  ```
 ## 注意
 因为启动mysql容器较为缓慢，健康检测的机制会重试3次，每次暂留5秒钟，基本在10s内mysql就能从creating到服务正常启动！
-
 当然你也可以在使用 testcli 时加上 --nodown，使其不用每次跑都新建容器，只在第一次跑的时候会初始化容器，后面都进行复用，这样速度会快很多。
 
-成功启动后就欢乐奔放的玩耍吧～ Good Lucky!
\ No newline at end of file
diff --git a/doc/wiki-cn/ut-testgen.md b/docs/ut-testgen.md
similarity index 92%
rename from doc/wiki-cn/ut-testgen.md
rename to docs/ut-testgen.md
index 890992ae..c13f90a3 100644
--- a/doc/wiki-cn/ut-testgen.md
+++ b/docs/ut-testgen.md
@@ -43,10 +43,3 @@ $GOPATH/bin/testgen --m mock YOUR_PROJECT/dao #仅传入包路径即可
 ```shell
 $GOPATH/bin/testgen --m monkey yourCodeDirPath #仅传入包路径即可
 ```
-### 赋诗一首
-```
-莫生气 莫生气
-代码辣鸡非我意 
-自己动手分田地
-谈笑风生活长命
-```
\ No newline at end of file
diff --git a/pkg/cache/redis/trace_test.go b/pkg/cache/redis/trace_test.go
index c9502b80..412bf7ce 100644
--- a/pkg/cache/redis/trace_test.go
+++ b/pkg/cache/redis/trace_test.go
@@ -190,3 +190,23 @@ func BenchmarkTraceConn(b *testing.B) {
 		c2.Close()
 	}
 }
+
+func TestTraceConnPending(t *testing.T) {
+	c, err := DialDefaultServer()
+	if err != nil {
+		t.Fatal(err)
+	}
+	tc := &traceConn{
+		Conn:             c,
+		connTags:         []trace.Tag{trace.TagString(trace.TagPeerAddress, "abc")},
+		slowLogThreshold: time.Duration(1 * time.Second),
+	}
+	err = tc.Send("SET", "a", "x")
+	if err != nil {
+		t.Fatal(err)
+	}
+	tc.Close()
+	assert.Equal(t, 1, tc.pending)
+	tc.Do("")
+	assert.Equal(t, 0, tc.pending)
+}
diff --git a/pkg/container/group/group_test.go b/pkg/container/group/group_test.go
index c5282729..e3028e91 100644
--- a/pkg/container/group/group_test.go
+++ b/pkg/container/group/group_test.go
@@ -36,10 +36,10 @@ func TestGroupReset(t *testing.T) {
 	})
 
 	length := 0
-	g.objs.Range(func(_, _ interface{}) bool {
+	for _,_ = range g.objs {
 		length++
-		return true
-	})
+	}
+
 	assert.Equal(t, 0, length)
 
 	g.Get("/x/internal/dummy/user")
@@ -52,18 +52,16 @@ func TestGroupClear(t *testing.T) {
 	})
 	g.Get("/x/internal/dummy/user")
 	length := 0
-	g.objs.Range(func(_, _ interface{}) bool {
+	for _,_ = range g.objs {
 		length++
-		return true
-	})
+	}
 	assert.Equal(t, 1, length)
 
 	g.Clear()
 	length = 0
-	g.objs.Range(func(_, _ interface{}) bool {
+	for _,_ = range g.objs {
 		length++
-		return true
-	})
+	}
 	assert.Equal(t, 0, length)
 
 }
diff --git a/pkg/log/log_test.go b/pkg/log/log_test.go
index 1a938bad..c430d334 100644
--- a/pkg/log/log_test.go
+++ b/pkg/log/log_test.go
@@ -33,6 +33,11 @@ type TestLog struct {
 }
 
 func testLog(t *testing.T) {
+	t.Run("Fatal", func(t *testing.T) {
+		Fatal("hello %s", "world")
+		Fatalv(context.Background(), KV("key", 2222222), KV("test2", "test"))
+		Fatalc(context.Background(), "keys: %s %s...", "key1", "key2")
+	})
 	t.Run("Error", func(t *testing.T) {
 		Error("hello %s", "world")
 		Errorv(context.Background(), KV("key", 2222222), KV("test2", "test"))
@@ -48,6 +53,11 @@ func testLog(t *testing.T) {
 		Infov(context.Background(), KV("key", 2222222), KV("test2", "test"))
 		Infoc(context.Background(), "keys: %s %s...", "key1", "key2")
 	})
+	t.Run("Debug", func(t *testing.T) {
+		Debug("hello %s", "world")
+		Debugv(context.Background(), KV("key", 2222222), KV("test2", "test"))
+		Debugc(context.Background(), "keys: %s %s...", "key1", "key2")
+	})
 }
 
 func TestFile(t *testing.T) {
diff --git a/pkg/testing/lich/README.md b/pkg/testing/lich/README.md
index 11260fa8..541c6c68 100644
--- a/pkg/testing/lich/README.md
+++ b/pkg/testing/lich/README.md
@@ -1,4 +1,4 @@
 ## testing/lich 运行环境构建
 基于 docker-compose 实现跨平台跨语言环境的容器依赖管理方案，以解决运行ut场景下的 (mysql, redis, mc)容器依赖问题。
 
-使用说明参见：https://github.com/go-kratos/kratos/tree/master/tool/testcli/README.md
\ No newline at end of file
+使用说明参见：https://go-kratos.github.io/kratos/#/ut
\ No newline at end of file
diff --git a/tool/testcli/README.MD b/tool/testcli/README.md
similarity index 98%
rename from tool/testcli/README.MD
rename to tool/testcli/README.md
index 7be62e3b..3b56fcad 100644
--- a/tool/testcli/README.MD
+++ b/tool/testcli/README.md
@@ -151,4 +151,4 @@ func TestMain(m *testing.M) {
 
 当然你也可以在使用 testcli 时加上 --nodown，使其不用每次跑都新建容器，只在第一次跑的时候会初始化容器，后面都进行复用，这样速度会快很多。
 
-成功启动后就欢乐奔放的玩耍吧～ Good Lucky!
\ No newline at end of file
+成功启动后就欢乐奔放的玩耍吧～ Good Luck!