drone源码-gin-context实现多数据库系统
多数据库 和 多remote是如何实现的?
- drone-server启动时通过数据库配置参数 进行初始化数据库
--driver value database driver (default: "sqlite3") [$DRONE_DATABASE_DRIVER, $DATAB
ASE_DRIVER]
--datasource value database driver configuration string (default: "drone.sqlite") [$DRO
NE_DATABASE_DATASOURCE, $DATABASE_CONFIG]
~/go/src/github.com/drone/drone/cmd/drone-server/server.go
//配置要使用git仓库
remote_, err := SetupRemote(c)
if err != nil {
logrus.Fatal(err)
}
//配置要使用的数据库
store_ := setupStore(c)
setupEvilGlobals(c, store_, remote_)
// we are switching from gin to httpservermux|treemux,
// so if this code looks strange, that is why.
tree := setupTree(c)
// setup the server and start the listener
handler := router.Load(
tree,
ginrus.Ginrus(logrus.StandardLogger(), time.RFC3339, true),
middleware.Version,
middleware.Config(c), // ~/go/src/github.com/drone/drone/router/middleware/config.go
middleware.Store(c, store_), // ~/go/src/github.com/drone/drone/router/middleware/store.go
middleware.Remote(remote_), // ~/go/src/github.com/drone/drone/router/middleware/remote.go
)
- 数据库配置完成之后
store_ := setupStore(c)
得到 store.Store 对象接口,它包含了所有
type Store interface {
// GetUser gets a user by unique ID.
GetUser(int64) (*model.User, error)
...
}
~/go/src/github.com/drone/drone/cmd/drone-server/setup.go
func setupStore(c *cli.Context) store.Store {
return datastore.New(
c.String("driver"),
c.String("datasource"),
)
}
通过router.Load(middleware.Store(c, store_)) 加载到gin的Context 中
这里 Store(cli *cli.Context, v store.Store) 返回的 gin.HandlerFunc 所以在drone-server 在每次处理 GET,POST等请求时
都会将store_ 接口对象 存储到 gin 的context中
~/go/src/github.com/drone/drone/router/middleware/store.go// Store is a middleware function that initializes the Datastore and attaches to // the context of every http.Request. func Store(cli *cli.Context, v store.Store) gin.HandlerFunc { return func(c *gin.Context) { store.ToContext(c, v) c.Next() } }
store.ToContext(c, v) //
这里ToContext(c Setter, store Store) 传入两个参数,第二个参数是实现Store接口的对象
第一个参数是实现Set()方法的的接口就可以了,传递进来的是一个 *gin.Context, 是实现了Set()方法的。
这样就把数据库的接口对象 保存到了 gin 的Context中了~/go/src/github.com/drone/drone/store/context.go
const key = "store" // Setter defines a context that enables setting values. type Setter interface { Set(string, interface{}) } // ToContext adds the Store to this context if it supports // the Setter interface. func ToContext(c Setter, store Store) { c.Set(key, store) //调用gin.Context 的func (c *Context) Set(key string, value interface{}) 方法 }
查看gin的Set()实现
~/go/src/github.com/drone/drone/vendor/github.com/gin-gonic/gin/context.go
// 1. 检测 gin.Context 是否实现 context.Context 接口
var _ context.Context = &Context{}
// 2. 158行 gin.Context 实现Setter接口
/************************************/
/******** METADATA MANAGEMENT********/
/************************************/
// Set is used to store a new key/value pair exclusivelly for this context.
// It also lazy initializes c.Keys if it was not used previously.
func (c *Context) Set(key string, value interface{}) {
if c.Keys == nil {
c.Keys = make(map[string]interface{})
}
c.Keys[key] = value
}
// 3. 553行 gin.Context 重新实现 Context 接口
/************************************/
/***** GOLANG.ORG/X/NET/CONTEXT *****/
/************************************/
func (c *Context) Value(key interface{}) interface{} {
if key == 0 {
return c.Request
}
if keyAsString, ok := key.(string); ok {
val, _ := c.Get(keyAsString)
return val
}
return nil
}
经过以上步骤,gin在router加载的时候就将所需的remote和store写入gin.Context中
~/go/src/github.com/drone/drone/router/router.go
// Load loads the router
func Load(mux *httptreemux.ContextMux, middleware ...gin.HandlerFunc) http.Handler {
e := gin.New()
e.Use(gin.Recovery())
e.Use(header.NoCache)
e.Use(header.Options)
e.Use(header.Secure)
e.Use(middleware...)
// 以依赖注入形式,注册进来
// middleware.Version,
// middleware.Config(c), // ~/go/src/github.com/drone/drone/router/middleware/config.go
// middleware.Store(c, store_), // ~/go/src/github.com/drone/drone/router/middleware/store.go
// middleware.Remote(remote_), // ~/go/src/github.com/drone/drone/router/middleware/remote.go
e.Use(session.SetUser())
e.Use(token.Refresh)
e.NoRoute(func(c *gin.Context) {
req := c.Request.WithContext(
web.WithUser(
c.Request.Context(),
session.User(c),
),
)
mux.ServeHTTP(c.Writer, req)
})
e.GET("/logout", server.GetLogout)
e.GET("/login", server.HandleLogin)
e.POST("/hook", server.PostHook) //处理push event关键
e.POST("/api/hook", server.PostHook)
e.GET("/healthz", server.Health)
return e
}
使用
/home/ubuntu/go/src/github.com/drone/drone/server/hook.go
func PostHook(c *gin.Context) {
remote_ := remote.FromContext(c)
user, err := store.GetUser(c, repo.UserID)
}