Quickly start building a simple project

Time:2022-6-2

preface

All projects start from one file and one main. You can initialize a project according to the following steps
Project address

1. create a project and run Hello world at the same time

package main

import "fmt"

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

Why run a hello world?
-If you can’t even run the hello in main, how can you run a large project? Maybe the environment is not configured properly

2. prepare folder

project
    |-Configs // application location of configuration file
    |-PKG // codes including interfaces are placed in this directory
    |-Utils // tool classes can be placed in this file
    |-Inits // system initialization and process management configuration
    |- README. MD // project description file
    |- main. Go // main function

3. prepare the required third-party package

  • Database connection: github com/jinzhu/gorm
  • Routing connection: github com/gin-gonic/gin
  • Cache connection: github com/gomodule/redigo/redis
  • Message queue connection: github com/apache/rocketmq-client-go/v2
  • Read configuration: github com/spf13/viper
  • Log processing: github com/sirupsen/logrus
  • Error handling: github com/pkg/errors
  • Test assertion tool: github com/stretchr/testify/assert

3. prepare the project configuration file and recommend it Toml (or.Yaml file)

file location

projcet
    |- configs
            |- local. Toml file
[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. configure log log (logrus)

File address

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

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

var (
    Log = logrus.New()
)

func init() {
    InitLogger()
}

func InitLogger() {
    //Log format is JSON instead of default ASCII
    Log.SetFormatter(&logrus.JSONFormatter{})
    //Output stdout instead of the default stderr, or it can be a file
    Log.SetOutput(os.Stdout)
    //Only serious or above warnings are logged
    Log.SetLevel(logrus.WarnLevel)
}

//New is used to create a new log
func New() *logrus.Logger {
    return Log
}

5. configure the read item configuration (Viper) database (Gorm) connection, cache connection (redigo), HTTP initialization (GIN), message queue connection (rocketmq) to be initialized

1. read the configuration file (config.go)

File address

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()
    //Initialize configuration information
    v.SetConfigName("local")
    v.SetConfigType("toml")

    //Todo project name for configuration
    v.AddConfigPath("$GOPATH/src/github.com/catbugdemo/project_order/configs")
    v.AddConfigPath("../configs")

    if err := ReadInConfig(v); err != nil {
        log. Fatalf ("read configuration failed:%+v\n", ERR)
    }
    //Rerun when configuration changes
    v.WatchConfig()

    config = v
}

//Readinconfig ensures content stability through read / write locks
func ReadInConfig(v *viper.Viper) error {
    mu.RLock()
    defer mu.RUnlock()
    if err := v.ReadInConfig(); err != nil {
        return errors.WithStack(err)
    }
    return nil
}

//Getconfig get configuration
func GetConfig() *viper.Viper {
    mu.RLock()
    defer mu.RUnlock()
    return config
}

2. initialize the database (db.go)

File address

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 ("failed to connect to database:%+v", errors.withstack (ERR))
    }
    if err = open.DB().Ping(); err != nil {
        log. Fatalf ("Ping database failed:%+v", errors.withstack (ERR))
    }

    //Setting table is not plural
    open.SingularTable(true)
    //Set print log
    open.LogMode(true)
    //Set the maximum time for a reusable connection
    open.DB().SetConnMaxLifetime(10 * time.Second)

    db = open
}

//Get database
func DB() *gorm.DB {
    return db
}

3. initialize cache (redis.go)

File address

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 initialize cache configuration
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 initialize cache pool
func InitRedisPool() {
    pool = &redis.Pool{
        //Maximum idle connections
        MaxIdle: redisConfig.MaxIdle,
        //Maximum number of activities
        MaxActive: redisConfig.MaxActive,
        //Database connection
        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
            }
            //Password authentication
            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 cache database authentication
            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
        },
        //Test whether the connection is normal
        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 get cache connection
func Get() redis.Conn {
    return pool.Get()
}

4. interface connection (http.go)

File address

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 failed to start listening service:%+v", errors.withstack (ERR))
    }

}

5. message queue connection (rocketmq.go)

File address

project 
    |- inits
        |- rocketmq.go

6. simply write main go

File address

project 
    |- main.go
package main

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

func main() {

    go inits.InitHttp()

    select {}
}

8. whether each method of unit test is correct

9. open project

  1. Run main go
  2. Test version port localhost:8080/version

epilogue

  • It is recommended that you try to write along for the first time and write a new project for yourself for the second time_ Order, the third direct use
  • Thanks for reading

This work adoptsCC agreement, reprint must indicate the author and the link to this article