btsgen support dao interface

pull/410/head
小旭 5 years ago
parent 3b510022a3
commit 0662113a26
  1. 2
      tool/kratos-gen-bts/README.md
  2. 6
      tool/kratos-gen-bts/header_template.go
  3. 52
      tool/kratos-gen-bts/main.go
  4. 6
      tool/kratos-gen-bts/multi_template.go
  5. 8
      tool/kratos-gen-bts/none_template.go
  6. 6
      tool/kratos-gen-bts/single_template.go
  7. 2
      tool/kratos/tool_index.go

@ -36,6 +36,7 @@ dao里面需要有cache对象 代码会调用d.cache来新增缓存
| ---------------- | ------ | ------------------------------------------------------------ | ------------------------------------------------------------ | | ---------------- | ------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
| -nullcache | | 空指针对象(存正常业务不会出现的内容 id的话像是-1这样的) | &Demo{ID:-1} 或-1 或"null" | | -nullcache | | 空指针对象(存正常业务不会出现的内容 id的话像是-1这样的) | &Demo{ID:-1} 或-1 或"null" |
| -check_null_code | | 开启空缓存并且value为指针对象时必填 用于判断是否是空缓存 $来指代对象名 | `-check_null_code=$!=nil&&$.ID==-1 或 $ == -1` | | -check_null_code | | 开启空缓存并且value为指针对象时必填 用于判断是否是空缓存 $来指代对象名 | `-check_null_code=$!=nil&&$.ID==-1 或 $ == -1` |
| -cache_err |continue| 缓存出错的时候的行为 continue: 继续执行 break: 抛出错误 方法返回|break|
| -batch | | (限多key模板) 批量获取数据 每组大小 | 100 | | -batch | | (限多key模板) 批量获取数据 每组大小 | 100 |
| -max_group | | (限多key模板)批量获取数据 最大组数量 | 10 | | -max_group | | (限多key模板)批量获取数据 最大组数量 | 10 |
| -batch_err | break | (限多key模板)批量获取数据回源错误的时候 降级继续请求(continue)还是直接返回(break) | break 或 continue | | -batch_err | break | (限多key模板)批量获取数据回源错误的时候 降级继续请求(continue)还是直接返回(break) | break 或 continue |
@ -44,3 +45,4 @@ dao里面需要有cache对象 代码会调用d.cache来新增缓存
| -paging | false | (限单key模板)分页 数据源应返回2个值 第一个为对外数据 第二个为全量数据 用于新增缓存 | false | | -paging | false | (限单key模板)分页 数据源应返回2个值 第一个为对外数据 第二个为全量数据 用于新增缓存 | false |
| -ignores | | 用于依赖的三个方法参数和主方法参数不一致的情况. 忽略方法的某些参数 用\|分隔方法逗号分隔参数 | pn,ps\|pn\|origin 表示"缓存获取"方法忽略pn,ps两个参数 回源方法忽略pn参数 加缓存方法忽略origin参数 | | -ignores | | 用于依赖的三个方法参数和主方法参数不一致的情况. 忽略方法的某些参数 用\|分隔方法逗号分隔参数 | pn,ps\|pn\|origin 表示"缓存获取"方法忽略pn,ps两个参数 回源方法忽略pn参数 加缓存方法忽略origin参数 |
| -custom_method | false | 自定义方法名 \|分隔 缓存获取方法名\|回源方法名\|增加缓存方法名 | d.mc.AddDemo\|d.mysql.Demo\|d.mc.AddDemo | | -custom_method | false | 自定义方法名 \|分隔 缓存获取方法名\|回源方法名\|增加缓存方法名 | d.mc.AddDemo\|d.mysql.Demo\|d.mc.AddDemo |
| -struct_name | dao | 所属结构体名称 | Dao|

@ -24,9 +24,9 @@ NEWLINE
{{if .EnableSingleFlight}} "golang.org/x/sync/singleflight" {{end}} {{if .EnableSingleFlight}} "golang.org/x/sync/singleflight" {{end}}
) )
var ( {{if .UseBTS}}
_ _bts var _ _bts
) {{end }}
{{if .EnableSingleFlight}} {{if .EnableSingleFlight}}
var cacheSingleFlights = [SFCOUNT]*singleflight.Group{SFINIT} var cacheSingleFlights = [SFCOUNT]*singleflight.Group{SFINIT}
{{end }} {{end }}

@ -22,6 +22,7 @@ var (
singleFlight = flag.Bool("singleflight", false, "enable singleflight") singleFlight = flag.Bool("singleflight", false, "enable singleflight")
nullCache = flag.String("nullcache", "", "null cache") nullCache = flag.String("nullcache", "", "null cache")
checkNullCode = flag.String("check_null_code", "", "check null code") checkNullCode = flag.String("check_null_code", "", "check null code")
cacheErr = flag.String("cache_err", "continue", "cache err to contine or break")
batchSize = flag.Int("batch", 0, "batch size") batchSize = flag.Int("batch", 0, "batch size")
batchErr = flag.String("batch_err", "break", "batch err to contine or break") batchErr = flag.String("batch_err", "break", "batch err to contine or break")
maxGroup = flag.Int("max_group", 0, "max group size") maxGroup = flag.Int("max_group", 0, "max group size")
@ -29,18 +30,19 @@ var (
paging = flag.Bool("paging", false, "use paging in single template") paging = flag.Bool("paging", false, "use paging in single template")
ignores = flag.String("ignores", "", "ignore params") ignores = flag.String("ignores", "", "ignore params")
customMethod = flag.String("custom_method", "", "自定义方法名 |分隔: 缓存|回源|增加缓存") customMethod = flag.String("custom_method", "", "自定义方法名 |分隔: 缓存|回源|增加缓存")
structName = flag.String("struct_name", "dao", "struct name")
numberTypes = []string{"int", "int8", "int16", "int32", "int64", "float32", "float64", "uint", "uint8", "uint16", "uint32", "uint64"} numberTypes = []string{"int", "int8", "int16", "int32", "int64", "float32", "float64", "uint", "uint8", "uint16", "uint32", "uint64"}
simpleTypes = []string{"int", "int8", "int16", "int32", "int64", "float32", "float64", "uint", "uint8", "uint16", "uint32", "uint64", "bool", "string", "[]byte"} simpleTypes = []string{"int", "int8", "int16", "int32", "int64", "float32", "float64", "uint", "uint8", "uint16", "uint32", "uint64", "bool", "string", "[]byte"}
optionNames = []string{"singleflight", "nullcache", "check_null_code", "batch", "max_group", "sync", "paging", "ignores", "batch_err", "custom_method"} optionNames = []string{"singleflight", "nullcache", "check_null_code", "batch", "max_group", "sync", "paging", "ignores", "batch_err", "custom_method", "cache_err", "struct_name"}
optionNamesMap = map[string]bool{} optionNamesMap = map[string]bool{}
interfaceName string
) )
const ( const (
_interfaceName = "_bts" _multiTpl = 1
_multiTpl = 1 _singleTpl = 2
_singleTpl = 2 _noneTpl = 3
_noneTpl = 3
) )
func resetFlag() { func resetFlag() {
@ -52,8 +54,10 @@ func resetFlag() {
*sync = false *sync = false
*paging = false *paging = false
*batchErr = "break" *batchErr = "break"
*cacheErr = "continue"
*ignores = "" *ignores = ""
*customMethod = "" *customMethod = ""
*structName = "dao"
} }
// options options // options options
@ -91,6 +95,10 @@ type options struct {
Comment string Comment string
CustomMethod string CustomMethod string
IDName string IDName string
CacheErrContinue bool
StructName string
hasDec bool
UseBTS bool
} }
func getOptions(opt *options, comment string) { func getOptions(opt *options, comment string) {
@ -108,6 +116,7 @@ func getOptions(opt *options, comment string) {
os.Args = append(os.Args, arg) os.Args = append(os.Args, arg)
} }
} }
opt.hasDec = true
} }
resetFlag() resetFlag()
flag.Parse() flag.Parse()
@ -122,13 +131,15 @@ func getOptions(opt *options, comment string) {
opt.GroupSize = *batchSize opt.GroupSize = *batchSize
opt.MaxGroup = *maxGroup opt.MaxGroup = *maxGroup
opt.CustomMethod = *customMethod opt.CustomMethod = *customMethod
opt.CacheErrContinue = *cacheErr == "continue"
opt.StructName = *structName
} }
func processList(s *pkg.Source, list *ast.Field) (opt options) { func processList(s *pkg.Source, list *ast.Field) (opt options) {
fset := s.Fset fset := s.Fset
src := s.Src src := s.Src
lines := strings.Split(src, "\n") lines := strings.Split(src, "\n")
opt = options{Args: s.GetDef(_interfaceName), importPackages: s.Packages(list)} opt = options{name: list.Names[0].Name, Args: s.GetDef(interfaceName), importPackages: s.Packages(list)}
// get comment // get comment
line := fset.Position(list.Pos()).Line - 3 line := fset.Position(list.Pos()).Line - 3
if len(lines)-1 >= line { if len(lines)-1 >= line {
@ -140,8 +151,11 @@ func processList(s *pkg.Source, list *ast.Field) (opt options) {
line = fset.Position(list.Pos()).Line - 2 line = fset.Position(list.Pos()).Line - 2
comment := lines[line] comment := lines[line]
getOptions(&opt, comment) getOptions(&opt, comment)
if !opt.hasDec {
log.Printf("%s: 无声明 忽略此方法\n", opt.name)
return
}
// get func // get func
opt.name = list.Names[0].Name
params := list.Type.(*ast.FuncType).Params.List params := list.Type.(*ast.FuncType).Params.List
if len(params) == 0 { if len(params) == 0 {
log.Fatalln(opt.name + "参数不足") log.Fatalln(opt.name + "参数不足")
@ -304,15 +318,26 @@ func processList(s *pkg.Source, list *ast.Field) (opt options) {
// parse parse options // parse parse options
func parse(s *pkg.Source) (opts []*options) { func parse(s *pkg.Source) (opts []*options) {
c := s.F.Scope.Lookup(_interfaceName) var c *ast.Object
if (c == nil) || (c.Kind != ast.Typ) { for _, name := range []string{"_bts", "Dao"} {
c = s.F.Scope.Lookup(name)
if (c == nil) || (c.Kind != ast.Typ) {
c = nil
continue
}
interfaceName = name
break
}
if c == nil {
log.Fatalln("无法找到缓存声明") log.Fatalln("无法找到缓存声明")
} }
lists := c.Decl.(*ast.TypeSpec).Type.(*ast.InterfaceType).Methods.List lists := c.Decl.(*ast.TypeSpec).Type.(*ast.InterfaceType).Methods.List
for _, list := range lists { for _, list := range lists {
opt := processList(s, list) opt := processList(s, list)
opt.Check() if opt.hasDec {
opts = append(opts, &opt) opt.Check()
opts = append(opts, &opt)
}
} }
return return
} }
@ -339,10 +364,6 @@ func (option *options) Check() {
if option.SimpleValue && option.NullCache == option.ZeroValue { if option.SimpleValue && option.NullCache == option.ZeroValue {
log.Fatalf("%s: %s 不能作为空缓存值 \n", option.name, option.NullCache) log.Fatalf("%s: %s 不能作为空缓存值 \n", option.name, option.NullCache)
} }
if strings.Contains(option.NullCache, "{}") {
// -nullcache=[]*model.OrderMain{} 这种无效
log.Fatalf("%s: %s 不能作为空缓存值 会导致空缓存无效 \n", option.name, option.NullCache)
}
if strings.Contains(option.CheckNullCode, "len") && strings.Contains(strings.Replace(option.CheckNullCode, " ", "", -1), "==0") { if strings.Contains(option.CheckNullCode, "len") && strings.Contains(strings.Replace(option.CheckNullCode, " ", "", -1), "==0") {
// -check_null_code=len($)==0 这种无效 // -check_null_code=len($)==0 这种无效
log.Fatalf("%s: -check_null_code=%s 错误 会有无意义的赋值\n", option.name, option.CheckNullCode) log.Fatalf("%s: -check_null_code=%s 错误 会有无意义的赋值\n", option.name, option.CheckNullCode)
@ -352,6 +373,7 @@ func (option *options) Check() {
func genHeader(opts []*options) (src string) { func genHeader(opts []*options) (src string) {
option := options{PkgName: os.Getenv("GOPACKAGE")} option := options{PkgName: os.Getenv("GOPACKAGE")}
option.UseBTS = interfaceName == "_bts"
var sfCount int var sfCount int
var packages, sfInit []string var packages, sfInit []string
packagesMap := map[string]bool{`"context"`: true} packagesMap := map[string]bool{`"context"`: true}

@ -2,15 +2,19 @@ package main
var _multiTemplate = ` var _multiTemplate = `
// NAME {{or .Comment "get data from cache if miss will call source method, then add to cache."}} // NAME {{or .Comment "get data from cache if miss will call source method, then add to cache."}}
func (d *Dao) NAME(c context.Context, {{.IDName}} []KEY{{.ExtraArgsType}}) (res map[KEY]VALUE, err error) { func (d *{{.StructName}}) NAME(c context.Context, {{.IDName}} []KEY{{.ExtraArgsType}}) (res map[KEY]VALUE, err error) {
if len({{.IDName}}) == 0 { if len({{.IDName}}) == 0 {
return return
} }
addCache := true addCache := true
if res, err = CACHEFUNC(c, {{.IDName}} {{.ExtraCacheArgs}});err != nil { if res, err = CACHEFUNC(c, {{.IDName}} {{.ExtraCacheArgs}});err != nil {
{{if .CacheErrContinue}}
addCache = false addCache = false
res = nil res = nil
err = nil err = nil
{{else}}
return
{{end}}
} }
var miss []KEY var miss []KEY
for _, key := range {{.IDName}} { for _, key := range {{.IDName}} {

@ -2,12 +2,16 @@ package main
var _noneTemplate = ` var _noneTemplate = `
// NAME {{or .Comment "get data from cache if miss will call source method, then add to cache."}} // NAME {{or .Comment "get data from cache if miss will call source method, then add to cache."}}
func (d *Dao) NAME(c context.Context) (res VALUE, err error) { func (d *{{.StructName}}) NAME(c context.Context) (res VALUE, err error) {
addCache := true addCache := true
res, err = CACHEFUNC(c) res, err = CACHEFUNC(c)
if err != nil { if err != nil {
{{if .CacheErrContinue}}
addCache = false addCache = false
err = nil err = nil
{{else}}
return
{{end}}
} }
{{if .EnableNullCache}} {{if .EnableNullCache}}
defer func() { defer func() {
@ -21,7 +25,7 @@ func (d *Dao) NAME(c context.Context) (res VALUE, err error) {
{{else}} {{else}}
if res != {{.ZeroValue}} { if res != {{.ZeroValue}} {
{{end}} {{end}}
cache.MetricHits.Inc("bts:NAME") cache.MetricHits.Inc("bts:NAME")
return return
} }
{{if .EnableSingleFlight}} {{if .EnableSingleFlight}}

@ -2,12 +2,16 @@ package main
var _singleTemplate = ` var _singleTemplate = `
// NAME {{or .Comment "get data from cache if miss will call source method, then add to cache."}} // NAME {{or .Comment "get data from cache if miss will call source method, then add to cache."}}
func (d *Dao) NAME(c context.Context, {{.IDName}} KEY{{.ExtraArgsType}}) (res VALUE, err error) { func (d *{{.StructName}}) NAME(c context.Context, {{.IDName}} KEY{{.ExtraArgsType}}) (res VALUE, err error) {
addCache := true addCache := true
res, err = CACHEFUNC(c, {{.IDName}} {{.ExtraCacheArgs}}) res, err = CACHEFUNC(c, {{.IDName}} {{.ExtraCacheArgs}})
if err != nil { if err != nil {
{{if .CacheErrContinue}}
addCache = false addCache = false
err = nil err = nil
{{else}}
return
{{end}}
} }
{{if .EnableNullCache}} {{if .EnableNullCache}}
defer func() { defer func() {

@ -34,7 +34,7 @@ var toolIndexs = []*Tool{
{ {
Name: "genbts", Name: "genbts",
Alias: "kratos-gen-bts", Alias: "kratos-gen-bts",
BuildTime: time.Date(2019, 7, 23, 0, 0, 0, 0, time.Local), BuildTime: time.Date(2019, 10, 31, 0, 0, 0, 0, time.Local),
Install: "go get -u github.com/bilibili/kratos/tool/kratos-gen-bts", Install: "go get -u github.com/bilibili/kratos/tool/kratos-gen-bts",
Summary: "缓存回源逻辑代码生成器", Summary: "缓存回源逻辑代码生成器",
Platform: []string{"darwin", "linux", "windows"}, Platform: []string{"darwin", "linux", "windows"},

Loading…
Cancel
Save