mirror of
https://github.com/go-eagle/eagle.git
synced 2025-09-26 20:41:26 +08:00
chore: improve template gen (#192)
* chore: improve repo template gen * chore: import cache template gen * chore: import protoc-gen-go-gin template gen * feat: add sonic json encoding * docs: add feature Co-authored-by: lvjiapeng <319161@myj.com.cn>
This commit is contained in:
@@ -1,7 +1,8 @@
|
|||||||
module github.com/go-eagle/eagle/cmd/eagle
|
module github.com/go-eagle/eagle/cmd/eagle
|
||||||
|
|
||||||
go 1.22
|
go 1.23.0
|
||||||
toolchain go1.24.1
|
|
||||||
|
toolchain go1.24.3
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/AlecAivazis/survey/v2 v2.2.12
|
github.com/AlecAivazis/survey/v2 v2.2.12
|
||||||
|
4
cmd/eagle/internal/cache/add/template.go
vendored
4
cmd/eagle/internal/cache/add/template.go
vendored
@@ -48,10 +48,10 @@ type {{.LcName}}Cache struct {
|
|||||||
|
|
||||||
// New{{.Name}}Cache new a cache
|
// New{{.Name}}Cache new a cache
|
||||||
func New{{.Name}}Cache(rdb *redis.Client) {{.Name}}Cache {
|
func New{{.Name}}Cache(rdb *redis.Client) {{.Name}}Cache {
|
||||||
jsonEncoding := encoding.JSONEncoding{}
|
sonicEncoding := encoding.SonicEncoding{}
|
||||||
cachePrefix := ""
|
cachePrefix := ""
|
||||||
return &{{.LcName}}Cache{
|
return &{{.LcName}}Cache{
|
||||||
cache: cache.NewRedisCache(rdb, cachePrefix, jsonEncoding, func() interface{} {
|
cache: cache.NewRedisCache(rdb, cachePrefix, sonicEncoding, func() interface{} {
|
||||||
return &model.{{.Name}}Model{}
|
return &model.{{.Name}}Model{}
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
@@ -16,6 +16,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
localCache "github.com/go-eagle/eagle/pkg/cache"
|
localCache "github.com/go-eagle/eagle/pkg/cache"
|
||||||
|
cacheBase "github.com/go-eagle/eagle/pkg/cache"
|
||||||
"github.com/go-eagle/eagle/pkg/encoding"
|
"github.com/go-eagle/eagle/pkg/encoding"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cast"
|
"github.com/spf13/cast"
|
||||||
@@ -57,7 +58,7 @@ func New{{.Name}}Repo(db *dal.DBClient, cache cache.{{.Name}}Cache) {{.Name}}Rep
|
|||||||
db: db,
|
db: db,
|
||||||
tracer: otel.Tracer("{{.LcName}}"),
|
tracer: otel.Tracer("{{.LcName}}"),
|
||||||
cache: cache,
|
cache: cache,
|
||||||
localCache: localCache.NewMemoryCache("local:{{.LcName}}:", encoding.JSONEncoding{}),
|
localCache: localCache.NewMemoryCache("local:{{.LcName}}:", encoding.SonicEncoding{}),
|
||||||
sg: singleflight.Group{},
|
sg: singleflight.Group{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,48 +125,49 @@ func (r *{{.LcName}}Repo) Get{{.Name}}(ctx context.Context, id int64) (ret *mode
|
|||||||
|
|
||||||
// read redis cache
|
// read redis cache
|
||||||
ret, err = r.cache.Get{{.Name}}Cache(ctx, id)
|
ret, err = r.cache.Get{{.Name}}Cache(ctx, id)
|
||||||
if err != nil {
|
if errors.Is(err, cacheBase.ErrPlaceholder) {
|
||||||
return nil, err
|
return nil, gorm.ErrRecordNotFound
|
||||||
}
|
} else if errors.Is(err, redis.ErrRedisNotFound) {
|
||||||
if ret != nil && ret.ID > 0 {
|
// get data from db
|
||||||
return ret, nil
|
// 避免缓存击穿(瞬间有大量请求过来)
|
||||||
}
|
val, err, _ := r.sg.Do("sg:{{.LcName}}:"+cast.ToString(id), func() (interface{}, error) {
|
||||||
|
// read db or rpc
|
||||||
// get data from db
|
data, err := dao.{{.Name}}Model.WithContext(ctx).Where(dao.{{.Name}}Model.ID.Eq(id)).Last()
|
||||||
// 避免缓存击穿(瞬间有大量请求过来)
|
if err != nil {
|
||||||
val, err, _ := r.sg.Do("sg:{{.LcName}}:"+cast.ToString(id), func() (interface{}, error) {
|
// cache not found and set empty cache to avoid 缓存穿透
|
||||||
// read db or rpc
|
// Note: 如果缓存空数据太多,会大大降低缓存命中率,可以改为使用布隆过滤器
|
||||||
data, err := dao.{{.Name}}Model.WithContext(ctx).Where(dao.{{.Name}}Model.ID.Eq(id)).Last()
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
r.cache.SetCacheWithNotFound(ctx, id)
|
||||||
|
}
|
||||||
|
return nil, errors.Wrapf(err, "[repo] get {{.Name}} from db error, id: %d", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// write cache
|
||||||
|
if data != nil && data.ID > 0 {
|
||||||
|
// write redis
|
||||||
|
err = r.cache.Set{{.Name}}Cache(ctx, id, data, 5*time.Minute)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WithMessage(err, "[repo] Get{{.Name}} Set{{.Name}}Cache error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// write local cache
|
||||||
|
err = r.localCache.Set(ctx, cast.ToString(id), data, 2*time.Minute)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WithMessage(err, "[repo] Get{{.Name}} localCache set error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data, nil
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// cache not found and set empty cache to avoid 缓存穿透
|
return nil, err
|
||||||
// Note: 如果缓存空数据太多,会大大降低缓存命中率,可以改为使用布隆过滤器
|
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
||||||
r.cache.SetCacheWithNotFound(ctx, id)
|
|
||||||
}
|
|
||||||
return nil, errors.Wrapf(err, "[repo] get {{.Name}} from db error, id: %d", id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// write cache
|
return val.(*model.{{.Name}}Model), nil
|
||||||
if data != nil && data.ID > 0 {
|
} else if err != nil {
|
||||||
// write redis
|
|
||||||
err = r.cache.Set{{.Name}}Cache(ctx, id, data, 5*time.Minute)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.WithMessage(err, "[repo] Get{{.Name}} Set{{.Name}}Cache error")
|
|
||||||
}
|
|
||||||
|
|
||||||
// write local cache
|
|
||||||
err = r.localCache.Set(ctx, cast.ToString(id), data, 2*time.Minute)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.WithMessage(err, "[repo] Get{{.Name}} localCache set error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return data, nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return ret, nil
|
||||||
|
|
||||||
return val.(*model.{{.Name}}Model), nil
|
|
||||||
{{- else }}
|
{{- else }}
|
||||||
// read db
|
// read db
|
||||||
ret, err = dao.{{.Name}}Model.WithContext(ctx).Where(dao.{{.Name}}Model.ID.Eq(id)).Last()
|
ret, err = dao.{{.Name}}Model.WithContext(ctx).Where(dao.{{.Name}}Model.ID.Eq(id)).Last()
|
||||||
|
@@ -18,6 +18,7 @@ const (
|
|||||||
metadataPackage = protogen.GoImportPath("google.golang.org/grpc/metadata")
|
metadataPackage = protogen.GoImportPath("google.golang.org/grpc/metadata")
|
||||||
eagleAppPackage = protogen.GoImportPath("github.com/go-eagle/eagle/pkg/app")
|
eagleAppPackage = protogen.GoImportPath("github.com/go-eagle/eagle/pkg/app")
|
||||||
errCodePackage = protogen.GoImportPath("github.com/go-eagle/eagle/pkg/errcode")
|
errCodePackage = protogen.GoImportPath("github.com/go-eagle/eagle/pkg/errcode")
|
||||||
|
utilsPackage = protogen.GoImportPath("github.com/go-eagle/eagle/pkg/utils")
|
||||||
deprecationComment = "// Deprecated: Do not use."
|
deprecationComment = "// Deprecated: Do not use."
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -40,7 +41,7 @@ func generateFile(gen *protogen.Plugin, file *protogen.File) *protogen.Generated
|
|||||||
g.P()
|
g.P()
|
||||||
g.P("// ", contextPackage.Ident(""))
|
g.P("// ", contextPackage.Ident(""))
|
||||||
g.P("// ", metadataPackage.Ident(""))
|
g.P("// ", metadataPackage.Ident(""))
|
||||||
g.P("// ", ginPackage.Ident(""), eagleAppPackage.Ident(""), errCodePackage.Ident(""))
|
g.P("// ", ginPackage.Ident(""), eagleAppPackage.Ident(""), errCodePackage.Ident(""), utilsPackage.Ident(""))
|
||||||
g.P()
|
g.P()
|
||||||
|
|
||||||
for _, service := range file.Services {
|
for _, service := range file.Services {
|
||||||
|
@@ -31,14 +31,19 @@ type {{$.Name}} struct{
|
|||||||
{{range .Methods}}
|
{{range .Methods}}
|
||||||
func (s *{{$.Name}}) {{ .HandlerName }} (ctx *gin.Context) {
|
func (s *{{$.Name}}) {{ .HandlerName }} (ctx *gin.Context) {
|
||||||
var in {{.Request}}
|
var in {{.Request}}
|
||||||
|
var err error
|
||||||
{{if eq .Method "GET" }}
|
{{if eq .Method "GET" }}
|
||||||
if err := ctx.ShouldBindQuery(&in); err != nil {
|
if err = ctx.ShouldBindQuery(&in); err != nil {
|
||||||
app.Error(ctx, errcode.ErrInvalidParam.WithDetails(err.Error()))
|
app.Error(ctx, errcode.ErrInvalidParam.WithDetails(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
{{if .HasPathParams }}
|
{{if .HasPathParams }}
|
||||||
// make sure the uri include :id
|
// make sure the uri include :id
|
||||||
in.Id = ctx.Param("id")
|
in.Id, err = utils.StringToInt64((ctx.Param("id")))
|
||||||
|
if err != nil {
|
||||||
|
app.Error(ctx, errcode.ErrInvalidParam.WithDetails(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
{{end}}
|
{{end}}
|
||||||
{{else if eq .Method "POST" "PUT" "PATCH" "DELETE"}}
|
{{else if eq .Method "POST" "PUT" "PATCH" "DELETE"}}
|
||||||
if err := ctx.ShouldBindJSON(&in); err != nil {
|
if err := ctx.ShouldBindJSON(&in); err != nil {
|
||||||
@@ -47,7 +52,11 @@ func (s *{{$.Name}}) {{ .HandlerName }} (ctx *gin.Context) {
|
|||||||
}
|
}
|
||||||
{{if .HasPathParams }}
|
{{if .HasPathParams }}
|
||||||
// make sure the uri include :id
|
// make sure the uri include :id
|
||||||
in.Id = ctx.Param("id")
|
in.Id, err = utils.StringToInt64((ctx.Param("id")))
|
||||||
|
if err != nil {
|
||||||
|
app.Error(ctx, errcode.ErrInvalidParam.WithDetails(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
{{end}}
|
{{end}}
|
||||||
{{else}}
|
{{else}}
|
||||||
if err := ctx.ShouldBind(&in); err != nil {
|
if err := ctx.ShouldBind(&in); err != nil {
|
||||||
|
@@ -78,7 +78,8 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// init service
|
// init service
|
||||||
service.Svc = service.New(repository.New(model.GetDB()))
|
db, _ := model.GetDB()
|
||||||
|
service.Svc = service.New(repository.New(db))
|
||||||
|
|
||||||
gin.SetMode(cfg.Mode)
|
gin.SetMode(cfg.Mode)
|
||||||
|
|
||||||
|
4
internal/cache/cache_client.go
vendored
4
internal/cache/cache_client.go
vendored
@@ -10,9 +10,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func getCacheClient(ctx context.Context) cache.Cache {
|
func getCacheClient(ctx context.Context) cache.Cache {
|
||||||
jsonEncoding := encoding.JSONEncoding{}
|
sonicEncoding := encoding.SonicEncoding{}
|
||||||
cachePrefix := ""
|
cachePrefix := ""
|
||||||
client := cache.NewRedisCache(redis.RedisClient, cachePrefix, jsonEncoding, func() interface{} {
|
client := cache.NewRedisCache(redis.RedisClient, cachePrefix, sonicEncoding, func() interface{} {
|
||||||
return &model.UserBaseModel{}
|
return &model.UserBaseModel{}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
45
pkg/encoding/sonic_encoding.go
Normal file
45
pkg/encoding/sonic_encoding.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package encoding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/bytedance/sonic"
|
||||||
|
"github.com/golang/snappy"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SonicEncoding struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s SonicEncoding) Marshal(v interface{}) ([]byte, error) {
|
||||||
|
buf, err := sonic.Marshal(v)
|
||||||
|
return buf, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s SonicEncoding) Unmarshal(data []byte, value interface{}) error {
|
||||||
|
err := sonic.Unmarshal(data, value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SonicSnappyEncoding Sonic json格式和snappy压缩
|
||||||
|
type SonicSnappyEncoding struct{}
|
||||||
|
|
||||||
|
// Marshal 序列化
|
||||||
|
func (s SonicSnappyEncoding) Marshal(v interface{}) (data []byte, err error) {
|
||||||
|
b, err := sonic.Marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
d := snappy.Encode(nil, b)
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal 反序列化
|
||||||
|
func (s SonicSnappyEncoding) Unmarshal(data []byte, value interface{}) error {
|
||||||
|
b, err := snappy.Decode(nil, data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return sonic.Unmarshal(b, value)
|
||||||
|
}
|
@@ -6,7 +6,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
jsoniter "github.com/json-iterator/go"
|
"github.com/bytedance/sonic"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IsEmpty 是否是空字符串
|
// IsEmpty 是否是空字符串
|
||||||
@@ -97,13 +97,10 @@ func StringToBytes(s string) []byte {
|
|||||||
return *(*[]byte)(unsafe.Pointer(&h))
|
return *(*[]byte)(unsafe.Pointer(&h))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 对于序列化和反序列化场景较多的服务可以使用性能更高的json-iterator
|
var Json = sonic.Config{
|
||||||
// https://github.com/json-iterator/go
|
EscapeHTML: true,
|
||||||
var Json = jsoniter.Config{
|
SortMapKeys: true,
|
||||||
EscapeHTML: true,
|
UseNumber: true,
|
||||||
SortMapKeys: true,
|
|
||||||
ValidateJsonRawMessage: true,
|
|
||||||
UseNumber: true,
|
|
||||||
}.Froze()
|
}.Froze()
|
||||||
|
|
||||||
func Stringify(obj interface{}) string {
|
func Stringify(obj interface{}) string {
|
||||||
|
Reference in New Issue
Block a user