gormTracing
gormTracing copied to clipboard
GORM V2 Opentracing Plugin
Golang ä¸æGORM V2 + Opentracingé¾è·¯è¿½è¸ªä¼åCRUDä½éªï¼æºç é 读ï¼
ä¸ãåè¨
ç³»ç»ç¯å¢ï¼è¿å å¹´æç¿»åæ¥çæ许ä¼ææ ¨æå½åå¦æ¤ä¸å ªï¼
go version go1.14.3 windows/amd64
gorm.io/gorm v0.2.31
ä¸ä¼å§ä¸ä¼å§ï¼0202å¹´äºï¼è¿æ人å纯ä¾èµæ¥å¿å»ææ¥CRUDé®é¢ï¼å¿«äºè§£ä¸ä¸é¾è·¯è¿½è¸ªå§ï¼
å
æ份ååï¼æ¯è¾æåçGORMæ¡æ¶
æ´æ°äºV2çæ¬ï¼å°½ç®¡ç°å¨ä¾æ§å¨æµè¯é¶æ®µï¼ä½æ¯æ们è¿æ¯è½ä½éªä¸ä¸æ¡æ¶çä¸é¨åæ°ç¹æ§ Featureï¼å
¶ä¸æé¦çè¿æ¯æ¯æContext
ä¸ä¸æä¼ éçç¹æ§ï¼ç»ååå¸å¼é¾è·¯è¿½è¸ªææ¯ï¼æå©äºæ们æå¡å¨åå¸å¼é¨ç½²çæ
åµä¸ç²¾åææ¥é®é¢ã
è¿æ¯è¦æåä¸ä¸ï¼ORMä½ä¸ºè¾ å©å·¥å ·è½å¸®å©æ们快éæ建项ç®ï¼è¿½æ±æè´çååºé度åºè¯¥ææ¸SQLï¼ä½æ¯ææ¸SQLå¾å¾ä¼éå°SQLæ³¨å ¥ãè¿ç¨ç¹ççé®é¢ï¼æ以ORMæ¯ä¸æåååï¼å©ç¨åå°çºç²ä¸å®çæ§è½ä»¥ä¾¿æ们æ´å¿«ä¸æ项ç®ã
æç« æä¾ä¾æ§æé¨åæºç åæï¼ä¹åæåè¿åç±»åORMæ¡æ¶XORM
çé¾è·¯è¿½è¸ªæç¨ï¼æå
´è¶£çå¯ä»¥çä¸ä¸
Golang XORMå®ç°åå¸å¼é¾è·¯è¿½è¸ªï¼æºç åæï¼åå¸å¼CRUDå¿ å¦ï¼
äºãDockeræ建Opentracing + jaeger all in oneå¹³å°
注ï¼Dockeræ¯æç®åçï¼è¿æå ¶ä»çæ¹å¼ï¼æå ´è¶£çæåå¯ä»¥å»ç¿»é ææ¯ææ¡£
使ç¨çéåï¼jaegertracing/all-in-one:1.18
Dockerå½ä»¤
docker run -d --name jaeger -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 -p 5775:5775/udp -p 6831:6831/udp -p 6832:6832/udp -p 5778:5778 -p 16686:16686 -p 14268:14268 -p 14250:14250 -p 9411:9411 jaegertracing/all-in-one:1.18
æµè§å¨è®¿é®localhost:16686
ï¼å¯ä»¥çå°JaegerUI
çé¢ï¼å¦ä¸æ示ï¼
ä¸ãå建项ç®
å¨é¡¹ç®ç®å½ä¸ä½¿ç¨æ§å¶å°è¾å ¥ï¼~~æå¾é½æ~~
go mod init
go get -u gorm.io/gorm
go get -u github.com/uber/jaeger-client-go
åãç¼åCallBacksæ件
è¿éçCallBackså模åçé©åä¸ä¸æ ·ï¼CallBacksä¼´éGORMçDB对象æ´ä¸ªçå½å¨æï¼æ们éè¦å©ç¨CallBacks对GORMæ¡æ¶è¿è¡ä¾µå ¥ï¼ä»¥è¾¾å°æä½å访é®GORMçDB对象çè¡ä¸º
1. å¨æ¯æ¬¡SQLæä½åä»contextä¸ä¸æçæåspan
é常æ们æå¡ï¼ä¸å¡ï¼å¨å ¥å£ä¼åé ä¸ä¸ªæ ¹Spanï¼ç¶åå¨åç»æä½ä¸ä¼åè£åºåSpanï¼æ¯ä¸ªspané½æèªå·±çå ·ä½çæ è¯ï¼Finshä¹åå°±ä¼æ±éå¨æ们çé¾è·¯è¿½è¸ªç³»ç»ä¸
ï¼OpenTracingçSpan示æå¾ï¼æåå¦ä¹çä¸æï¼å
¶å®ä¹å°±é£æ ·ï¼
æ件ï¼gormTracing.go
package gormTracing
// å
å
éæåé
const gormSpanKey = "__gorm_span"
func before(db *gorm.DB) {
// å
ä»ç¶çº§spansçæåspan ---> è¿éå½å为gormï¼ä½å®é
ä¸å¯ä»¥èªå®ä¹
// èªå·±å欢çoperationName
span, _ := opentracing.StartSpanFromContext(db.Statement.Context, "gorm")
// å©ç¨dbå®ä¾å»ä¼ éspan
db.InstanceSet(gormSpanKey, span)
return
}
å°±è¿ä¹æ´å®æ åç两è¡ä»£ç å°±è½çæåspanï¼æç
§æ¯ä¾ï¼æ们éè¦æå¼æStartSpanFromContext
第äºä¸ªç»æï¼å 为æ们ä¸è½æç¶spanè¦çæï¼åæ¶å©ç¨dbçSettingï¼sync.Mapï¼
å»å¯ååSpan
ä¸æä¼å¯¹db.InstanceSet
æ¹æ³è¿è¡æºç 说æï¼æå
´è¶£çæåå¯ä»¥çä¸ä¸
2. å¨æ¯æ¬¡SQLæä½åä»DBå®ä¾æ¿å°Span并记å½æ°æ®
æ件: gormTracing.go
// 注æï¼è¿ééå½åäºlog模å
import tracerLog "github.com/opentracing/opentracing-go/log"
func after(db *gorm.DB) {
// ä»GORMçDBå®ä¾ä¸ååºspan
_span, isExist := db.InstanceGet(gormSpanKey)
if !isExist {
// ä¸åå¨å°±ç´æ¥æå¼æ
return
}
// æè¨è¿è¡ç±»å转æ¢
span, ok := _span.(opentracing.Span)
if !ok {
return
}
// <---- ä¸å®ä¸å®ä¸å®è¦Finsihæï¼ï¼ï¼
defer span.Finish()
// Error
if db.Error != nil {
span.LogFields(tracerLog.Error(db.Error))
}
// sql --> åæ³æ¥æºGORM V2çæ¥å¿
span.LogFields(tracerLog.String("sql", db.Dialector.Explain(db.Statement.SQL.String(), db.Statement.Vars...)))
return
}
åæ ·é常ç®åå°å°±è½ä»DBçSettingéé¢æ¿å°ç¨äºå¤çGORMæä½çåSpanï¼æ们åªéè¦è°ç¨spançLogFieldsæ¹æ³å°±è½è®°å½ä¸æ们æ³è¦çä¿¡æ¯
3. å建ç»æä½ï¼å®ç°gorm.Pluginæ¥å£
æ件: gormTracing.go
const (
callBackBeforeName = "opentracing:before"
callBackAfterName = "opentracing:after"
)
type OpentracingPlugin struct {}
func (op *OpentracingPlugin) Name() string {
return "opentracingPlugin"
}
func (op *OpentracingPlugin) Initialize(db *gorm.DB) (err error) {
// å¼å§å - 并ä¸æ¯é½ç¨ç¸åçæ¹æ³ï¼å¯ä»¥èªå·±èªå®ä¹
db.Callback().Create().Before("gorm:before_create").Register(callBackBeforeName, before)
db.Callback().Query().Before("gorm:query").Register(callBackBeforeName, before)
db.Callback().Delete().Before("gorm:before_delete").Register(callBackBeforeName, before)
db.Callback().Update().Before("gorm:setup_reflect_value").Register(callBackBeforeName, before)
db.Callback().Row().Before("gorm:row").Register(callBackBeforeName, before)
db.Callback().Raw().Before("gorm:raw").Register(callBackBeforeName, before)
// ç»æå - 并ä¸æ¯é½ç¨ç¸åçæ¹æ³ï¼å¯ä»¥èªå·±èªå®ä¹
db.Callback().Create().After("gorm:after_create").Register(callBackAfterName, after)
db.Callback().Query().After("gorm:after_query").Register(callBackAfterName, after)
db.Callback().Delete().After("gorm:after_delete").Register(callBackAfterName, after)
db.Callback().Update().After("gorm:after_update").Register(callBackAfterName, after)
db.Callback().Row().After("gorm:row").Register(callBackAfterName, after)
db.Callback().Raw().After("gorm:raw").Register(callBackAfterName, after)
return
}
// åè¯ç¼è¯å¨è¿ä¸ªç»æä½å®ç°äºgorm.Pluginæ¥å£
var _ gorm.Plugin = &OpentracingPlugin{}
æ们éè¦é常ç¹çå°ç»GORMææçæç»æä½ï¼æºç åæä¼æå°ï¼æ³¨åä¸ååç¼åç两个æ¹æ³ï¼åå«æ¯æ¯ä¸ªæç»æä½çæå¼å§åç»å°¾ï¼è¿éç´æ¥æä¸ä¸GORMçPluginæ¥å£ï¼æºç å¦ä¸ï¼
// Plugin GORM plugin interface
type Plugin interface {
Name() string
Initialize(*DB) error
}
æ们åªéè¦å®ç°è¿ä¸¤ä¸ªæ¹æ³å³å¯å®ç°è¿ä¸ªæ¥å£ï¼é常ç®åã
äºãåå æµè¯
1. åå§åJeager
æ件: gormTracing_test.go
package gormTracing
func initJaeger() (closer io.Closer, err error) {
// æ ¹æ®é
ç½®åå§åTracer è¿åCloser
tracer, closer, err := (&config.Configuration{
ServiceName: "gormTracing",
Disabled: false,
Sampler: &config.SamplerConfig{
Type: jaeger.SamplerTypeConst,
// paramçå¼å¨0å°1ä¹é´ï¼è®¾ç½®ä¸º1åå°ææçOperationè¾åºå°Reporter
Param: 1,
},
Reporter: &config.ReporterConfig{
LogSpans: true,
LocalAgentHostPort: "localhost:6831",
},
}).NewTracer()
if err != nil {
return
}
// 设置å
¨å±Tracer - å¦æä¸è®¾ç½®å°ä¼å¯¼è´ä¸ä¸ææ æ³çææ£ç¡®çSpan
opentracing.SetGlobalTracer(tracer)
return
}
æå ´è¶£çæåä¸å®ä¸å®è¦èªå·±å»é 读ä¸ä¸Jaegerçææ¯ææ¡£ï¼è¿éåªæ¯èä¾ï¼èä¸æ¯æ åï¼åå§åJeagerä¸å®æ¯æ ¹æ®å®é ä¸å¡å»å®ç°ï¼è¿éåªæ¯ç®å示èã
2. å®ç°GORMå®æ¹èä¾
æ件: gormTracing_test.go
type Product struct {
gorm.Model
Code string
Price uint
}
å®ä¹ä¸ä¸ªé常ç®åçç»æä½ä½ä¸ºæ¨¡å
// å¾å¤äººä¼ä¸ç¥éV2å¦ä½è¿æ¥MySQLæ°æ®åº
// éè¦å©ç¨Driveræ¥å®ç°
import "gorm.io/driver/mysql"
func Test_GormTracing(t *testing.T) {
// 1. åå§åJaeger
closer, err := initJaeger()
if err != nil{
t.Fatal(err)
}
defer closer.Close()
// 2. è¿æ¥æ°æ®åº
dsn := "root:root@tcp(localhost:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
t.Fatal(err)
}
// 3. æéè¦çä¸æ¥ï¼ä½¿ç¨æ们å®ä¹çæ件
_ = db.Use(&OpentracingPlugin{})
// è¿ç§» schema ---> çæ对åºçæ°æ®è¡¨
_ = db.AutoMigrate(&Product{})
// 4. çææ°çSpan - 注æå°spanç»ææï¼ä¸ç¶æ æ³åé对åºçç»æ
span := opentracing.StartSpan("gormTracing unit test")
defer span.Finish()
// 5. æçæçRoot Spanåå
¥å°Contextä¸ä¸æï¼è·åä¸ä¸ªåContext
// é常å¨Web项ç®ä¸ï¼Root Spanç±ä¸é´ä»¶çæ
ctx := opentracing.ContextWithSpan(context.Background(), span)
// 6. å°ä¸ä¸æä¼ å
¥DBå®ä¾ï¼çæSessionä¼è¯
// è¿æ ·åå°±è½æè¿ä¸ªä¼è¯çå
¨é¨ä¿¡æ¯åé¦ç»Jaeger
session := db.WithContext(ctx)
// ---> ä¸é¢å°±æ¯GORMçèä¾
// Create
session.Create(&Product{Code: "D42", Price: 100})
// Read
var product Product
session.First(&product, 1) // æ ¹æ®æ´å½¢ä¸»é®æ¥æ¾
session.First(&product, "code = ?", "D42") // æ¥æ¾ code å段å¼ä¸º D42 çè®°å½
// Update - å° product ç price æ´æ°ä¸º 200
session.Model(&product).Update("Price", 200)
// Update - æ´æ°å¤ä¸ªå段
session.Model(&product).Updates(Product{Price: 200, Code: "F42"}) // ä»
æ´æ°éé¶å¼å段
session.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F42"})
// Delete - å é¤ product
session.Delete(&product, 1)
}
3. æ§è¡å¹¶æ¥çç»æ
=== RUN Test_GormTracing
--- PASS: Test_GormTracing (0.42s)
PASS
Process finished with exit code 0
æ§å¶å°æ¾ç¤ºç¨åºæ£å¸¸è¿è¡ï¼æ们访é®Jaegeræ§å¶å°ï¼localhost:16686
ï¼ï¼å¯ä»¥çå°æä¸æ¡æ°çè®°å½
ç¹å»è¿å ¥æ¥ç详æ ï¼å¯ä»¥éå¸¸æ¸ æ¥ç¬¬çè§æ们æ´ä¸ªåå æµè¯ä»å¼å§å°ç»æçSQLæ§è¡æ åµï¼æ»å ±æ§è¡äº7æ¡SQLå½ä»¤ï¼æ´ä¸ªè¿ç¨èæ¶163.03msï¼æ³¨æï¼è¿ä¸ä»£è¡¨GORMæ§è½æµè¯ï¼å 为æçæ°æ®åºé¨ç½²å¨æ èæ´¾ï¼åæ¶ä½¿ç¨è¿ç¨è¿æ¥ï¼æ¥è¯¢è¿ç¨ä¼æ¯è¾æ ¢ï¼
æ们å¯ä»¥ç¹å¼å¯¹åºçSpanï¼å¯ä»¥çå°æ¯æ¬¡GORMæä½ææ§è¡çSQLå½ä»¤ã
è³æ¤ï¼ä½¿ç¨OpenTracing对GORMæ§è¡è¿ç¨è¿è¡é¾è·¯è¿½è¸ªå·²ç»æåå®ç°ï¼ä»æ¤æè±éè¦æ£ç´¢åºå¤§çæ¥å¿æ¥æ¾æ ¢æ¥è¯¢ãå¼å¸¸åé误çæ åµï¼ç´æ¥ä¸ç®äºç¶ã
4. æè - 并åæ åµä¸é¾è·¯è¿½è¸ªçææ
func Test_GormTracing2(t *testing.T) {
closer, err := initJaeger()
if err != nil{
t.Fatal(err)
}
defer closer.Close()
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
t.Fatal(err)
}
_ = db.Use(&OpentracingPlugin{})
rand.Seed(time.Now().UnixNano())
num,wg := 1<<10, &sync.WaitGroup{}
wg.Add(num)
for i :=0 ;i < num; i ++{
go func(t int) {
span := opentracing.StartSpan(fmt.Sprintf("gormTracing unit test %d", t))
defer span.Finish()
ctx := opentracing.ContextWithSpan(context.Background(), span)
session := db.WithContext(ctx)
p := &Product{Code: strconv.Itoa(t), Price: uint(rand.Intn(1<<10))}
session.Create(p)
session.First(p, p.ID)
session.Delete(p, p.ID)
wg.Done()
}(i)
}
wg.Wait()
}
ä¸åºæå¤ï¼ç´æ¥ææçå°æ èæ´¾æå®æºäº
2020/08/15 00:54:29 D:/GoPath/src/gormTracing/gormTracing_test.go:122 Error 1040: Too many connections
[224.000ms] [rows:0] INSERT INTO `products` (`created_at`,`updated_at`,`deleted_at`,`code`,`price`) VALUES ("2020-08-15 00:54:29.153","2020-08-15 00:54:29.153",NULL,"152",206)
[mysql] 2020/08/15 00:54:44 packets.go:36: read tcp 192.168.50.3:11966->113.119.123.000:10012: wsarecv: An existing connection was forcibly closed by the remote host.
[mysql] 2020/08/15 00:54:44 packets.go:36: read tcp 192.168.50.3:11972->113.119.123.000:10012: wsarecv: An existing connection was forcibly closed by the remote host.
æ¾å°å ¶ä¸ä¸ä¸ªåéèï¼å¯ä»¥çå°é¾è·¯è¿½è¸ªè®°å½ä¸åççé误ï¼æå ¥æä½é¿æ¶é´è·åä¸å°æ°æ®åºè¿æ¥åçé»å¡çæ åµã
å ã GORM V2 é¨åæºç é 读
1. DBåSession对象
gorm.go
// DB GORM DB definition
type DB struct {
// é
置信æ¯
*Config
// é误
Error error
// sqlæ§è¡å½±åçè¡æ°
RowsAffected int64
// ä¸å¤ªå¥½ç¿»è¯ï¼ä¸ªäººè§å¾åºè¯¥å«ç¶æï¼å 为è¿ä¸ªå段
// ä¿åäºGORMçDBç°åºï¼ä¸»è¦ç¨æ¥è¿è¡é¾å¼æä½
// ---> é常éè¦å¦
Statement *Statement
// å
éæ°ï¼åå§å建çDB为1ï¼åè£å建Sessionä¹åSessionæ¯1
// å¦æä¼è¯ä¸ºDEBUG模å¼å为2
clone int
}
// Session session config when create session with Session() method
type Session struct {
// å¦æ为çï¼ååªçæSQLè¯å¥èä¸æ§è¡
DryRun bool
// stmté¢å¤çï¼å¦æ为çåè¿è¡SQLé¢å¤çï¼é¿å
SQL注å
¥
PrepareStmt bool
// æ¯å¦ææ¡ä»¶ï¼å¦æSessionå¤å¨Debug模å¼å为ç
WithConditions bool
// å½±åMySQLçäºå¡æä½
SkipDefaultTransaction bool
// ä¸ä¸æ --> è½ä¼ éä¸ä¸æå°±è½å®ç°å¾å¤éªæä½
Context context.Context
// æ¥å¿å¯¹è±¡
Logger logger.Interface
// å建å½åæ¶é´æ³çæ¹æ³
NowFunc func() time.Time
}
æ¯è¾æææçæ¯DBç»æä½çCloneå段ï¼ä¼å½±åDBé¾å¼æä½
gorm.go
func (db *DB) getInstance() *DB {
if db.clone > 0 {
tx := &DB{Config: db.Config}
if db.clone == 1 {
// clone with new statement
tx.Statement = &Statement{
DB: tx,
ConnPool: db.Statement.ConnPool,
Context: db.Statement.Context,
Clauses: map[string]clause.Clause{},
}
} else {
// with clone statement
tx.Statement = db.Statement.clone()
tx.Statement.DB = tx
}
return tx
}
return db
}
å¦æcloneå段çäº0ï¼åç´æ¥è¿ådbï¼å¨é¾å¼æä½çè¿ç¨éè¦ä¸æå å WHEREãJOINè¿ç±»æ¹æ³æ¶åï¼ä½å¹¶ä¸éè¦äº§çæ°çDBï¼å¯ä»¥ç´æ¥è¿åDB
大äº0çæ åµå°±æåºå«çï¼é¦å æ¯æç®åçï¼çäº1çæ åµï¼ä¼å建ä¸ä¸ªæ°çDB对象ï¼å¤å¶MySQLè¿æ¥å¯¹è±¡ï¼è¿æ¥æ± ï¼ä¸ä¸æï¼ä»¥åå ³èä¿¡æ¯é ç½®ã
ä¸çäº1çæ
åµï¼å°±æ¯DEBUG模å¼ï¼å¨DEBUG模å¼ä¸ï¼Clone为2ï¼æå³çéè¦è°ç¨db.Statement.clone()
ï¼å°å
¨é¨ç¶æé½æ·è´è¿æ¥ï¼å
æ¬Settingsè¿ä¸ªå¹¶åMap
// Debug start debug mode
func (db *DB) Debug() (tx *DB) {
return db.Session(&Session{
WithConditions: true,
Logger: db.Logger.LogMode(logger.Info),
})
}
// Session create new db session
func (db *DB) Session(config *Session) *DB {
...
if config.WithConditions {
tx.clone = 2
}
...
return tx
}
2.Statment对象
该ç»æä½åºæ¬ä¸åå¨äºææé¾å¼æä½éè¦ä¿åç¶æçä¿¡æ¯
type Statement struct {
// æåå½åææStatementçDB
*DB
// Joinçä¿¡æ¯ï¼å°±æ¯`FROM A JOIN B WHERE A.id = B.id`
TableExpr *clause.Expr
// å½åç表
Table string
// 模å
Model interface{}
// 常ç¨äºæ°¸ä¹
å é¤ï¼è·³è¿è½¯å é¤ï¼
Unscoped bool
// ç®æ 对象ï¼å°±æ¯æ们Findæè
Getæä½æ¶éè¦èµå¼ç对象
Dest interface{}
// åå°å¼ ---> åªéè¦è¿è¡ä¸æ¬¡åå°å°±è½è·åï¼ä¼åäºåå°çæ¶é´
ReflectValue reflect.Value
// å
³èï¼é«çº§æ¥è¯¢ç¨
Clauses map[string]clause.Clause
// ç»ææ¯å¦å»é
Distinct bool
// SELECTçå段
Selects []string // selected columns
Omits []string // omit columns
Joins map[string][]interface{}
Preloads map[string][]interface{}
// ---> éç¹ Settings
Settings sync.Map
ConnPool ConnPool
Schema *schema.Schema
// --> éç¹ä¸ä¸æ
Context context.Context
RaiseErrorOnNotFound bool
UpdatingColumn bool
// --> éç¹ SQLè¯å¥
SQL strings.Builder
// --> éç¹ SQLæä½çåæ°
Vars []interface{}
// å¦æç®æ 对象为åçæè
æ°ç»ï¼è¿éè®°å½äºå½åç®æ çä¸æ
CurDestIndex int
attrs []interface{}
assigns []interface{}
}
3. Settingså段ï¼Sync.Mapï¼ä»£æ¿Contextä¼ éæ°æ®
// Set store value with key into current db instance's context
func (db *DB) Set(key string, value interface{}) *DB {
tx := db.getInstance()
tx.Statement.Settings.Store(key, value)
return tx
}
// Get get value with key from current db instance's context
func (db *DB) Get(key string) (interface{}, bool) {
return db.Statement.Settings.Load(key)
}
// InstanceSet store value with key into current db instance's context
func (db *DB) InstanceSet(key string, value interface{}) *DB {
tx := db.getInstance()
tx.Statement.Settings.Store(fmt.Sprintf("%p", tx.Statement)+key, value)
return tx
}
// InstanceGet get value with key from current db instance's context
func (db *DB) InstanceGet(key string) (interface{}, bool) {
return db.Statement.Settings.Load(fmt.Sprintf("%p", db.Statement) + key)
}
é¦å
æ们已ç»ç¥édb.Statement.Settings
æ¯ä¸ä¸ªSync.Map
ï¼å¹¶åå®å
¨çMapï¼ï¼Set
ãGet
æ¹æ³ä¸InstanceSet
ãInstanceGet
æ大çåºå«å°±æ¯ å å
¥äºdb
çå°åä½ä¸ºé®å¼ï¼è¿æ ·æå©äºæ们ä¿è¯ä¸ä¸æä¼ éçæ°æ®å¯ä¸ï¼åæ¶å®ç°äºå¹¶åå®å
¨ã
4. CallBacksæ件ç使ç¨
ä½ä¸ºä¾µå
¥GORMæ¡æ¶çå
³é®ä¸ç¯ï¼æ件çç使ç¨ä¸å¯è·åï¼å®ç°çé»è¾å
¶å®é常ç®åï¼å°±æ¯å®ä¹ä¸ä¸ªæ¥å£ï¼æ们å®ç°äºè¿ä¸ªæ¥å£å°±è½ï¼~~èæå¦ä¸º~~ï¼éå¿æ欲æä½å
³é®çDB
对象ã
// Plugin GORM plugin interface
type Plugin interface {
Name() string
Initialize(*DB) error
}
func (db *DB) Use(plugin Plugin) (err error) {
// è·åä¸ä¸æ件çå
name := plugin.Name()
// å¦æ没æååå°±åå§åæ件ï¼å¦ææååå°±ç´æ¥æ¥é
if _, ok := db.Plugins[name]; !ok {
if err = plugin.Initialize(db); err == nil {
db.Plugins[name] = plugin
}
} else {
return ErrRegistered
}
return err
}
谢谢é 读ï¼ç¥è¯æ ä»·ï¼å¸æè½å¸®å°æ¨ã