fix conflict
@@ -1,4 +1,4 @@
|
|||||||

|

|
||||||
|
|
||||||
[](https://golang.org/)
|
[](https://golang.org/)
|
||||||
[](https://github.com/go-kratos/kratos/actions)
|
[](https://github.com/go-kratos/kratos/actions)
|
||||||
@@ -20,7 +20,7 @@ Kratos是[bilibili](https://www.bilibili.com)开源的一套Go微服务框架,
|
|||||||
* GRPC Warden:基于官方gRPC开发,集成[discovery](https://github.com/bilibili/discovery)服务发现,并融合P2C负载均衡;
|
* GRPC Warden:基于官方gRPC开发,集成[discovery](https://github.com/bilibili/discovery)服务发现,并融合P2C负载均衡;
|
||||||
* Cache:优雅的接口化设计,非常方便的缓存序列化,推荐结合代理模式[overlord](https://github.com/bilibili/overlord);
|
* Cache:优雅的接口化设计,非常方便的缓存序列化,推荐结合代理模式[overlord](https://github.com/bilibili/overlord);
|
||||||
* Database:集成MySQL/HBase/TiDB,添加熔断保护和统计支持,可快速发现数据层压力;
|
* 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实现远程日志管理;
|
* Log:类似[zap](https://github.com/uber-go/zap)的field实现高性能日志库,并结合log-agent实现远程日志管理;
|
||||||
* Trace:基于opentracing,集成了全链路trace支持(gRPC/HTTP/MySQL/Redis/Memcached);
|
* Trace:基于opentracing,集成了全链路trace支持(gRPC/HTTP/MySQL/Redis/Memcached);
|
||||||
* Kratos Tool:工具链,可快速生成标准项目,或者通过Protobuf生成代码,非常便捷使用gRPC、HTTP、swagger文档;
|
* Kratos Tool:工具链,可快速生成标准项目,或者通过Protobuf生成代码,非常便捷使用gRPC、HTTP、swagger文档;
|
||||||
@@ -50,12 +50,12 @@ go build
|
|||||||
|
|
||||||
打开浏览器访问:[http://localhost:8000/kratos-demo/start](http://localhost:8000/kratos-demo/start),你会看到输出了`Golang 大法好 !!!`
|
打开浏览器访问:[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
|
## Documentation
|
||||||
|
|
||||||
> [简体中文](doc/wiki-cn/summary.md)
|
> [简体中文](https://go-kratos.github.io/kratos)
|
||||||
> [FAQ](doc/wiki-cn/FAQ.md)
|
> [FAQ](https://go-kratos.github.io/kratos/#/FAQ)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
Kratos is under the MIT license. See the [LICENSE](./LICENSE) file for details.
|
Kratos is under the MIT license. See the [LICENSE](./LICENSE) file for details.
|
||||||
|
|||||||
@@ -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)
|
|
||||||
@@ -1,40 +1,36 @@
|
|||||||
# Kratos
|

|
||||||
|
# Kratos
|
||||||
Kratos是bilibili开源的一套Go微服务框架,包含大量微服务相关框架及工具。
|
|
||||||
|
Kratos是bilibili开源的一套Go微服务框架,包含大量微服务相关框架及工具。
|
||||||
### Goals
|
|
||||||
|
### Goals
|
||||||
我们致力于提供完整的微服务研发体验,整合相关框架及工具后,微服务治理相关部分可对整体业务开发周期无感,从而更加聚焦于业务交付。对每位开发者而言,整套Kratos框架也是不错的学习仓库,可以了解和参考到bilibili在微服务方面的技术积累和经验。
|
|
||||||
|
我们致力于提供完整的微服务研发体验,整合相关框架及工具后,微服务治理相关部分可对整体业务开发周期无感,从而更加聚焦于业务交付。对每位开发者而言,整套Kratos框架也是不错的学习仓库,可以了解和参考到bilibili在微服务方面的技术积累和经验。
|
||||||
### Principles
|
|
||||||
|
### Principles
|
||||||
* 简单:不过度设计,代码平实简单
|
|
||||||
* 通用:通用业务开发所需要的基础库的功能
|
* 简单:不过度设计,代码平实简单
|
||||||
* 高效:提高业务迭代的效率
|
* 通用:通用业务开发所需要的基础库的功能
|
||||||
* 稳定:基础库可测试性高,覆盖率高,有线上实践安全可靠
|
* 高效:提高业务迭代的效率
|
||||||
* 健壮:通过良好的基础库设计,减少错用
|
* 稳定:基础库可测试性高,覆盖率高,有线上实践安全可靠
|
||||||
* 高性能:性能高,但不特定为了性能做hack优化,引入unsafe
|
* 健壮:通过良好的基础库设计,减少错用
|
||||||
* 扩展性:良好的接口设计,来扩展实现,或者通过新增基础库目录来扩展功能
|
* 高性能:性能高,但不特定为了性能做hack优化,引入unsafe
|
||||||
* 容错性:为失败设计,大量引入对SRE的理解,鲁棒性高
|
* 扩展性:良好的接口设计,来扩展实现,或者通过新增基础库目录来扩展功能
|
||||||
* 工具链:包含大量工具链,比如cache代码生成,lint工具等等
|
* 容错性:为失败设计,大量引入对SRE的理解,鲁棒性高
|
||||||
|
* 工具链:包含大量工具链,比如cache代码生成,lint工具等等
|
||||||
### Features
|
|
||||||
* HTTP Blademaster:核心基于[gin](https://github.com/gin-gonic/gin)进行模块化设计,简单易用、核心足够轻量;
|
### Features
|
||||||
* GRPC Warden:基于官方gRPC开发,集成[discovery](https://github.com/bilibili/discovery)服务发现,并融合P2C负载均衡;
|
* HTTP Blademaster:核心基于[gin](https://github.com/gin-gonic/gin)进行模块化设计,简单易用、核心足够轻量;
|
||||||
* Cache:优雅的接口化设计,非常方便的缓存序列化,推荐结合代理模式[overlord](https://github.com/bilibili/overlord);
|
* GRPC Warden:基于官方gRPC开发,集成[discovery](https://github.com/bilibili/discovery)服务发现,并融合P2C负载均衡;
|
||||||
* Database:集成MySQL/HBase/TiDB,添加熔断保护和统计支持,可快速发现数据层压力;
|
* Cache:优雅的接口化设计,非常方便的缓存序列化,推荐结合代理模式[overlord](https://github.com/bilibili/overlord);
|
||||||
* Config:方便易用的[paladin sdk](config-paladin.md),可配合远程配置中心,实现配置版本管理和更新;
|
* Database:集成MySQL/HBase/TiDB,添加熔断保护和统计支持,可快速发现数据层压力;
|
||||||
* Log:类似[zap](https://github.com/uber-go/zap)的field实现高性能日志库,并结合log-agent实现远程日志管理;
|
* Config:方便易用的[paladin sdk](config-paladin.md),可配合远程配置中心,实现配置版本管理和更新;
|
||||||
* Trace:基于opentracing,集成了全链路trace支持(gRPC/HTTP/MySQL/Redis/Memcached);
|
* Log:类似[zap](https://github.com/uber-go/zap)的field实现高性能日志库,并结合log-agent实现远程日志管理;
|
||||||
* Kratos Tool:工具链,可快速生成标准项目,或者通过Protobuf生成代码,非常便捷使用gRPC、HTTP、swagger文档;
|
* Trace:基于opentracing,集成了全链路trace支持(gRPC/HTTP/MySQL/Redis/Memcached);
|
||||||
|
* Kratos Tool:工具链,可快速生成标准项目,或者通过Protobuf生成代码,非常便捷使用gRPC、HTTP、swagger文档;
|
||||||
|
|
||||||
<br>
|
|
||||||
|
-------------
|
||||||
-------------
|
|
||||||
|
> 名字来源于:《战神》游戏以希腊神话为背景,讲述由凡人成为战神的奎托斯(Kratos)成为战神并展开弑神屠杀的冒险历程。
|
||||||
> 名字来源于:《战神》游戏以希腊神话为背景,讲述由凡人成为战神的奎托斯(Kratos)成为战神并展开弑神屠杀的冒险历程。
|
|
||||||
|
|
||||||
-------------
|
|
||||||
|
|
||||||
[文档目录树](summary.md)
|
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
# Summary
|
|
||||||
|
|
||||||
* [介绍](README.md)
|
* [介绍](README.md)
|
||||||
* [快速开始](quickstart.md)
|
* [快速开始 - 项目初始化](quickstart.md)
|
||||||
* [案例](https://github.com/go-kratos/kratos-demo)
|
* [FAQ](FAQ.md)
|
||||||
* [http blademaster](blademaster.md)
|
* [http blademaster](blademaster.md)
|
||||||
* [bm quickstart](blademaster-quickstart.md)
|
* [bm quickstart](blademaster-quickstart.md)
|
||||||
* [bm module](blademaster-mod.md)
|
* [bm module](blademaster-mod.md)
|
||||||
@@ -158,7 +158,7 @@ e.POST("/api", cors, myHandler)
|
|||||||
|
|
||||||
## 自适应限流
|
## 自适应限流
|
||||||
|
|
||||||
更多关于自适应限流的信息可参考:[kratos 自适应限流](/doc/wiki-cn/ratelimit.md)。如要使用如下:
|
更多关于自适应限流的信息可参考:[kratos 自适应限流](ratelimit.md)。如要使用如下:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
e := bm.DefaultServer(nil)
|
e := bm.DefaultServer(nil)
|
||||||
@@ -175,6 +175,3 @@ e.GET("/api", csrf, myHandler)
|
|||||||
[bm模块说明](blademaster-mod.md)
|
[bm模块说明](blademaster-mod.md)
|
||||||
[bm基于pb生成](blademaster-pb.md)
|
[bm基于pb生成](blademaster-pb.md)
|
||||||
|
|
||||||
-------------
|
|
||||||
|
|
||||||
[文档目录树](summary.md)
|
|
||||||
@@ -72,7 +72,7 @@ func (c *Context) Protobuf(data proto.Message, err error)
|
|||||||
|
|
||||||
# Handler
|
# Handler
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
初次接触`blademaster`的用户可能会对其`Handler`的流程处理产生不小的疑惑,实际上`bm`对`Handler`对处理非常简单:
|
初次接触`blademaster`的用户可能会对其`Handler`的流程处理产生不小的疑惑,实际上`bm`对`Handler`对处理非常简单:
|
||||||
|
|
||||||
@@ -86,6 +86,3 @@ func (c *Context) Protobuf(data proto.Message, err error)
|
|||||||
[bm中间件](blademaster-mid.md)
|
[bm中间件](blademaster-mid.md)
|
||||||
[bm基于pb生成](blademaster-pb.md)
|
[bm基于pb生成](blademaster-pb.md)
|
||||||
|
|
||||||
-------------
|
|
||||||
|
|
||||||
[文档目录树](summary.md)
|
|
||||||
@@ -81,6 +81,3 @@ kratos tool swagger serve api/api.swagger.json
|
|||||||
[bm模块说明](blademaster-mod.md)
|
[bm模块说明](blademaster-mod.md)
|
||||||
[bm中间件](blademaster-mid.md)
|
[bm中间件](blademaster-mid.md)
|
||||||
|
|
||||||
-------------
|
|
||||||
|
|
||||||
[文档目录树](summary.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
|
```go
|
||||||
engine = bm.DefaultServer(hc.Server)
|
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中间件](blademaster-mid.md)
|
||||||
[bm基于pb生成](blademaster-pb.md)
|
[bm基于pb生成](blademaster-pb.md)
|
||||||
|
|
||||||
-------------
|
|
||||||
|
|
||||||
[文档目录树](summary.md)
|
|
||||||
@@ -21,12 +21,12 @@
|
|||||||
|
|
||||||
# blademaster架构
|
# blademaster架构
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
`blademaster`由几个非常精简的内部模块组成。其中`Router`用于根据请求的路径分发请求,`Context`包含了一个完整的请求信息,`Handler`则负责处理传入的`Context`,`Handlers`为一个列表,一个串一个地执行。
|
`blademaster`由几个非常精简的内部模块组成。其中`Router`用于根据请求的路径分发请求,`Context`包含了一个完整的请求信息,`Handler`则负责处理传入的`Context`,`Handlers`为一个列表,一个串一个地执行。
|
||||||
所有的`middlerware`均以`Handler`的形式存在,这样可以保证`blademaster`自身足够精简且扩展性足够强。
|
所有的`middlerware`均以`Handler`的形式存在,这样可以保证`blademaster`自身足够精简且扩展性足够强。
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
`blademaster`处理请求的模式非常简单,大部分的逻辑都被封装在了各种`Handler`中。一般而言,业务逻辑作为最后一个`Handler`。
|
`blademaster`处理请求的模式非常简单,大部分的逻辑都被封装在了各种`Handler`中。一般而言,业务逻辑作为最后一个`Handler`。
|
||||||
|
|
||||||
@@ -41,6 +41,3 @@
|
|||||||
[bm中间件](blademaster-mid.md)
|
[bm中间件](blademaster-mid.md)
|
||||||
[bm基于pb生成](blademaster-pb.md)
|
[bm基于pb生成](blademaster-pb.md)
|
||||||
|
|
||||||
-------------
|
|
||||||
|
|
||||||
[文档目录树](summary.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
|
```toml
|
||||||
[Client]
|
[Client]
|
||||||
@@ -243,6 +193,3 @@ func (d *Dao) DelCacheDemo(c context.Context, id int64) (err error) {
|
|||||||
[memcache代码生成器](kratos-genmc.md)
|
[memcache代码生成器](kratos-genmc.md)
|
||||||
[redis模块说明](cache-redis.md)
|
[redis模块说明](cache-redis.md)
|
||||||
|
|
||||||
-------------
|
|
||||||
|
|
||||||
[文档目录树](summary.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
|
```toml
|
||||||
[Client]
|
[Client]
|
||||||
@@ -229,6 +179,3 @@ func (d *Dao) HGETALLDemo(c context.Context, pid int64) (res map[string]int64, e
|
|||||||
|
|
||||||
[memcache模块说明](cache-mc.md)
|
[memcache模块说明](cache-mc.md)
|
||||||
|
|
||||||
-------------
|
|
||||||
|
|
||||||
[文档目录树](summary.md)
|
|
||||||
@@ -18,6 +18,3 @@
|
|||||||
|
|
||||||
[redis模块说明](cache-redis.md)
|
[redis模块说明](cache-redis.md)
|
||||||
|
|
||||||
-------------
|
|
||||||
|
|
||||||
[文档目录树](summary.md)
|
|
||||||
@@ -46,6 +46,3 @@ Redis、MySQL等业务组件,可以使用静态的配置文件来初始化,
|
|||||||
|
|
||||||
[paladin配置sdk](config-paladin.md)
|
[paladin配置sdk](config-paladin.md)
|
||||||
|
|
||||||
-------------
|
|
||||||
|
|
||||||
[文档目录树](summary.md)
|
|
||||||
@@ -49,6 +49,3 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
-------------
|
|
||||||
|
|
||||||
[文档目录树](summary.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)
|
||||||
|
|
||||||
@@ -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
|
```toml
|
||||||
[demo]
|
[demo]
|
||||||
@@ -243,6 +193,3 @@ if err = tx.Rollback().Error; err!=nil{
|
|||||||
[tidb模块说明](database-tidb.md)
|
[tidb模块说明](database-tidb.md)
|
||||||
[hbase模块说明](database-hbase.md)
|
[hbase模块说明](database-hbase.md)
|
||||||
|
|
||||||
-------------
|
|
||||||
|
|
||||||
[文档目录树](summary.md)
|
|
||||||
@@ -17,6 +17,3 @@ HBase客户端,支持trace、slowlog和统计功能。
|
|||||||
TiDB客户端,支持服务发现和熔断功能。
|
TiDB客户端,支持服务发现和熔断功能。
|
||||||
[tidb client](database-tidb.md)
|
[tidb client](database-tidb.md)
|
||||||
|
|
||||||
-------------
|
|
||||||
|
|
||||||
[文档目录树](summary.md)
|
|
||||||
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 661 KiB After Width: | Height: | Size: 661 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
@@ -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.jsdelivr.net/npm/docsify/lib/themes/vue.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script src="//cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js"></script>
|
||||||
|
<script src="//cdn.jsdelivr.net/npm/docsify/lib/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>
|
||||||
@@ -64,6 +64,3 @@ go build
|
|||||||
|
|
||||||
[kratos工具](kratos-tool.md)
|
[kratos工具](kratos-tool.md)
|
||||||
|
|
||||||
-------------
|
|
||||||
|
|
||||||
[文档目录树](summary.md)
|
|
||||||
@@ -25,6 +25,3 @@ type _bts interface {
|
|||||||
|
|
||||||
也可以参考完整的testdata例子:kratos/tool/kratos-gen-bts/testdata
|
也可以参考完整的testdata例子:kratos/tool/kratos-gen-bts/testdata
|
||||||
|
|
||||||
-------------
|
|
||||||
|
|
||||||
[文档目录树](summary.md)
|
|
||||||
@@ -66,6 +66,3 @@ func noneKey() string {
|
|||||||
|
|
||||||
也可以参考完整的testdata例子:kratos/tool/kratos-gen-mc/testdata
|
也可以参考完整的testdata例子:kratos/tool/kratos-gen-mc/testdata
|
||||||
|
|
||||||
-------------
|
|
||||||
|
|
||||||
[文档目录树](summary.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
|
protoc --proto_path=$GOPATH --proto_path=$GOPATH/github.com/go-kratos/kratos/third_party --proto_path=. --ecode_out=:. api.proto
|
||||||
```
|
```
|
||||||
|
|
||||||
-------------
|
|
||||||
|
|
||||||
[文档目录树](summary.md)
|
|
||||||
@@ -6,6 +6,3 @@ kratos tool swagger serve api/api.swagger.json
|
|||||||
同时也可以查看更多的 [go-swagger](https://github.com/go-swagger/go-swagger) 官方参数进行使用。
|
同时也可以查看更多的 [go-swagger](https://github.com/go-swagger/go-swagger) 官方参数进行使用。
|
||||||
|
|
||||||
|
|
||||||
-------------
|
|
||||||
|
|
||||||
[文档目录树](summary.md)
|
|
||||||
@@ -92,8 +92,6 @@ swagger(已安装): swagger api文档 Author(goswagger.io) [2019/05/05]
|
|||||||
执行工具: kratos tool demo
|
执行工具: kratos tool demo
|
||||||
安装全部工具: kratos tool install all
|
安装全部工具: kratos tool install all
|
||||||
全部升级: kratos tool upgrade all
|
全部升级: kratos tool upgrade all
|
||||||
|
|
||||||
详细文档: https://github.com/go-kratos/kratos/blob/master/doc/wiki-cn/kratos-tool.md
|
|
||||||
```
|
```
|
||||||
|
|
||||||
> 小小说明:如未安装工具,第一次运行也可自动安装,不需要特别执行install
|
> 小小说明:如未安装工具,第一次运行也可自动安装,不需要特别执行install
|
||||||
@@ -106,6 +104,3 @@ swagger(已安装): swagger api文档 Author(goswagger.io) [2019/05/05]
|
|||||||
* [genmc](kratos-genmc.md) 用于自动生成memcached缓存代码;
|
* [genmc](kratos-genmc.md) 用于自动生成memcached缓存代码;
|
||||||
* [genbts](kratos-genbts.md) 用于生成缓存回源代码生成,如果miss则调用回源函数从数据源获取,然后塞入缓存;
|
* [genbts](kratos-genbts.md) 用于生成缓存回源代码生成,如果miss则调用回源函数从数据源获取,然后塞入缓存;
|
||||||
|
|
||||||
-------------
|
|
||||||
|
|
||||||
[文档目录树](summary.md)
|
|
||||||
@@ -32,6 +32,3 @@ func main() {
|
|||||||
## 扩展阅读
|
## 扩展阅读
|
||||||
* [log-agent](log-agent.md)
|
* [log-agent](log-agent.md)
|
||||||
|
|
||||||
-------------
|
|
||||||
|
|
||||||
[文档目录树](summary.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`的工具生成代码。
|
请注意替换`/Users/felix/work/go/src`目录为你本地开发环境对应GOPATH目录,其中`--gogofast_out`意味着告诉`protoc`工具需要使用`gogo protobuf`的工具生成代码。
|
||||||
|
|
||||||
-------------
|
|
||||||
|
|
||||||
[文档目录树](summary.md)
|
|
||||||
@@ -67,6 +67,3 @@ go build
|
|||||||
|
|
||||||
[kratos工具](kratos-tool.md)
|
[kratos工具](kratos-tool.md)
|
||||||
|
|
||||||
-------------
|
|
||||||
|
|
||||||
[文档目录树](summary.md)
|
|
||||||
@@ -25,7 +25,7 @@ kratos 借鉴了 Sentinel 项目的自适应限流系统,通过综合分析服
|
|||||||
|
|
||||||
在自适应限流保护中,采集到的指标的时效性非常强,系统只需要采集最近一小段时间内的 qps、rt 即可,对于较老的数据,会自动丢弃。为了实现这个效果,kratos 使用了滑动窗口来保存采样数据。
|
在自适应限流保护中,采集到的指标的时效性非常强,系统只需要采集最近一小段时间内的 qps、rt 即可,对于较老的数据,会自动丢弃。为了实现这个效果,kratos 使用了滑动窗口来保存采样数据。
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
如上图,展示了一个具有两个桶(bucket)的滑动窗口(rolling window)。整个滑动窗口用来保存最近 1s 的采样数据,每个小的桶用来保存 500ms 的采样数据。
|
如上图,展示了一个具有两个桶(bucket)的滑动窗口(rolling window)。整个滑动窗口用来保存最近 1s 的采样数据,每个小的桶用来保存 500ms 的采样数据。
|
||||||
当时间流动之后,过期的桶会自动被新桶的数据覆盖掉,在图中,在 1000-1500ms 时,bucket 1 的数据因为过期而被丢弃,之后 bucket 3 的数据填到了窗口的头部。
|
当时间流动之后,过期的桶会自动被新桶的数据覆盖掉,在图中,在 1000-1500ms 时,bucket 1 的数据因为过期而被丢弃,之后 bucket 3 的数据填到了窗口的头部。
|
||||||
@@ -45,7 +45,7 @@ windows 表示一秒内采样窗口的数量,默认配置中是 5s 50 个采
|
|||||||
|
|
||||||
场景1,请求以每秒增加1个的速度不停上升,压测效果如下:
|
场景1,请求以每秒增加1个的速度不停上升,压测效果如下:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
左测是没有限流的压测效果,右侧是带限流的压测效果。
|
左测是没有限流的压测效果,右侧是带限流的压测效果。
|
||||||
可以看到,没有限流的场景里,系统在 700qps 时开始抖动,在 1k qps 时被拖垮,几乎没有新的请求能被放行,然而在使用限流之后,系统请求能够稳定在 600 qps 左右,rt 没有暴增,服务也没有被打垮,可见,限流有效的保护了服务。
|
可以看到,没有限流的场景里,系统在 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)
|
[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)
|
|
||||||
@@ -39,4 +39,4 @@ func main(){
|
|||||||
|
|
||||||
### zipkin效果图
|
### zipkin效果图
|
||||||
|
|
||||||

|

|
||||||
@@ -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])
|
1. 每个方法至少包含一个测试方法(命名为Test[PackageName][FunctionName])
|
||||||
@@ -169,6 +169,7 @@ func TestUsers(t *testing.T) {
|
|||||||
#### 使用示例
|
#### 使用示例
|
||||||
1. 上层包对下层包依赖示例
|
1. 上层包对下层包依赖示例
|
||||||
Service层对Dao层依赖:
|
Service层对Dao层依赖:
|
||||||
|
|
||||||
```GO
|
```GO
|
||||||
// 原方法
|
// 原方法
|
||||||
func (s *Service) realnameAlipayApply(c context.Context, mid int64) (info *model.RealnameAlipayApply, err error) {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 测试方法
|
// 测试方法
|
||||||
func TestServicerealnameAlipayApply(t *testing.T) {
|
func TestServicerealnameAlipayApply(t *testing.T) {
|
||||||
convey.Convey("realnameAlipayApply", t, func(ctx convey.C) {
|
convey.Convey("realnameAlipayApply", t, func(ctx convey.C) {
|
||||||
@@ -198,8 +199,8 @@ func TestServicerealnameAlipayApply(t *testing.T) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
2. 基础库错误Mock示例
|
2. 基础库错误Mock示例
|
||||||
|
|
||||||
```Go
|
```Go
|
||||||
|
|
||||||
// 原方法(部分)
|
// 原方法(部分)
|
||||||
func (d *Dao) BaseInfoCache(c context.Context, mid int64) (info *model.BaseInfo, err error) {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 测试方法(错误Mock部分)
|
// 测试方法(错误Mock部分)
|
||||||
func TestDaoBaseInfoCache(t *testing.T) {
|
func TestDaoBaseInfoCache(t *testing.T) {
|
||||||
convey.Convey("BaseInfoCache", t, func(ctx convey.C) {
|
convey.Convey("BaseInfoCache", t, func(ctx convey.C) {
|
||||||
@@ -232,6 +232,7 @@ func TestDaoBaseInfoCache(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 注意事项
|
#### 注意事项
|
||||||
- Monkey非线程安全
|
- Monkey非线程安全
|
||||||
- Monkey无法针对Inline方法打补丁,在测试时可以使用go test -gcflags=-l来关闭inline编译的模式(一些简单的go inline介绍戳这里)
|
- Monkey无法针对Inline方法打补丁,在测试时可以使用go test -gcflags=-l来关闭inline编译的模式(一些简单的go inline介绍戳这里)
|
||||||
@@ -269,9 +270,10 @@ func TestDaoBaseInfoCache(t *testing.T) {
|
|||||||
|
|
||||||
使用示例:
|
使用示例:
|
||||||
1. net/http 标准库 HTTP 请求Mock
|
1. net/http 标准库 HTTP 请求Mock
|
||||||
|
|
||||||
```Go
|
```Go
|
||||||
import gock "gopkg.in/h2non/gock.v1"
|
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) {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 测试方法
|
// 测试方法
|
||||||
func TestDaoUpload(t *testing.T) {
|
func TestDaoUpload(t *testing.T) {
|
||||||
convey.Convey("Upload", t, func(ctx convey.C) {
|
convey.Convey("Upload", t, func(ctx convey.C) {
|
||||||
@@ -348,6 +349,7 @@ func httpMock(method, url string) *gock.Request {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
2. blademaster库HTTP请求Mock
|
2. blademaster库HTTP请求Mock
|
||||||
|
|
||||||
```Go
|
```Go
|
||||||
// 原方法
|
// 原方法
|
||||||
func (d *Dao) SendWechatToGroup(c context.Context, chatid, msg string) (err error) {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 测试方法
|
// 测试方法
|
||||||
func TestDaoSendWechatToGroup(t *testing.T) {
|
func TestDaoSendWechatToGroup(t *testing.T) {
|
||||||
convey.Convey("SendWechatToGroup", t, func(ctx convey.C) {
|
convey.Convey("SendWechatToGroup", t, func(ctx convey.C) {
|
||||||
@@ -393,7 +395,7 @@ func TestDaoSendWechatToGroup(t *testing.T) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func httpMock(method, url string) *gock.Request {
|
func httpMock(method, url string) *gock.Request {
|
||||||
r := gock.New(url)
|
r := gock.New(url)
|
||||||
r.Method = strings.ToUpper(method)
|
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
|
./creater --m mock absolute/path/to/your/pkg
|
||||||
```
|
```
|
||||||
- 测试代码内使用方法
|
- 测试代码内使用方法
|
||||||
|
|
||||||
```Go
|
```Go
|
||||||
// 测试用例内直接使用
|
// 测试用例内直接使用
|
||||||
// 需引入的包
|
// 需引入的包
|
||||||
@@ -464,7 +467,7 @@ func TestPkgFoo(t *testing.T) {
|
|||||||
...
|
...
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 可以利用Convey执行顺序方式适当调整以简化代码
|
// 可以利用Convey执行顺序方式适当调整以简化代码
|
||||||
func TestPkgFoo(t *testing.T) {
|
func TestPkgFoo(t *testing.T) {
|
||||||
convey.Convey("Foo", t, func(ctx convey.C) {
|
convey.Convey("Foo", t, func(ctx convey.C) {
|
||||||
@@ -148,7 +148,5 @@ func TestMain(m *testing.M) {
|
|||||||
```
|
```
|
||||||
## 注意
|
## 注意
|
||||||
因为启动mysql容器较为缓慢,健康检测的机制会重试3次,每次暂留5秒钟,基本在10s内mysql就能从creating到服务正常启动!
|
因为启动mysql容器较为缓慢,健康检测的机制会重试3次,每次暂留5秒钟,基本在10s内mysql就能从creating到服务正常启动!
|
||||||
|
|
||||||
当然你也可以在使用 testcli 时加上 --nodown,使其不用每次跑都新建容器,只在第一次跑的时候会初始化容器,后面都进行复用,这样速度会快很多。
|
当然你也可以在使用 testcli 时加上 --nodown,使其不用每次跑都新建容器,只在第一次跑的时候会初始化容器,后面都进行复用,这样速度会快很多。
|
||||||
|
|
||||||
成功启动后就欢乐奔放的玩耍吧~ Good Luck!
|
|
||||||
@@ -43,10 +43,3 @@ $GOPATH/bin/testgen --m mock YOUR_PROJECT/dao #仅传入包路径即可
|
|||||||
```shell
|
```shell
|
||||||
$GOPATH/bin/testgen --m monkey yourCodeDirPath #仅传入包路径即可
|
$GOPATH/bin/testgen --m monkey yourCodeDirPath #仅传入包路径即可
|
||||||
```
|
```
|
||||||
### 赋诗一首
|
|
||||||
```
|
|
||||||
莫生气 莫生气
|
|
||||||
代码辣鸡非我意
|
|
||||||
自己动手分田地
|
|
||||||
谈笑风生活长命
|
|
||||||
```
|
|
||||||
@@ -30,9 +30,3 @@ golang 的单元测试,既可以用官方自带的 testing 包,也有开源
|
|||||||
> - 基础组件,如mc、redis、mysql等,由 testcli(testing/lich) 起基础镜像支持(需要提供建表、INSERT语句)与本地开发环境一致,也保证了结果的一致性。
|
> - 基础组件,如mc、redis、mysql等,由 testcli(testing/lich) 起基础镜像支持(需要提供建表、INSERT语句)与本地开发环境一致,也保证了结果的一致性。
|
||||||
> - rpc server,如 xxxx-service 需要定义 interface 供业务依赖方使用。所有rpc server 都必须要提供一个interface+mock代码(gomock)。
|
> - rpc server,如 xxxx-service 需要定义 interface 供业务依赖方使用。所有rpc server 都必须要提供一个interface+mock代码(gomock)。
|
||||||
> - http server则直接写mock代码gock。
|
> - http server则直接写mock代码gock。
|
||||||
|
|
||||||
# 注意
|
|
||||||
单元测试极其重要,良好的单元测试习惯能很大程度上避免代码变更引起的bug!
|
|
||||||
单元测试极其重要,良好的单元测试习惯能很大程度上避免代码变更引起的bug!
|
|
||||||
单元测试极其重要,良好的单元测试习惯能很大程度上避免代码变更引起的bug!
|
|
||||||
以为很重要所以重复 3 遍~
|
|
||||||
@@ -37,7 +37,3 @@ func NewClient(conf *ClientConfig, opt ...grpc.DialOption) *Client {
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
-------------
|
|
||||||
|
|
||||||
[文档目录树](summary.md)
|
|
||||||
@@ -324,7 +324,7 @@ func serverLogging() grpc.UnaryServerInterceptor {
|
|||||||
|
|
||||||
## 自适应限流拦截器
|
## 自适应限流拦截器
|
||||||
|
|
||||||
更多关于自适应限流的信息,请参考:[kratos 自适应限流](/doc/wiki-cn/ratelimit.md)
|
更多关于自适应限流的信息,请参考:[kratos 自适应限流](ratelimit.md)
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package grpc
|
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)
|
[warden快速开始](warden-quickstart.md)
|
||||||
|
[warden基于pb生成](warden-pb.md)
|
||||||
-------------
|
[warden负载均衡](warden-balancer.md)
|
||||||
|
[warden服务发现](warden-resolver.md)
|
||||||
[文档目录树](summary.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)
|
|
||||||
@@ -169,7 +169,3 @@ func (d *Dao) SayHello(c context.Context, req *demoapi.HelloReq) (resp *grpcempt
|
|||||||
[warden基于pb生成](warden-pb.md)
|
[warden基于pb生成](warden-pb.md)
|
||||||
[warden服务发现](warden-resolver.md)
|
[warden服务发现](warden-resolver.md)
|
||||||
[warden负载均衡](warden-balancer.md)
|
[warden负载均衡](warden-balancer.md)
|
||||||
|
|
||||||
-------------
|
|
||||||
|
|
||||||
[文档目录树](summary.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)
|
[warden快速开始](warden-quickstart.md)
|
||||||
|
[warden拦截器](warden-mid.md)
|
||||||
-------------
|
[warden基于pb生成](warden-pb.md)
|
||||||
|
[warden负载均衡](warden-balancer.md)
|
||||||
[文档目录树](summary.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)
|
|
||||||
@@ -20,6 +20,9 @@ func formatErr(err error, name, addr string) string {
|
|||||||
case strings.HasPrefix(es, "read"):
|
case strings.HasPrefix(es, "read"):
|
||||||
return "read timeout"
|
return "read timeout"
|
||||||
case strings.HasPrefix(es, "dial"):
|
case strings.HasPrefix(es, "dial"):
|
||||||
|
if strings.Contains(es, "connection refused") {
|
||||||
|
return "connection refused"
|
||||||
|
}
|
||||||
return "dial timeout"
|
return "dial timeout"
|
||||||
case strings.HasPrefix(es, "write"):
|
case strings.HasPrefix(es, "write"):
|
||||||
return "write timeout"
|
return "write timeout"
|
||||||
@@ -29,6 +32,10 @@ func formatErr(err error, name, addr string) string {
|
|||||||
return "reset"
|
return "reset"
|
||||||
case strings.Contains(es, "broken"):
|
case strings.Contains(es, "broken"):
|
||||||
return "broken pipe"
|
return "broken pipe"
|
||||||
|
case strings.Contains(es, "pool exhausted"):
|
||||||
|
return "pool exhausted"
|
||||||
|
case strings.Contains(es, "pool closed"):
|
||||||
|
return "pool closed"
|
||||||
default:
|
default:
|
||||||
return "unexpected err"
|
return "unexpected err"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,13 +22,14 @@ var _internalTags = []trace.Tag{
|
|||||||
}
|
}
|
||||||
|
|
||||||
type traceConn struct {
|
type traceConn struct {
|
||||||
// tr for pipeline, if tr != nil meaning on pipeline
|
// tr parent trace.
|
||||||
tr trace.Trace
|
tr trace.Trace
|
||||||
|
// trPipe for pipeline, if trPipe != nil meaning on pipeline.
|
||||||
|
trPipe trace.Trace
|
||||||
|
|
||||||
// connTag include e.g. ip,port
|
// connTag include e.g. ip,port
|
||||||
connTags []trace.Tag
|
connTags []trace.Tag
|
||||||
|
|
||||||
ctx context.Context
|
|
||||||
|
|
||||||
// origin redis conn
|
// origin redis conn
|
||||||
Conn
|
Conn
|
||||||
pending int
|
pending int
|
||||||
@@ -39,9 +40,15 @@ type traceConn struct {
|
|||||||
func (t *traceConn) Do(commandName string, args ...interface{}) (reply interface{}, err error) {
|
func (t *traceConn) Do(commandName string, args ...interface{}) (reply interface{}, err error) {
|
||||||
statement := getStatement(commandName, args...)
|
statement := getStatement(commandName, args...)
|
||||||
defer t.slowLog(statement, time.Now())
|
defer t.slowLog(statement, time.Now())
|
||||||
|
|
||||||
// NOTE: ignored empty commandName
|
// NOTE: ignored empty commandName
|
||||||
// current sdk will Do empty command after pipeline finished
|
// 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...)
|
return t.Conn.Do(commandName, args...)
|
||||||
}
|
}
|
||||||
tr := t.tr.Fork("", "Redis:"+commandName)
|
tr := t.tr.Fork("", "Redis:"+commandName)
|
||||||
@@ -60,18 +67,19 @@ func (t *traceConn) Send(commandName string, args ...interface{}) (err error) {
|
|||||||
if t.tr == nil {
|
if t.tr == nil {
|
||||||
return t.Conn.Send(commandName, args...)
|
return t.Conn.Send(commandName, args...)
|
||||||
}
|
}
|
||||||
if t.pending == 1 {
|
|
||||||
t.tr = t.tr.Fork("", "Redis:Pipeline")
|
if t.trPipe == nil {
|
||||||
t.tr.SetTag(_internalTags...)
|
t.trPipe = t.tr.Fork("", "Redis:Pipeline")
|
||||||
t.tr.SetTag(t.connTags...)
|
t.trPipe.SetTag(_internalTags...)
|
||||||
|
t.trPipe.SetTag(t.connTags...)
|
||||||
}
|
}
|
||||||
t.tr.SetLog(
|
t.trPipe.SetLog(
|
||||||
trace.Log(trace.LogEvent, "Send"),
|
trace.Log(trace.LogEvent, "Send"),
|
||||||
trace.Log("db.statement", statement),
|
trace.Log("db.statement", statement),
|
||||||
)
|
)
|
||||||
if err = t.Conn.Send(commandName, args...); err != nil {
|
if err = t.Conn.Send(commandName, args...); err != nil {
|
||||||
t.tr.SetTag(trace.TagBool(trace.TagError, true))
|
t.trPipe.SetTag(trace.TagBool(trace.TagError, true))
|
||||||
t.tr.SetLog(
|
t.trPipe.SetLog(
|
||||||
trace.Log(trace.LogEvent, "Send Fail"),
|
trace.Log(trace.LogEvent, "Send Fail"),
|
||||||
trace.Log(trace.LogMessage, err.Error()),
|
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 {
|
func (t *traceConn) Flush() error {
|
||||||
defer t.slowLog("Flush", time.Now())
|
defer t.slowLog("Flush", time.Now())
|
||||||
if t.tr == nil {
|
if t.trPipe == nil {
|
||||||
return t.Conn.Flush()
|
return t.Conn.Flush()
|
||||||
}
|
}
|
||||||
t.tr.SetLog(trace.Log(trace.LogEvent, "Flush"))
|
t.trPipe.SetLog(trace.Log(trace.LogEvent, "Flush"))
|
||||||
err := t.Conn.Flush()
|
err := t.Conn.Flush()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.tr.SetTag(trace.TagBool(trace.TagError, true))
|
t.trPipe.SetTag(trace.TagBool(trace.TagError, true))
|
||||||
t.tr.SetLog(
|
t.trPipe.SetLog(
|
||||||
trace.Log(trace.LogEvent, "Flush Fail"),
|
trace.Log(trace.LogEvent, "Flush Fail"),
|
||||||
trace.Log(trace.LogMessage, err.Error()),
|
trace.Log(trace.LogMessage, err.Error()),
|
||||||
)
|
)
|
||||||
@@ -98,14 +106,14 @@ func (t *traceConn) Flush() error {
|
|||||||
|
|
||||||
func (t *traceConn) Receive() (reply interface{}, err error) {
|
func (t *traceConn) Receive() (reply interface{}, err error) {
|
||||||
defer t.slowLog("Receive", time.Now())
|
defer t.slowLog("Receive", time.Now())
|
||||||
if t.tr == nil {
|
if t.trPipe == nil {
|
||||||
return t.Conn.Receive()
|
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()
|
reply, err = t.Conn.Receive()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.tr.SetTag(trace.TagBool(trace.TagError, true))
|
t.trPipe.SetTag(trace.TagBool(trace.TagError, true))
|
||||||
t.tr.SetLog(
|
t.trPipe.SetLog(
|
||||||
trace.Log(trace.LogEvent, "Receive Fail"),
|
trace.Log(trace.LogEvent, "Receive Fail"),
|
||||||
trace.Log(trace.LogMessage, err.Error()),
|
trace.Log(trace.LogMessage, err.Error()),
|
||||||
)
|
)
|
||||||
@@ -114,8 +122,8 @@ func (t *traceConn) Receive() (reply interface{}, err error) {
|
|||||||
t.pending--
|
t.pending--
|
||||||
}
|
}
|
||||||
if t.pending == 0 {
|
if t.pending == 0 {
|
||||||
t.tr.Finish(nil)
|
t.trPipe.Finish(nil)
|
||||||
t.tr = nil
|
t.trPipe = nil
|
||||||
}
|
}
|
||||||
return reply, err
|
return reply, err
|
||||||
}
|
}
|
||||||
@@ -123,6 +131,8 @@ func (t *traceConn) Receive() (reply interface{}, err error) {
|
|||||||
func (t *traceConn) WithContext(ctx context.Context) Conn {
|
func (t *traceConn) WithContext(ctx context.Context) Conn {
|
||||||
t.Conn = t.Conn.WithContext(ctx)
|
t.Conn = t.Conn.WithContext(ctx)
|
||||||
t.tr, _ = trace.FromContext(ctx)
|
t.tr, _ = trace.FromContext(ctx)
|
||||||
|
t.pending = 0
|
||||||
|
t.trPipe = nil
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -190,3 +190,23 @@ func BenchmarkTraceConn(b *testing.B) {
|
|||||||
c2.Close()
|
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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ package warden
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
"github.com/go-kratos/kratos/pkg/ecode"
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"gopkg.in/go-playground/validator.v9"
|
"gopkg.in/go-playground/validator.v9"
|
||||||
@@ -15,7 +15,7 @@ var validate = validator.New()
|
|||||||
func (s *Server) validate() grpc.UnaryServerInterceptor {
|
func (s *Server) validate() grpc.UnaryServerInterceptor {
|
||||||
return func(ctx context.Context, req interface{}, args *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
|
return func(ctx context.Context, req interface{}, args *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
|
||||||
if err = validate.Struct(req); err != nil {
|
if err = validate.Struct(req); err != nil {
|
||||||
err = ecode.Error(ecode.RequestErr, err.Error())
|
err = status.Error(codes.InvalidArgument, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
resp, err = handler(ctx, req)
|
resp, err = handler(ctx, req)
|
||||||
@@ -30,7 +30,7 @@ func (s *Server) RegisterValidation(key string, fn validator.Func) error {
|
|||||||
return validate.RegisterValidation(key, fn)
|
return validate.RegisterValidation(key, fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
//GetValidate return the default validate
|
// GetValidate return the default validate
|
||||||
func (s *Server) GetValidate() *validator.Validate {
|
func (s *Server) GetValidate() *validator.Validate {
|
||||||
return validate
|
return validate
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
## testing/lich 运行环境构建
|
## testing/lich 运行环境构建
|
||||||
基于 docker-compose 实现跨平台跨语言环境的容器依赖管理方案,以解决运行ut场景下的 (mysql, redis, mc)容器依赖问题。
|
基于 docker-compose 实现跨平台跨语言环境的容器依赖管理方案,以解决运行ut场景下的 (mysql, redis, mc)容器依赖问题。
|
||||||
|
|
||||||
使用说明参见:https://github.com/go-kratos/kratos/tree/master/tool/testcli/README.md
|
使用说明参见:https://go-kratos.github.io/kratos/#/ut
|
||||||
@@ -17,7 +17,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
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.
|
// Tool is kratos tool.
|
||||||
|
|||||||
@@ -151,4 +151,4 @@ func TestMain(m *testing.M) {
|
|||||||
|
|
||||||
当然你也可以在使用 testcli 时加上 --nodown,使其不用每次跑都新建容器,只在第一次跑的时候会初始化容器,后面都进行复用,这样速度会快很多。
|
当然你也可以在使用 testcli 时加上 --nodown,使其不用每次跑都新建容器,只在第一次跑的时候会初始化容器,后面都进行复用,这样速度会快很多。
|
||||||
|
|
||||||
成功启动后就欢乐奔放的玩耍吧~ Good Luck!
|
成功启动后就欢乐奔放的玩耍吧~ Good Luck!
|
||||||