From d9cee15ef6288c246b9a3bbfabe84439eecb11fc Mon Sep 17 00:00:00 2001 From: Felix Hao Date: Thu, 15 Aug 2019 11:08:58 +0800 Subject: [PATCH] doc and example (#267) * add doc and example * rm deprecated ecode.Equal --- doc/wiki-cn/cache-redis.md | 2 +- doc/wiki-cn/database-mysql-orm.md | 81 +++++++++++++++++++ doc/wiki-cn/database.md | 3 +- doc/wiki-cn/kratos-genbts.md | 2 +- doc/wiki-cn/ratelimit.md | 20 ++--- doc/wiki-cn/summary.md | 1 + .../blademaster/middleware/auth/README.md | 0 .../blademaster/middleware/auth/auth.go | 0 .../middleware/auth/example_test.go | 2 +- pkg/database/sql/sql.go | 4 +- pkg/ecode/ecode.go | 6 +- pkg/ecode/status.go | 6 -- pkg/ecode/status_test.go | 3 - pkg/naming/discovery/discovery.go | 18 ++--- pkg/net/rpc/warden/client.go | 19 +++-- pkg/net/rpc/warden/server_test.go | 10 +-- 16 files changed, 125 insertions(+), 52 deletions(-) create mode 100644 doc/wiki-cn/database-mysql-orm.md rename {pkg/net/http => example}/blademaster/middleware/auth/README.md (100%) rename {pkg/net/http => example}/blademaster/middleware/auth/auth.go (100%) rename {pkg/net/http => example}/blademaster/middleware/auth/example_test.go (93%) diff --git a/doc/wiki-cn/cache-redis.md b/doc/wiki-cn/cache-redis.md index a6c08e052..8304dea0a 100644 --- a/doc/wiki-cn/cache-redis.md +++ b/doc/wiki-cn/cache-redis.md @@ -180,7 +180,7 @@ func (d *Dao) DemoIncrbys(c context.Context, pid int) (err error) { ## 返回值转换 -与[memcache包](cache-mc.md)类似地,kratos/pkg/cache/redis包中也提供了Scan方法将redis server的返回值转换为golang类型。 +kratos/pkg/cache/redis包中也提供了Scan方法将redis server的返回值转换为golang类型。 除此之外,kratos/pkg/cache/redis包提供了大量返回值转换的快捷方式: diff --git a/doc/wiki-cn/database-mysql-orm.md b/doc/wiki-cn/database-mysql-orm.md new file mode 100644 index 000000000..5b776034c --- /dev/null +++ b/doc/wiki-cn/database-mysql-orm.md @@ -0,0 +1,81 @@ +# 准备工作 + +推荐使用[kratos工具](kratos-tool.md)快速生成项目,如我们生成一个叫`kratos-demo`的项目。目录结构如下: + +``` +├── CHANGELOG.md +├── CONTRIBUTORS.md +├── LICENSE +├── README.md +├── cmd +│   ├── cmd +│   └── main.go +├── configs +│   ├── application.toml +│   ├── grpc.toml +│   ├── http.toml +│   ├── log.toml +│   ├── memcache.toml +│   ├── mysql.toml +│   └── redis.toml +├── go.mod +├── go.sum +└── internal + ├── dao + │   └── dao.go + ├── model + │   └── model.go + ├── server + │   └── http + │   └── http.go + └── service + └── service.go +``` + +# 开始使用 + +## 配置 + +创建项目成功后,进入项目中的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目录,打开dao.go,其中: + +```go +var ( + dc struct { + Demo *sql.Config + } +) +checkErr(paladin.Get("mysql.toml").UnmarshalTOML(&dc)) +``` +使用paladin配置管理工具将上文中的mysql.toml中的配置解析为我们需要使用mysql的相关配置。 + +# TODO:补充常用方法 + +# 扩展阅读 + +[tidb模块说明](database-tidb.md) +[hbase模块说明](database-hbase.md) + +------------- + +[文档目录树](summary.md) diff --git a/doc/wiki-cn/database.md b/doc/wiki-cn/database.md index 1939bccaa..61d9f81d6 100644 --- a/doc/wiki-cn/database.md +++ b/doc/wiki-cn/database.md @@ -6,7 +6,8 @@ ## MySQL MySQL数据库驱动,支持读写分离、context、timeout、trace和统计功能,以及错误熔断防止数据库雪崩。 -[mysql client](database-mysql.md) +[mysql client](database-mysql.md) +[mysql client orm](database-mysql-orm.md) ## HBase HBase客户端,支持trace、slowlog和统计功能。 diff --git a/doc/wiki-cn/kratos-genbts.md b/doc/wiki-cn/kratos-genbts.md index f9e5803d9..7c889ddc4 100644 --- a/doc/wiki-cn/kratos-genbts.md +++ b/doc/wiki-cn/kratos-genbts.md @@ -2,7 +2,7 @@ > 缓存回源代码生成 -在internal/dao/dao.go中添加mc缓存interface定义,可以指定对应的[注解参数](../../tool/kratos-gen-mc/README.md); +在internal/dao/dao.go中添加mc缓存interface定义,可以指定对应的[注解参数](../../tool/kratos-gen-bts/README.md); 并且在接口前面添加`go:generate kratos tool genbts`; 然后在当前目录执行`go generate`,可以看到自动生成的dao.bts.go代码。 diff --git a/doc/wiki-cn/ratelimit.md b/doc/wiki-cn/ratelimit.md index d6a875537..51d28a76d 100644 --- a/doc/wiki-cn/ratelimit.md +++ b/doc/wiki-cn/ratelimit.md @@ -11,17 +11,17 @@ kratos 借鉴了 Sentinel 项目的自适应限流系统,通过综合分析服 ## 限流规则 -1,指标介绍 +### 指标介绍 -|指标名称|指标含义| -|---|---| -|cpu|最近 1s 的 CPU 使用率均值,使用滑动平均计算,采样周期是 250ms| -|inflight|当前处理中正在处理的请求数量| -|pass|请求处理成功的量| -|rt|请求成功的响应耗时| +| 指标名称 | 指标含义 | +| -------- | ------------------------------------------------------------- | +| cpu | 最近 1s 的 CPU 使用率均值,使用滑动平均计算,采样周期是 250ms | +| inflight | 当前处理中正在处理的请求数量 | +| pass | 请求处理成功的量 | +| rt | 请求成功的响应耗时 | -2,滑动窗口 +### 滑动窗口 在自适应限流保护中,采集到的指标的时效性非常强,系统只需要采集最近一小段时间内的 qps、rt 即可,对于较老的数据,会自动丢弃。为了实现这个效果,kratos 使用了滑动窗口来保存采样数据。 @@ -31,7 +31,7 @@ kratos 借鉴了 Sentinel 项目的自适应限流系统,通过综合分析服 当时间流动之后,过期的桶会自动被新桶的数据覆盖掉,在图中,在 1000-1500ms 时,bucket 1 的数据因为过期而被丢弃,之后 bucket 3 的数据填到了窗口的头部。 -3,限流公式 +### 限流公式 判断是否丢弃当前请求的算法如下: @@ -51,7 +51,7 @@ windows 表示一秒内采样窗口的数量,默认配置中是 5s 50 个采 可以看到,没有限流的场景里,系统在 700qps 时开始抖动,在 1k qps 时被拖垮,几乎没有新的请求能被放行,然而在使用限流之后,系统请求能够稳定在 600 qps 左右,rt 没有暴增,服务也没有被打垮,可见,限流有效的保护了服务。 -参考资料: +## 参考资料 [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) diff --git a/doc/wiki-cn/summary.md b/doc/wiki-cn/summary.md index 5fc3d0356..4b6940647 100644 --- a/doc/wiki-cn/summary.md +++ b/doc/wiki-cn/summary.md @@ -21,6 +21,7 @@ * [log-agent](log-agent.md) * [database](database.md) * [mysql](database-mysql.md) + * [mysql-orm](database-mysql-orm.md) * [hbase](database-hbase.md) * [tidb](database-tidb.md) * [cache](cache.md) diff --git a/pkg/net/http/blademaster/middleware/auth/README.md b/example/blademaster/middleware/auth/README.md similarity index 100% rename from pkg/net/http/blademaster/middleware/auth/README.md rename to example/blademaster/middleware/auth/README.md diff --git a/pkg/net/http/blademaster/middleware/auth/auth.go b/example/blademaster/middleware/auth/auth.go similarity index 100% rename from pkg/net/http/blademaster/middleware/auth/auth.go rename to example/blademaster/middleware/auth/auth.go diff --git a/pkg/net/http/blademaster/middleware/auth/example_test.go b/example/blademaster/middleware/auth/example_test.go similarity index 93% rename from pkg/net/http/blademaster/middleware/auth/example_test.go rename to example/blademaster/middleware/auth/example_test.go index f1e20ea21..b97e22276 100644 --- a/pkg/net/http/blademaster/middleware/auth/example_test.go +++ b/example/blademaster/middleware/auth/example_test.go @@ -4,7 +4,7 @@ import ( "fmt" bm "github.com/bilibili/kratos/pkg/net/http/blademaster" - "github.com/bilibili/kratos/pkg/net/http/blademaster/middleware/auth" + "github.com/bilibili/kratos/example/blademaster/middleware/auth" "github.com/bilibili/kratos/pkg/net/metadata" ) diff --git a/pkg/database/sql/sql.go b/pkg/database/sql/sql.go index 282fe8469..a59997264 100644 --- a/pkg/database/sql/sql.go +++ b/pkg/database/sql/sql.go @@ -196,7 +196,7 @@ func (db *DB) Prepared(query string) (stmt *Stmt) { func (db *DB) Query(c context.Context, query string, args ...interface{}) (rows *Rows, err error) { idx := db.readIndex() for i := range db.read { - if rows, err = db.read[(idx+i)%len(db.read)].query(c, query, args...); !ecode.ServiceUnavailable.Equal(err) { + if rows, err = db.read[(idx+i)%len(db.read)].query(c, query, args...); !ecode.EqualError(ecode.ServiceUnavailable, err) { return } } @@ -209,7 +209,7 @@ func (db *DB) Query(c context.Context, query string, args ...interface{}) (rows func (db *DB) QueryRow(c context.Context, query string, args ...interface{}) *Row { idx := db.readIndex() for i := range db.read { - if row := db.read[(idx+i)%len(db.read)].queryRow(c, query, args...); !ecode.ServiceUnavailable.Equal(row.err) { + if row := db.read[(idx+i)%len(db.read)].queryRow(c, query, args...); !ecode.EqualError(ecode.ServiceUnavailable, row.err) { return row } } diff --git a/pkg/ecode/ecode.go b/pkg/ecode/ecode.go index 3e627970f..259424cf4 100644 --- a/pkg/ecode/ecode.go +++ b/pkg/ecode/ecode.go @@ -9,7 +9,7 @@ import ( ) var ( - _messages atomic.Value // NOTE: stored map[string]map[int]string + _messages atomic.Value // NOTE: stored map[int]string _codes = map[int]struct{}{} // register codes. ) @@ -71,10 +71,6 @@ func (e Code) Message() string { // Details return details. func (e Code) Details() []interface{} { return nil } -// Equal for compatible. -// Deprecated: please use ecode.EqualError. -func (e Code) Equal(err error) bool { return EqualError(e, err) } - // Int parse code int to error. func Int(i int) Code { return Code(i) } diff --git a/pkg/ecode/status.go b/pkg/ecode/status.go index 79e0f2a0d..e690e79d0 100644 --- a/pkg/ecode/status.go +++ b/pkg/ecode/status.go @@ -74,12 +74,6 @@ func (s *Status) WithDetails(pbs ...proto.Message) (*Status, error) { return s, nil } -// Equal for compatible. -// Deprecated: please use ecode.EqualError. -func (s *Status) Equal(err error) bool { - return EqualError(s, err) -} - // Proto return origin protobuf message func (s *Status) Proto() *types.Status { return s.s diff --git a/pkg/ecode/status_test.go b/pkg/ecode/status_test.go index c1fec659c..78bf6c158 100644 --- a/pkg/ecode/status_test.go +++ b/pkg/ecode/status_test.go @@ -16,9 +16,6 @@ func TestEqual(t *testing.T) { err2 = Errorf(RequestErr, "test") ) assert.Equal(t, err1, err2) - assert.True(t, OK.Equal(nil)) - assert.True(t, err1.Equal(err2)) - assert.False(t, err1.Equal(nil)) assert.True(t, Equal(nil, nil)) } diff --git a/pkg/naming/discovery/discovery.go b/pkg/naming/discovery/discovery.go index 1030d32c4..635229ce4 100644 --- a/pkg/naming/discovery/discovery.go +++ b/pkg/naming/discovery/discovery.go @@ -338,7 +338,7 @@ func (d *Discovery) Register(ctx context.Context, ins *naming.Instance) (cancelF for { select { case <-ticker.C: - if err := d.renew(ctx, ins); err != nil && ecode.NothingFound.Equal(err) { + if err := d.renew(ctx, ins); err != nil && ecode.EqualError(ecode.NothingFound, err) { _ = d.register(ctx, ins) } case <-ctx.Done(): @@ -380,7 +380,7 @@ func (d *Discovery) register(ctx context.Context, ins *naming.Instance) (err err uri, c.Zone, c.Env, ins.AppID, ins.Addrs, err) return } - if ec := ecode.Int(res.Code); !ec.Equal(ecode.OK) { + if ec := ecode.Int(res.Code); !ecode.Equal(ecode.OK, ec) { log.Warn("discovery: register client.Get(%v) env(%s) appid(%s) addrs(%v) code(%v)", uri, c.Env, ins.AppID, ins.Addrs, res.Code) err = ec return @@ -408,9 +408,9 @@ func (d *Discovery) renew(ctx context.Context, ins *naming.Instance) (err error) uri, c.Env, ins.AppID, c.Host, err) return } - if ec := ecode.Int(res.Code); !ec.Equal(ecode.OK) { + if ec := ecode.Int(res.Code); !ecode.Equal(ecode.OK, ec) { err = ec - if ec.Equal(ecode.NothingFound) { + if ecode.Equal(ecode.NothingFound, ec) { return } log.Error("discovery: renew client.Get(%v) env(%s) appid(%s) hostname(%s) code(%v)", @@ -440,7 +440,7 @@ func (d *Discovery) cancel(ins *naming.Instance) (err error) { uri, c.Env, ins.AppID, c.Host, err) return } - if ec := ecode.Int(res.Code); !ec.Equal(ecode.OK) { + if ec := ecode.Int(res.Code); !ecode.Equal(ecode.OK, ec) { log.Warn("discovery cancel client.Get(%v) env(%s) appid(%s) hostname(%s) code(%v)", uri, c.Env, ins.AppID, c.Host, res.Code) err = ec @@ -484,7 +484,7 @@ func (d *Discovery) set(ctx context.Context, ins *naming.Instance) (err error) { uri, conf.Zone, conf.Env, ins.AppID, ins.Addrs, err) return } - if ec := ecode.Int(res.Code); !ec.Equal(ecode.OK) { + if ec := ecode.Int(res.Code); !ecode.Equal(ecode.OK, ec) { log.Warn("discovery: set client.Get(%v) env(%s) appid(%s) addrs(%v) code(%v)", uri, conf.Env, ins.AppID, ins.Addrs, res.Code) err = ec @@ -587,8 +587,8 @@ func (d *Discovery) polls(ctx context.Context) (apps map[string]*naming.Instance log.Error("discovery: client.Get(%s) error(%+v)", uri+"?"+params.Encode(), err) return } - if ec := ecode.Int(res.Code); !ec.Equal(ecode.OK) { - if !ec.Equal(ecode.NotModified) { + if ec := ecode.Int(res.Code); !ecode.Equal(ecode.OK, ec) { + if !ecode.Equal(ecode.NotModified, ec) { log.Error("discovery: client.Get(%s) get error code(%d)", uri+"?"+params.Encode(), res.Code) err = ec } @@ -611,7 +611,7 @@ func (d *Discovery) broadcast(apps map[string]*naming.InstancesInfo) { for appID, v := range apps { var count int // v maybe nil in old version(less than v1.1) discovery,check incase of panic - if v==nil { + if v == nil { continue } for zone, ins := range v.Instances { diff --git a/pkg/net/rpc/warden/client.go b/pkg/net/rpc/warden/client.go index f8ae1d2b8..07cdad77c 100644 --- a/pkg/net/rpc/warden/client.go +++ b/pkg/net/rpc/warden/client.go @@ -3,8 +3,6 @@ package warden import ( "context" "fmt" - "github.com/bilibili/kratos/pkg/net/rpc/warden/resolver" - "github.com/bilibili/kratos/pkg/net/rpc/warden/resolver/direct" "net/url" "os" "strconv" @@ -12,6 +10,9 @@ import ( "sync" "time" + "github.com/bilibili/kratos/pkg/net/rpc/warden/resolver" + "github.com/bilibili/kratos/pkg/net/rpc/warden/resolver/direct" + "github.com/bilibili/kratos/pkg/conf/env" "github.com/bilibili/kratos/pkg/conf/flagvar" "github.com/bilibili/kratos/pkg/ecode" @@ -84,14 +85,15 @@ type Client struct { handlers []grpc.UnaryClientInterceptor } -type TimeOutCallOption struct { +// TimeoutCallOption timeout option. +type TimeoutCallOption struct { *grpc.EmptyCallOption Timeout time.Duration } // WithTimeoutCallOption can override the timeout in ctx and the timeout in the configuration file -func WithTimeoutCallOption(timeout time.Duration) *TimeOutCallOption { - return &TimeOutCallOption{&grpc.EmptyCallOption{}, timeout} +func WithTimeoutCallOption(timeout time.Duration) *TimeoutCallOption { + return &TimeoutCallOption{&grpc.EmptyCallOption{}, timeout} } // handle returns a new unary client interceptor for OpenTracing\Logging\LinkTimeout. @@ -127,10 +129,10 @@ func (c *Client) handle() grpc.UnaryClientInterceptor { return } defer onBreaker(brk, &err) - var timeOpt *TimeOutCallOption + var timeOpt *TimeoutCallOption for _, opt := range opts { var tok bool - timeOpt, tok = opt.(*TimeOutCallOption) + timeOpt, tok = opt.(*TimeoutCallOption) if tok { break } @@ -173,9 +175,10 @@ func (c *Client) handle() grpc.UnaryClientInterceptor { func onBreaker(breaker breaker.Breaker, err *error) { if err != nil && *err != nil { - if ecode.ServerErr.Equal(*err) || ecode.ServiceUnavailable.Equal(*err) || ecode.Deadline.Equal(*err) || ecode.LimitExceed.Equal(*err) { + if ecode.EqualError(ecode.ServerErr, *err) || ecode.EqualError(ecode.ServiceUnavailable, *err) || ecode.EqualError(ecode.Deadline, *err) || ecode.EqualError(ecode.LimitExceed, *err) { breaker.MarkFailed() return + } } breaker.MarkSuccess() diff --git a/pkg/net/rpc/warden/server_test.go b/pkg/net/rpc/warden/server_test.go index 72f479ac7..248caff4e 100644 --- a/pkg/net/rpc/warden/server_test.go +++ b/pkg/net/rpc/warden/server_test.go @@ -220,7 +220,7 @@ func Test_Warden(t *testing.T) { func testValidation(t *testing.T) { _, err := runClient(context.Background(), &clientConfig, t, "", 0) - if !ecode.RequestErr.Equal(err) { + if !ecode.EqualError(ecode.RequestErr, err) { t.Fatalf("testValidation should return ecode.RequestErr,but is %v", err) } } @@ -296,7 +296,7 @@ func testBreaker(t *testing.T) { for i := 0; i < 50; i++ { _, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: "breaker_test"}) if err != nil { - if ecode.ServiceUnavailable.Equal(err) { + if ecode.EqualError(ecode.ServiceUnavailable, err) { return } } @@ -334,7 +334,7 @@ func testLinkTimeout(t *testing.T) { if err == nil { t.Fatalf("testLinkTimeout must return error") } - if !ecode.Deadline.Equal(err) { + if !ecode.EqualError(ecode.Deadline, err) { t.Fatalf("testLinkTimeout must return error RPCDeadline,err:%v", err) } @@ -344,7 +344,7 @@ func testClientConfig(t *testing.T) { if err == nil { t.Fatalf("testLinkTimeout must return error") } - if !ecode.Deadline.Equal(err) { + if !ecode.EqualError(ecode.Deadline, err) { t.Fatalf("testLinkTimeout must return error RPCDeadline,err:%v", err) } } @@ -397,7 +397,7 @@ func testClientRecovery(t *testing.T) { t.Fatalf("recovery must return ecode error") } - if !ecode.ServerErr.Equal(e) { + if !ecode.EqualError(ecode.ServerErr, e) { t.Fatalf("recovery must return ecode.RPCClientErr") } }