go实践之apiserver搭建
阅读原文时间:2023年07月10日阅读:1

文章目录

本文主要记录下博主用gin搭建app server的过程,方便后续学习。web框架用的gin,日志用的zap,数据库连接用的mysql driver,配置文件读取用的是viper。整个项目的框架如下:

.
├── app
│   ├── comment.go
│   ├── server.go
│   └── user.go
├── config.toml
├── Gopkg.lock
├── Gopkg.toml
├── LICENSE
├── main.go
├── README.md
├── tool
│   ├── config
│   │   ├── config.go
│   │   └── config_test.go
│   ├── db
│   │   ├── mysql.go
│   │   └── mysql_test.go
│   └── log
│       ├── log.go
│       └── log_test.go
└── vendor
    ├── github.com
    ......

main.go作为整个api server 的入口文件,tool文件夹下面存放了日志,数据库,配置文件相关代码。app文件夹里面是api实现相关的代码。

1、配置文件读取

配置文件读取的代码如下:

package config

import(
    "fmt"
    "github.com/spf13/viper"
)

func ParseConfig() error {
    viper.SetConfigName("config") // name of config file (without extension)
    viper.AddConfigPath("./conf") // call multiple times to add many search paths
    viper.AddConfigPath(".")    // optionally look for config in the working directory
    err := viper.ReadInConfig() // Find and read the config file
    if err != nil {             // Handle errors reading the config file
        return  fmt.Errorf("Fatal error config file: %s \n", err)
    }
    viper.WatchConfig()
    return nil
}

这里只是简单的一个demo,viper还有更多的功能,比如设置默认值 viper.SetDefault("ContentDir", "content"),可以加载环境变量中的值AutomaticEnv(),热更新配置文件 viper.WatchConfig()。具体可以参考官方文档:链接

2、数据连接

本文件主要实现的是数据库连接,以及增删查改操作。具体代码如下:

package db

import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
    "time"
    "github.com/spf13/viper"
    "fmt"
)

type DbConn struct {
    Db *sql.DB
}

func InitDbConn()(*DbConn, error) {
    dbConn := viper.GetString("Database.DbConn")
    db, err := sql.Open("mysql", dbConn)
    if err != nil {
        return nil, fmt.Errorf("get db connection error")
    }
    maxOpenConns := viper.GetInt("Database.MaxOpenConn") //从配置文件中获取配置值
    maxIdleConns := viper.GetInt("Database.MaxIdleConn")
    maxLifetime := viper.GetInt("Database.MaxLifetime")

    ......//省略部分代码

    err = db.Ping()
    if err != nil {
        return nil,fmt.Errorf("connect to db error")
    }else{
        dbconn := &DbConn{Db:db}
        return dbconn,nil
    }
}

func (c *DbConn) Insert(sql string, args ...interface{}) (lastInsertId int64, err error) {
    res, err := c.Db.Exec(sql, args...)
    if err != nil {
        return
    }
    return res.LastInsertId()
}
......//省略部分代码

主要是调用mysql driver根据配置文件配置值连接数据库。

3、日志初始化

日志主要的是uber的zap日志框架,这是一个高性能的日志框架,在这里有点小用它了。也只是一个简单的使用举例。更多功能需要去学习。

package log

import (
    "fmt"
    "strings"

    "github.com/spf13/viper"
    "go.uber.org/zap"
    "go.uber.org/zap/zapcore"
    "gopkg.in/natefinch/lumberjack.v2"
)

var Logger *zap.SugaredLogger //global logger

func InitLog() {
    logLevel := viper.GetString("Log.LogLevel")
    logName := viper.GetString("Log.LogName")
    logAge := viper.GetInt("Log.LogAge")
    logSize := viper.GetInt("Log.LogSize")

    w := zapcore.AddSync(&lumberjack.Logger{
        Filename:   logName,
        MaxSize:    logSize, // megabytes
        MaxBackups: 3,
        LocalTime:  true,
        MaxAge:     logAge, // days
    })
    zapLogLevel := zap.NewAtomicLevel()
    if err := zapLogLevel.UnmarshalText([]byte(strings.ToLower(logLevel))); err != nil {
        panic(fmt.Errorf("get config log level:%v config error: %v", logLevel, err))
    }

    .....//此处省略部分代码

    logger := zap.New(core, zap.AddCaller())
    Logger = logger.Sugar()
    Logger.Info("logger init successful!")
}

4、server初始化

主要就是调用配置读取函数,数据库连接和日志初始化函数,再启动http server

package main

import (
   "fmt"
   "os"
   "os/signal"
   "syscall"

   "github.com/zhanben/go_site/app"
   "github.com/zhanben/go_site/tool/config"
   "github.com/zhanben/go_site/tool/db"
   "github.com/zhanben/go_site/tool/log"

   "go.uber.org/zap"
)

func main() {
   //Read config file
   err := config.ParseConfig()
   if err != nil {
      panic(fmt.Errorf("Failed to read config file: %s \n", err))
   }

   //Init log
   log.InitLog()

   //Init db connection
   db, err := db.InitDbConn()
   if err != nil {
      panic("connect db error!")
   }
   log.Logger.Info("Db init successful!")

   //start http sever
   startServer(db)
}

.....//此处省略部分代码

5、接口编写

下面是简单的两个接口实现的例子:一个是URL中带有参数,一个没有带的。

package app

import (
    "github.com/gin-gonic/gin"
    "go.uber.org/zap"
    "net/http"
    "github.com/zhanben/go_site/tool/db"
)

.....//此处省略部分代码

func (u *User) initRouter(r *gin.RouterGroup) {
    //在此添加接口
    r.GET("/users", u.getAllUsers)      //获取所有用户信息
    r.GET("/users/:name", u.getOneUser) //根据用户名获取用户详细信息
}

func (u *User) getAllUsers(c *gin.Context) {
    //构建返回结构体
    res := map[string]interface{}{
        "Action":  "GetAllUserResponse",
        "RetCode": 0,
    }

    sql := "select * from user limit 10"
    result, err := u.db.Select(sql)
    if err != nil {
        u.Logger.Error("get user info from db error!")
        abortWithError(u.Logger, c, err)
    }
    res["UserInfo"] = result
    c.JSON(http.StatusOK, res)
}

func (u *User) getOneUser(c *gin.Context) {
    //构建返回结构体
    res := map[string]interface{}{
        "Action":  "GetOneUserResponse",
        "RetCode": 0,
    }
    userName, ok :=c.Params.Get("name")
    if !ok {
        u.Logger.Error("parameter name must be fixed!")
    }
    u.Logger.Infof("get user name from url:%s",userName)

    sql := "select * from user where username=?"
    result, err := u.db.Select(sql,userName)
    if err != nil {
        u.Logger.Error("get user info from db error!")
        res["RetCode"]= "-1"
        res["Error"] = "user not exist!"
    }else{
        res["UserInfo"] = result
        u.Logger.Info("get one user info successful!")
    }
    c.JSON(http.StatusOK, res)
}

至此一个简单的go api sever的框架就搭建好了。当然后续还有单元测试的代码需要补充。本文的全部的代码可以到github下载:git clone --branch v0.1 git@github.com:Zhanben/go_site.git

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章