使用Gin,MySQL和Docker开发博客(Part 1)


原文地址

本文侧重的读者

这部分内容重点不在Go基础。侧重于如何构建后端服务。内容特别适合热衷于使用Gin框架来做web开发的读者。

学习目标

这个系列介绍如何使用Go来创建REST APIs基础。完成这个系列后你就学会了以下内容:

  • 基于MySQL和Docker配置本地Go开发环境
  • Gin框架实现CRUD接口
  • DDD领域驱动开发
  • Go依赖注入
  • 使用Firebase认证等

要求

  • 安装Go(go1.16.3版本 linux/amd64)
  • Docker (版本 20.10.6)和Docker-compose(版本1.29.1)
  • 最好是Linux开发环境
  • 理解Restful服务/API

以上提到的工具已经在我的机器上安装。你可以在这个链接中找到本系列内容的所有源代码。

开始

首先在你的机器上面创建blog目录。然后在blog目录下使用如下命令初始化Go module。

go mod init blog

上面的命令创建了一个go.mod文件。它将允许您管理项目依赖包。让我们添加一些依赖项。

go get github.com/gin-gonic/gin

上面的命令将安装Gin框架和Gorm并创建go.sum文件。该文件存储有关依赖项及其版本信息。
Gin是一个轻量级的、文档丰富的、快速的HTTP web框架。创建者声称,Gin的速度比其他类似的框架快40倍。

启动Hello world服务器

在项目根路径创建一个main.go文件;打开您喜欢的编辑器并编写以下代码:

package main

import (
  "net/http"
  "github.com/gin-gonic/gin"
)

func main() {
  router := gin.Default() //new gin router initialization
  router.GET("/", func(context *gin.Context) {
    context.JSON(http.StatusOK, gin.H{"data": "Hello World !"})    
  }) // first endpoint returns Hello World
  router.Run(":8000") //running application, Default port is 8080
}

我们梳理下main.go中的代码:

  • package main:每个go文件都是一个包。main包是一个项目的入口。
  • import:导入项目的依赖
  • func main:当运行项目时,首先会调main函数。在router上注册了一个/路径。配置一个路由或web接口需要做两件事:
  • 1、Endpoint:它是获取数据的路径。例如,如果访问者想获得所有的帖子,他们将获取/posts这个端点。
  • 2、Handler:它决定如何向endpoint提供数据,是业务逻辑,比如从数据库获取或保存数据,验证用户输入等等。上下文对象的JSON方法用于发送JSON格式的数据。此方法以HTTP状态代码和JSON响应作为参数。
    使用如下命令运行项目:
go run main.go

如果您看到类似于下面的输出,这意味着您的web服务已经工作了。

[GIN-debug] GET    /                         --> main.main.func1 (3 handlers)
[GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
[GIN-debug] Listening and serving HTTP on :8000

在浏览器上访问http://localhost:8000/。你应该看到{"data":"Hello World !"}。

配置docker

上述运行服务的方法是Go运行应用程序常用方式。让我们通过Docker的方式来运行go项目。 在项目的根目录下创建一个Dockerfile文件,并添如下代码:

FROM golang:alpine

RUN mkdir /app

WORKDIR /app

ADD go.mod .
ADD go.sum .

RUN go mod download
ADD . .

RUN go get github.com/githubnemo/CompileDaemon

EXPOSE 8000

ENTRYPOINT CompileDaemon --build="go build main.go" --command=./main

要从上面的Dockerfile构建docker容器,请先使用下面的命令创建容器镜像:

docker build -t hello_world :1.0 .

可以在终端上使用docker ps -a查看机器上运行的容器。可以看到如下容器信息:

CONTAINER ID   IMAGE                    COMMAND                  CREATED          STATUS                      PORTS     NAMES
62cddf7c2659   hello_world:1.0      "/bin/sh -c 'Compile…"   23 minutes ago   Exited (0) 23 minutes ago             gifted_kilby

可以使用如下命令,运行容器:

docker run -p 8000:8000 hello_world:1.0

在浏览器上访问http://localhost:8000/。你应该看到{"data":"Hello World !"}。
让我们回顾Dockerfile中提到的Docker命令:

  • FROM:FROM表示容器使用的基础映像。golang:1.16是一个基于linux的镜像,它已经安装了golang,没有其他的程序或软件。
  • WORKDIR:WORKDIR修改工作目录。在我们的例子中是切换到 /app目录。它为后续命令创建一个工作目录。
  • ADD:ADD指令将文件从一个位置复制到另一个位置。ADD [SOURCE] [DESTINATION]是该命令的语法。类似地,还有一个COPY命令用于类似的目的。这里,我们先拷贝go.sum和go.mod文件这样在拷贝其他文件之前,我们就可以安装所有的依赖。
  • RUN:RUN指令将在当前镜像基础上执行任意命令,命令生成新的文件层并提交镜像。生成并提交的映像将用于Dockerfile的下一步。
  • EXPOSE:EXPOSE指示运行在Docker容器上的服务监听端口为8000。
  • ENTRYPOINT:一旦从镜像中创建容器,Entrypoint就会在容器内运行该命令。在Dockerfile中只能有一条Entrypoint指令。如果使用了多个Entrypoint指令,则只执行最后一条。在这里,一旦创建了容器,Entrypoint命令将运行我们的golang项目。

配置数据库

需要MySQL包来连接数据库和应用程序。使用以下命令安装它。

go get gorm.io/driver/mysql gorm.io/gorm

Gorm:是Golang中的一个功能强大的ORM库。 因为连接数据库是应用程序的基本功能。让我们创建一个infrastructure文件夹并在此文件夹里创建db.go文件。写入如下代码:

package infrastructure

import (
    "fmt"
    "os"
    "time"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
)

//Database struct
type Database struct {
    DB *gorm.DB
}

//NewDatabase : intializes and returns mysql db
func NewDatabase() Database {
    USER := os.Getenv("DB_USER")
    PASS := os.Getenv("DB_PASSWORD")
    HOST := os.Getenv("DB_HOST")
    DBNAME := os.Getenv("DB_NAME")

    URL := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8&parseTime=True&loc=Local", USER, PASS, 
    HOST, DBNAME)
    fmt.Println(URL)
    db, err := gorm.Open(mysql.Open(URL))

    if err != nil {
        panic("Failed to connect to database!")

    }
    fmt.Println("Database connection established")
    return Database{
        DB: db,
    }
}

在NewDatabase函数里面主要做了以下工作:
1、从环境变量中获取数据库账号密码等信息:USER, PASS, HOST和DBNAME。
2、将连接数据库URL使用环境变量拼装好,存在URL变量里。
3、根据URL使用gorm.Open方法来创建mysql连接。
4、最后,以gorm数据库实例作为参数返回database结构体,以后应用程序访问数据库就使用该结构体实例。

环境变量

项目中有两种类型的变量,程序变量和环境变量。程序变量是在代码块或模块中存储值的普通变量,而环境变量在整个项目中都是可用的。

设置环境变量

可以使用各种方法设置环境变量。这里,我们使用.env文件来设置它们。在项目根目录上创建.env文件,并在文件中添加以下变量。

MYSQL_ROOT_PASSWORD=root
DB_USER=user
DB_PASSWORD=Password@123
DB_HOST=db
DB_PORT=3306
DB_NAME=golang
SERVER_PORT=8000
ENVIRONMENT=local

读取环境变量

现在,我们编写代码来读取.env文件。创建文件env。进入infracture文件夹,添加以下代码:

package infrastructure

import (
    "log"
    "github.com/joho/godotenv"
)

//LoadEnv loads environment variables from .env file
func LoadEnv() {
    err := godotenv.Load(".env")
    if err != nil {
        log.Fatalf("unable to load .env file")
    }
}

通过以下命令安装godotenv包:

go get github.com/joho/godotenv

为了加载.env文件和数据库,我们需要编辑main.go,如下所示:

func main() {

    router := gin.Default()

    router.GET("/", func(context *gin.Context) {
        infrastructure.LoadEnv()  //加载环境变量文件
        infrastructure.NewDatabase() //创建数据库连接
        context.JSON(http.StatusOK, gin.H{"data": "Hello World !"})
    })
    router.Run(":8000")
}

Docker Compose文件

我们需要docker compose来定义和运行多容器应用服务。在我们的案例中包括数据库和Gin应用程序。在项目中创建docker-compose.yml,并写入以下代码:

version: '3'
services:
  db:
    image: mysql/mysql-server:5.7
    ports:
      - "3305:3306"
    environment:
      - "MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}"
      - "MYSQL_USER=${DB_USER}"
      - "MYSQL_PASSWORD=${DB_PASSWORD}"
      - "MYSQL_DATABASE=${DB_NAME}"   
  web:
    build: .
    ports:
      - "8000:8000"
    volumes:
      - ".:/app"
    depends_on:
      - db
    links:
      - "db:database"

让我们回顾一下compose文件中提到的术语。

  • version:Docker compose版本
  • services:services部分定义了所有将创建的容器。这里我们有两个容器服务即web和db
  • web:这是Gin app服务的名称。可以自定义任意名称。Docker compose将使用这个名称创建容器。
  • build:这个术语指定了Dockerfile文件的位置,点号表示docker-compose.yml就在的当前目录。Dockerfile用于构建容器映像,根据它来运行容器。我们也可以在这里输入Dockerfile的路径。
  • ports:ports术语用于将容器端口映射或暴露给主机。这里映射端口“8000:8000”,这样我们就可以在主机的8000端口上访问我们的服务。
  • volumes:在这里,我们将代码文件目录附加到容器的./app目录,这样我们就不必为文件的每次更改去重新构建映像。这也有助于在调试模式下自动重新加载服务。
  • links:links就是将一个服务链接到另一个服务。这里,我们将数据库容器链接到web容器,这样我们的web容器就可以在bridge网中访问数据库。(提示! !)如果你想详细了解docker网络请参考:容器网络
  • image:如果我们没有Dockerfile,想要使用已经构建的镜像直接运行服务,我们可以使用' image '子句指定镜像位置。Compose将从指定位置拉镜像并从中创建一个容器。在我们的例子中,使用mysql/mysql-server:5.7镜像来创建数据库服务。
  • environment:需要在容器中设置任何环境变量都可以使用“environment”术语创建。
environment:
      - "MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}"

${MYSQL_ROOT_PASSWORD}和其他变量是从.env中读取的。 现在你已经准备好运行容器启动文件。使用以下命令开始构建并运行。

 docker-compose up --build

如果在你的终端上看到下面这样的信息,这意味着你的服务器已经运行:

db_1   | 2021-05-24T09:18:02.841094Z 0 [Note] Event Scheduler: Loaded 0 events
db_1   | 2021-05-24T09:18:02.841370Z 0 [Note] mysqld: ready for connections.
db_1   | Version: '5.7.34'  socket: '/var/lib/mysql/mysql.sock'  port: 3306  MySQL Community Server (GPL)
web_1  | 2021/05/24 09:18:06 Running build command!
web_1  | 2021/05/24 09:18:17 Build ok.
web_1  | 2021/05/24 09:18:17 Restarting the given command.
web_1  | 2021/05/24 09:18:17 stdout: [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
web_1  | 2021/05/24 09:18:17 stdout:
web_1  | 2021/05/24 09:18:17 stdout: [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
web_1  | 2021/05/24 09:18:17 stdout:  - using env:      export GIN_MODE=release
web_1  | 2021/05/24 09:18:17 stdout:  - using code:     gin.SetMode(gin.ReleaseMode)
web_1  | 2021/05/24 09:18:17 stdout:
web_1  | 2021/05/24 09:18:17 stdout: [GIN-debug] GET    /                         --> main.main.func1 (3 handlers)
web_1  | 2021/05/24 09:18:17 stdout: [GIN-debug] Listening and serving HTTP on :8000

在浏览器输入http://localhost:8000/并检查终端,会输出数据库连接的日志信息。
本文源代码仓库地址https://github.com/umschaudhary/hashnode-series

总结

以上就是本系列的第一部分。 下一部分重点:

  • 向应用程序添加模型(结构)
  • 采用领域驱动开发模式

请关注我以获取下一部分的更新或订阅,这样您就不会错过我即将发表的文章。 谢谢你! !

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容