mirror of
https://github.com/go-kratos/kratos.git
synced 2025-03-17 21:07:54 +02:00
doc and example (#267)
* add doc and example * rm deprecated ecode.Equal
This commit is contained in:
parent
0160db5832
commit
d9cee15ef6
@ -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包提供了大量返回值转换的快捷方式:
|
||||
|
||||
|
81
doc/wiki-cn/database-mysql-orm.md
Normal file
81
doc/wiki-cn/database-mysql-orm.md
Normal file
@ -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…
x
Reference in New Issue
Block a user