快速开始构建一个简单项目
前言
所有项目都是从一个文件,一个 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.开启项目
- 运行 main.go
- 测试 version 端口 localhost:8080/version
结语
- 推荐第一次自己尝试跟着写,第二次自己写一个新的作为自己的 project_order,第三次直接使用
- 感谢阅读
本作品采用《CC 协议》,转载必须注明作者和本文链接