doc and example (#267)

* add doc and example
* rm deprecated ecode.Equal
pull/269/head
Felix Hao 5 years ago committed by GitHub
parent 0160db5832
commit d9cee15ef6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      doc/wiki-cn/cache-redis.md
  2. 81
      doc/wiki-cn/database-mysql-orm.md
  3. 3
      doc/wiki-cn/database.md
  4. 2
      doc/wiki-cn/kratos-genbts.md
  5. 20
      doc/wiki-cn/ratelimit.md
  6. 1
      doc/wiki-cn/summary.md
  7. 0
      example/blademaster/middleware/auth/README.md
  8. 0
      example/blademaster/middleware/auth/auth.go
  9. 2
      example/blademaster/middleware/auth/example_test.go
  10. 4
      pkg/database/sql/sql.go
  11. 6
      pkg/ecode/ecode.go
  12. 6
      pkg/ecode/status.go
  13. 3
      pkg/ecode/status_test.go
  14. 18
      pkg/naming/discovery/discovery.go
  15. 19
      pkg/net/rpc/warden/client.go
  16. 10
      pkg/net/rpc/warden/server_test.go

@ -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包提供了大量返回值转换的快捷方式:

@ -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)

@ -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和统计功能。

@ -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代码。

@ -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)

@ -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)

@ -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"
)

@ -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
}
}

@ -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) }

@ -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

@ -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))
}

@ -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 {

@ -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()

@ -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")
}
}

Loading…
Cancel
Save