程序员社区

【Go Web开发】连接PostgreSQL数据库

上一篇文章,项目中的greenlight数据库已经创建好了,接下来一起看看如何从Go应用程序连接到数据库。

要连接SQL数据库,我们需要使用一个数据库驱动程序,作为Go和数据库之间的“中间人”。你可以在Go wiki中找到一个可用的PostgreSQL驱动程序列表,但对于我们的项目,我们将选择pq包,它的特点是常用、可靠和功能完善。

如果你跟随本系列文章操作,那么使用go get下载pq的v1.10.0版本,如下所示:

$ go get github.com/lib/pq@v1.10.0
go: downloading github.com/lib/pq v1.10.0

为了连接到数据库,我们还需要一个数据源名称(DSN),它是一个包含必要连接参数的字符串。DSN格式将取决于您使用的数据库驱动程序(应该在驱动程序文档中有描述),但当使用pq时,以greenlight用户结合DSN,您应该能够连接到您的本地greenlight数据库:

 postgres://greenlight:pa55word@localhost/greenlight

创建数据库连接池

我们在Go应用程序连接到greenlight数据库的代码几乎与mysql中的代码完全相同。我就不细讲了,希望大家都很熟悉。

包含以下几点:

  • 我们希望DSN在运行时可配置,因此我们将使用命令行参数将它传递给应用程序,而不是硬编码。在开发过程中,为了简单起见,我们将使用上面的DSN作为参数的默认值。
  • 在cmd/api/main.go文件中我们创建一个新的openDB()函数。在该函数中,使用sql.Open()来建立sql.DB连接池,因为数据库连接是在第一次需要时惰性地建立的。因此,我们还需要使用db.PingContext()方法来创建一个实际的连接,并验证数据库设置正确。

回到cmd/api/main.go文件更新代码如下:

File: cmd/api/main.go


package main

import (
    "context" 
    "database/sql"
    "flag"
    "fmt"
    "log"
    "net/http"
    "os"
    "time"
        //导入pq驱动程序,通过包database/sql来注册数据库驱动程序
    _ "github.com/lib/pq"
)

const version = "1.0.0"

//添加一个db结构字段来保存数据库连接的配置
type config struct {
    port int
    env  string
    db   struct {
        dsn string
    }
}
type application struct {
    config config
    logger *log.Logger
}

func main() {
    var cfg config
    flag.IntVar(&cfg.port, "port", 4000, "API server port")
    flag.StringVar(&cfg.env, "env", "development", "Environment (development|staging|production)")
    // 将DSN值从db-dsn命令行参数中读入配置,设置默认值
    flag.StringVar(&cfg.db.dsn, "db-dsn", "postgres://greenlight:pa55word@localhost/greenlight", "PostgreSQL DSN")
    flag.Parse()
    logger := log.New(os.Stdout, "", log.Ldate|log.Ltime)
  
    //调用openDB()帮助函数(参见下面)来创建连接池
    db, err := openDB(cfg)
    if err != nil {
        logger.Fatal(err)
    }
  
    // defer调用, 以便main()函数退出之前关闭连接池。
    defer db.Close()
  
    //打印连接数据库成功日志
    logger.Printf("database connection pool established")
    app := &application{config: cfg,
        logger: logger}
    srv := &http.Server{
        Addr: fmt.Sprintf(":%d", cfg.port), Handler: app.routes(),
        IdleTimeout: time.Minute,
        ReadTimeout: 10 * time.Second, WriteTimeout: 30 * time.Second,
    }
    logger.Printf("starting %s server on %s", cfg.env, srv.Addr)
    err = srv.ListenAndServe()
    logger.Fatal(err)
}

//openDB()函数返回一个sql.DB连接池。
func openDB(cfg config) (*sql.DB, error) {
    //使用sql.Open()创建一个空连接池
    db, err := sql.Open("postgres", cfg.db.dsn)
    if err != nil {
        return nil, err
    }
 
    //创建一个具有5秒超时期限的上下文。
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
 
    //使用PingContext()建立到数据库的新连接,并传入上下文信息,连接超时就返回
    err = db.PingContext(ctx)
    if err != nil {
        return nil, err
    }
    // 返回sql.DB连接池
    return db, nil
}

重要提示:我们将在本书的后面讨论如何使用上下文来正确地管理超时,所以现在不要过多地担心这个问题。现在,只需要知道PingContext()调用不能在5秒内完成,将返回一个超时错误。

接下来启动应用程序,你将看到条关于连接数据库成功的日志,如下所示:

$ go run ./cmd/api
2021/04/07 15:56:54 database connection pool established 
2021/04/07 15:56:54 starting development server on :4000

解耦DSN

目前,DSN的命令行参数默认值是在cmd/api/main.go文件中写死的。尽管DSN中的用户名和密码只是用于您自己机器上的开发数据库,但最好不要将这些敏感信息硬编码到项目文件中(这些文件可能在未来被共享或分发)。

因此,我们采取一些步骤将DSN从我们的项目代码中解耦,并将其存储为本地机器上的一个环境变量。

如果你按照本文操作,先创建GREENLIGHT_DB_DSN环境变量,在$HOME/.profile文件中添加如下内容:

File: $HOME/.profile

...
export GREENLIGHT_DB_DSN='postgres://greenlight:pa55word@localhost/greenlight'

或者在$HOME/.bashrc文件中添加以上内容也可以。

完成上面操作之后,需要重新启动您的计算机,如果现在不方便重启的话,可以在刚刚编辑的文件上运行source命令来实现环境变量生效。例如:

$ source $HOME/.profile

注意:运行source命令只会在当前的终端窗口生效。因此,如果您切换到另一个终端,您需要再次运行source生效。

不管怎样,一旦您重启机器或运行source,通过运行echo命令就可以在终端中看到GREENLIGHT_DB_DSN环境变量的值。像这样:

 $ echo $GREENLIGHT_DB_DSN
postgres://greenlight:pa55word@localhost/greenlight

下面更新cmd/api/main.go文件,可以通过访问环境变量来获取DSN值。Go中读取环境变量值可以调用os.Getenv()函数,然后将DSN命令行参数默认值设置为环境变量值。

File:cmd/api/main.go


package main

...

func main() {
var cfg config

flag.IntVar(&cfg.port, "port", 4000, "API server port")
flag.StringVar(&cfg.env, "env", "development", "Environment (development|staging|production)")

// 使用GREENLIGHT_DB_DSN环境变量,作为DSN命令行参数默认值
flag.StringVar(&cfg.db.dsn, "db-dsn", os.Getenv("GREENLIGHT_DB_DSN"), "PostgreSQL DSN")
flag.Parse()
...
}

如果您现在再次重新启动应用程序,您应该会发现可以正常编译,并像之前一样工作。

您还可以尝试在运行应用程序时指定-help标志。应该会输出三个命令行参数的描述内容和默认值,包括从环境变量中提取的DSN值。类似于:

$ go run ./cmd/api -help
Usage of /tmp/go-build417842398/b001/exe/api: 
  -db-dsn string
    PostgreSQL DSN (default "postgres://greenlight:pa55word@localhost/greenlight") 
  -env string
    Environment (development|staging|production) (default "development") 
  -port int
    API server port (default 4000)

附加内容

psql使用DSN连接数据库

将DSN存储在环境变量中的一个很好处是:能够以greenlight用户轻松地连接到greenlight数据库,而不是在运行psql时手动指定所有连接配置。像这样:

$ psql $GREENLIGHT_DB_DSN
psql (13.4)
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off) 
Type "help" for help.

greenlight=>
赞(0) 打赏
未经允许不得转载:IDEA激活码 » 【Go Web开发】连接PostgreSQL数据库

一个分享Java & Python知识的社区