为了账号安全,请及时绑定邮箱和手机立即绑定

多数据库驱动场景:将接口作为类型传递

多数据库驱动场景:将接口作为类型传递

Go
哆啦的时光机 2022-06-06 16:45:12
场景:我有一个数据库类型实现了一些 CRUD 操作。我想:抽象出数据库层以支持多个数据库将缓存层“放在它前面”作为通读缓存所以我的想法是:为 postgres 特定代码和缓存创建数据库接口将 postgres 类型传递给缓存构造函数,以便它可以从数据库中使用它请参阅下面的内容,了解我在分配时导致 cacheNew() 错误:不能使用 dbdriver(接口 {} 类型的变量)作为结构文字中的数据库值:缺少方法 GetUser解决这个问题的最佳方法是什么?package mainimport "fmt"type database interface {    GetUser(string)}type postgres struct {    hostname string}func (p *postgres) GetUser(u string) {    fmt.Printf("Postgres: GetUser %s\n", u)}type cache struct {    db database}func cacheNew(dbdriver interface{}) cache {    return cache{        db: dbdriver,    }}func (c *cache) GetUser(u string) {    fmt.Printf("Cache: GetUser %s\n", u)    c.db.GetUser(u)}func main() {    // var db database    db := postgres{        hostname: "x",    }    db.GetUser("joe")    dbViaCache := cacheNew(db)    dbViaCache.GetUser("joe")}
查看完整描述

3 回答

?
繁华开满天机

TA贡献1816条经验 获得超4个赞

在函数中使用database而不是interface{}cacheNew

func cacheNew(dbdriver database) cache {

postgres没有实现database,因为GetUser方法有指针接收器。因此,将postgres类型变量的地址发送到cacheNew

dbViaCache := cacheNew(&db)


查看完整回答
反对 回复 2022-06-06
?
梦里花落0921

TA贡献1772条经验 获得超6个赞

无论是基于意见的,我都不建议混合使用数据库和缓存功能。


我可以推荐以下内容:首先创建包cache,然后从接口开始cache.go:


// Cache is an interface which abstracts cache providers details for testability and usability

type Cache interface {

    // Get retrieves the content associated with the given key. decoding it into the given

    // pointer.

    //

    // Returns:

    //   - nil if the value was successfully retrieved and ptrValue set

    //   - ErrCacheMiss if the value was not found in the cache

    //   - an implementation specific error otherwise

    Get(key string, ptrValue interface{}) error


    // set the given key/value in the cache, overwriting any existing value

    // associated with that key

    Set(key string, ptrValue interface{}, expires time.Duration) error


}

然后你可以在这个包中创建文件,比如inmem.go,,redis.go实现这个接口的所有其他东西。然后,您可以准备 YAML 配置,指示您要使用的缓存提供程序,并在服务器启动期间对其进行实例化。


cache:

  active: "redis"

  redis:

    address: "127.0.0.1:6379"

    password: # 

    poolSize: #

对于数据库,一切都比较棘手。根据我的经验,一次选择数据库提供程序并使用jmoiron/sqlx甚至本机驱动程序会更容易,例如用于高负载处理程序的纯pgx 。由于缺乏泛型和相对较慢的反射,除了基本的 CRUD 之外,大多数情况下对数据库提供者的抽象是不切实际的。对于非常简单的情况,您可以模仿存储库模式


type UserRepository interface {

    Get(ctx context.Context, id uint64) (*User, error)

}


type userRepository struct {

    db *sqlx.DB

}


func (r *userRepository) Get(ctx context.Context, id uint64) (*User, error) {

    const query = `SELECT id, email, login, language_id FROM users WHERE id = $1`

    user := &User{}

    if err := r.db.GetContext(ctx, user, query, id); err != nil {

        return nil, err

    }

    return user, nil

}

然后在您的服务中,您将拥有类似的东西


    key := "app:users#23"

    user := &User{}

    if err := svc.cache.Get(key, user); err != nil {

        user, err = svc.userRepo.Get(userID)

        if err != nil {

            // handle 

        }

        // otherwise, set cache

        go svc.cache.Set(key, user, time.Minutes * 15)

    }


    // perform other actions with user 

希望能帮助到你!


查看完整回答
反对 回复 2022-06-06
?
繁花如伊

TA贡献2012条经验 获得超12个赞

为了解决您的问题,添加 newPostgres 构造函数。下面我对您的初始代码进行了更正。


package main


import "fmt"


type database interface {

    GetUser(string)

}


type postgres struct {

    hostname string

}


func newPostgres(hostname string) *postgres {

    return &postgres{

        hostname: hostname,

    }

}


func (p *postgres) GetUser(u string) {

    fmt.Printf("Postgres: GetUser %s\n", u)

}


type cache struct {

    db database

}


func cacheNew(db database) cache {

    return cache{

        db: db,

    }

}


func (c *cache) GetUser(u string) {

    fmt.Printf("Cache: GetUser %s\n", u)


    c.db.GetUser(u)

}


func main() {

    db := newPostgres("x")


    db.GetUser("joe")


    dbViaCache := cacheNew(db)

    dbViaCache.GetUser("joe")

}


查看完整回答
反对 回复 2022-06-06
  • 3 回答
  • 0 关注
  • 104 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信