快速开始构建一个简单项目

前言

所有项目都是从一个文件,一个 main 中开始的,可以根据以下步骤进行一个项目的初始化
项目地址

1.创建一个项目,同时运行 Hello world

package main

import "fmt"

func main {
    fmt.Println("hello world")
}

为什么要运行一个 hello world?
- 如果连 main 中的 hello 都运行不了还怎么运行一个大项目,可能是环境没配置好

2.准备文件夹

project
    |- configs // 配置文件应用位置
    |- pkg     // 包括接口在内的代码放在该目录中
    |- utils // 工具类可以放在该文件中
    |- inits     // 系统初始化,和进程管理配置
    |- README.md // 项目说明文件
    |- main.go     // 主函数

3.准备好需要的第三方包

  • 数据库连接: github.com/jinzhu/gorm
  • 路由连接: github.com/gin-gonic/gin
  • 缓存连接: github.com/gomodule/redigo/redis
  • 消息队列连接: github.com/apache/rocketmq-client-go/v2
  • 读取配置: github.com/spf13/viper
  • 日志处理: github.com/sirupsen/logrus
  • 错误处理: github.com/pkg/errors
  • 测试断言工具: github.com/stretchr/testify/assert

3.准备好项目配置文件,推荐使用 .toml (或者 .yaml文件)

文件位置

projcet
    |- configs
            |- local.toml文件
[http]
    port=":8080"
    read_timeout=60
    write_timeout=60
[database]
    host="127.0.0.1"
    dbname="postgres"
    user="postgres"
    password="123456"
    sslmode="disable"
[redis]
    host="127.0.0.1:6379"
    password=""
    max_idle=10
    max_active=10
[rocketmq]
    server = [
    "127.0.0.1:9876"
     ]

4.配置 log 日志 (logrus)

文件地址

project 
    |- utils
        |- logger
            |- log.go
package logger

import (
    "github.com/sirupsen/logrus"
    "os"
)

var (
    Log = logrus.New()
)

func init() {
    InitLogger()
}

func InitLogger() {
    // 日志格式化为JSON而不是默认的ASCII
    Log.SetFormatter(&logrus.JSONFormatter{})
    // 输出stdout而不是默认的stderr,也可以是一个文件
    Log.SetOutput(os.Stdout)
    // 只记录严重或以上警告
    Log.SetLevel(logrus.WarnLevel)
}

// New 用来创建新的 log 日志
func New() *logrus.Logger {
    return Log
}

5.配置需要初始化的读取项目配置(viper)数据库(gorm)连接,缓存连接(redigo),http初始化(gin),消息队列连接(rocketmq)

1.读取配置文件 (config.go)

文件地址

project 
    |- inits
        |- config.go
package inits

import (
    "github.com/catbugdemo/project_order/utils/logger"
    "github.com/pkg/errors"
    "github.com/spf13/viper"
    "sync"
)

var (
    config *viper.Viper
    mu     sync.RWMutex
    log    = logger.New()
)

func init() {
    InitConfig()
}

func InitConfig() {
    v := viper.New()
    // 初始化配置信息
    v.SetConfigName("local")
    v.SetConfigType("toml")

    // TODO 项目名称进行配置
    v.AddConfigPath("$GOPATH/src/github.com/catbugdemo/project_order/configs")
    v.AddConfigPath("../configs")

    if err := ReadInConfig(v); err != nil {
        log.Fatalf("读取配置失败:%+v\n", err)
    }
    // 当配置改变是重新运行
    v.WatchConfig()

    config = v
}

// ReadInConfig 通过读写锁保证内容稳定内容
func ReadInConfig(v *viper.Viper) error {
    mu.RLock()
    defer mu.RUnlock()
    if err := v.ReadInConfig(); err != nil {
        return errors.WithStack(err)
    }
    return nil
}

// GetConfig 获取配置
func GetConfig() *viper.Viper {
    mu.RLock()
    defer mu.RUnlock()
    return config
}

2.初始化数据库 (db.go)

文件地址

project 
    |- inits
        |- db.go
package init

import (
    "fmt"
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/postgres"

    "github.com/pkg/errors"
    "time"
)

var (
    db       *gorm.DB
    dbConfig *DbConfig
)

type DbConfig struct {
    Host     string `toml:"host"`
    DbName   string `toml:"dbname"`
    User     string `toml:"user"`
    Password string `toml:"password"`
    Sslmode  string `toml:"disable"`
}

func init() {
    InitDbConfig()
    InitDB()
}

func InitDbConfig() {
    c := GetConfig()

    dbConfig = &DbConfig{
        Host:     c.GetString("database.host"),
        DbName:   c.GetString("database.dbname"),
        User:     c.GetString("database.user"),
        Password: c.GetString("database.password"),
        Sslmode:  c.GetString("database.sslmode"),
    }
}

func InitDB() {

    params := fmt.Sprintf("host=%s user=%s dbname=%s sslmode=%s password=%s",
        dbConfig.Host,
        dbConfig.User,
        dbConfig.DbName,
        dbConfig.Sslmode,
        dbConfig.Password,
    )

    open, err := gorm.Open("postgres", params)
    if err != nil {
        log.Fatalf("连接数据库失败:%+v", errors.WithStack(err))
    }
    if err = open.DB().Ping(); err != nil {
        log.Fatalf("ping 数据库失败:%+v", errors.WithStack(err))
    }

    // 设置表不为复数
    open.SingularTable(true)
    // 设置打印日志
    open.LogMode(true)
    // 设置可重复使用连接的最长时间
    open.DB().SetConnMaxLifetime(10 * time.Second)

    db = open
}

// 获取数据库
func DB() *gorm.DB {
    return db
}

3.初始化缓存 (redis.go)

文件地址

project 
    |- inits
        |- redis.go
package init

import (
    "github.com/gomodule/redigo/redis"
    "github.com/pkg/errors"
    "time"
)

var (
    pool        *redis.Pool
    redisConfig *RedisConfig
)

type RedisConfig struct {
    Host      string `toml:"host"`
    Password  string `toml:"passowrd"`
    MaxIdle   int    `toml:"max_idle"`
    MaxActive int    `toml:"max_active"`
    Db        string `toml:"db"`
}

func init() {
    InitRedisConfig()
    InitRedisPool()
}

// InitRedisConfig 初始化缓存配置
func InitRedisConfig() {
    c := GetConfig()

    redisConfig = &RedisConfig{
        Host:      c.GetString("redis.host"),
        Password:  c.GetString("redis.password"),
        MaxIdle:   c.GetInt("redis.max_idle"),
        MaxActive: c.GetInt("redis.max_active"),
    }
}

// InitRedisPool 初始化缓存池
func InitRedisPool() {
    pool = &redis.Pool{
        //最大闲置连接
        MaxIdle: redisConfig.MaxIdle,
        //最大活动数
        MaxActive: redisConfig.MaxActive,
        //数据库连接
        Dial: func() (redis.Conn, error) {
            c, err := redis.Dial("tcp", redisConfig.Host)
            if err != nil {
                c.Close()
                log.Printf("fail to dial redis: %+v\n", errors.WithStack(err))
                return nil, err
            }
            //密码认证
            if redisConfig.Password != "" {
                if _, err = c.Do("AUTH", redisConfig.Password); err != nil {
                    c.Close()
                    log.Printf("fail to auth redis: %+v\n", errors.WithStack(err))
                    return nil, err
                }
            }
            //redis 缓存数据库认证
            if redisConfig.Db != "" {
                if _, err = c.Do("SELECT", redisConfig.Db); err != nil {
                    c.Close()
                    log.Printf("fail to SELECT DB redis: %+v\n", errors.WithStack(err))
                    return nil, err
                }
            }
            return c, err
        },
        //测试连接是否正常
        TestOnBorrow: func(c redis.Conn, t time.Time) error {
            _, err := c.Do("PING")
            if err != nil {
                c.Close()
                log.Printf("fail to ping redis: %+v\n", err)
                return err
            }
            return nil
        },
    }
}

// Get 获取缓存连接
func Get() redis.Conn {
    return pool.Get()
}

4.接口连接 (http.go)

文件地址

project 
    |- inits
        |- http.go
``
package inits

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

var (
    ginConfig *GinConfig
)

type GinConfig struct {
    Port         string `toml:"port"`
    ReadTimeout  int    `toml:"read_timeout"`
    WriteTimeout int    `toml:"write_timeout"`
}

func InitHttp() {
    InitGinConfig()
    InitGin()
}

func InitGinConfig() {
    c := GetConfig()
    ginConfig = &GinConfig{
        Port:         c.GetString("http.port"),
        ReadTimeout:  c.GetInt("http.read_timeout"),
        WriteTimeout: c.GetInt("http.write_timeout"),
    }
}

func InitGin() {
    router := gin.New()

    router.Use(gin.Logger(), gin.Recovery())

    router.GET("/version/", func(c *gin.Context) {
        c.JSON(200, gin.H{"version": "v1.0.0"})
    })

    s := &http.Server{
        Addr:         ginConfig.Port,
        Handler:      router,
        ReadTimeout:  time.Duration(ginConfig.ReadTimeout) * time.Second,
        WriteTimeout: time.Duration(ginConfig.WriteTimeout) * time.Second,
    }

    if err := s.ListenAndServe(); err != nil {
        if err == http.ErrServerClosed {
            log.Println("http: Server Close:%+v", errors.WithStack(err))
        }
        log.Fatalf("http开启监听服务失败:%+v", errors.WithStack(err))
    }

}

5. 消息队列连接 (rocketmq.go)

文件地址

project 
    |- inits
        |- rocketmq.go

6.简单编写 main.go

文件地址

project 
    |- main.go
package main

import (
    "fmt"
    "github.com/catbugdemo/project_order/inits"
)

func main() {

    go inits.InitHttp()

    select {}
}

8.单元测试每个方法是否正确

9.开启项目

  1. 运行 main.go
  2. 测试 version 端口 localhost:8080/version

结语

  • 推荐第一次自己尝试跟着写,第二次自己写一个新的作为自己的 project_order,第三次直接使用
  • 感谢阅读
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!