教程地址:https://www.bilibili.com/video/BV1FV4y1C72M?spm_id_from=333.788.videopod.sections&vd_source=707ec8983cc32e6e065d5496a7f79ee6
01-项目搭建
- 各常用目录的说明:
https://github.com/golang-standards/project-layout/blob/master/README_zh.md
02-优雅启停
- package main
- import (
- "context"
- "github.com/gin-gonic/gin"
- "log"
- "net/http"
- "os"
- "os/signal"
- "syscall"
- "time"
- )
- func main() {
- r := gin.Default()
- srv := &http.Server{
- Addr: ":5000",
- Handler: r,
- }
- // 通过协程启动服务
- go func() {
- log.Printf("server listen at %s", srv.Addr)
- if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
- log.Fatalf("listen: %s\n", err)
- }
- }()
- // 制作按Ctrl+C退出功能,此处阻塞
- quit := make(chan os.Signal)
- signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
- <-quit
- // 系统停止开始
- log.Println("Shutdown Server ...")
-
- // 等待2秒
- ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
- defer cancel()
- // 停止服务
- if err := srv.Shutdown(ctx); err != nil {
- log.Fatal("Server Shutdown:", err)
- }
- // 2秒后打印
- select {
- case <-ctx.Done():
- log.Println("timeout of 2 seconds.")
- }
- log.Println("Server exiting")
- }
复制代码 03-路由
- 路由通过InitRouter初始化;路由与api操纵分离开来;api的路由设置与路由执行函数分开
- router/router.go :路由启动,并挂载user模块的路由
- package router
- import (
- "Gin_gPRC/api/user"
- "github.com/gin-gonic/gin"
- )
- // Router 接口,规定里面有一个Route函数
- type Router interface {
- Route(r *gin.Engine)
- }
- // 定义一个RegisterRouter 注册类,这个类有一个Route方法
- // Route方法接收一个符合Router接口规范的对象
- // Route方法里运行了接收对象里的Route方法,用以绑定路由
- type RegisterRouter struct{}
- func New() *RegisterRouter {
- return &RegisterRouter{}
- }
- func (*RegisterRouter) Route(router Router, r *gin.Engine) {
- router.Route(r)
- }
- // 初始化路由
- func InitRouter(r *gin.Engine) {
- router := New()
- // 把user.RouterUser对象给到注册类,委托运行了user里的Route方法,传递r参数
- router.Route(&user.RouterUser{}, r)
- }
复制代码
- api/user/route.go;注册了/login/getCaptcha的POST接口,执行函数写再user.go中
- package user
- import "github.com/gin-gonic/gin"
- type RouterUser struct{}
- func (*RouterUser) Route(r *gin.Engine) {
- handler := &HandlerUser{}
- r.POST("/login/getCaptcha", handler.getCaptcha)
- }
复制代码
- package user
- import "github.com/gin-gonic/gin"
- type HandlerUser struct{}
- func (*HandlerUser) getCaptcha(ctx *gin.Context) {
- ctx.JSON(200, "getCaptcha test")
- }
复制代码 04-发送验证码
- package model
- type BusinessCode int
- type Result struct {
- Code BusinessCode `json:"code"`
- Msg string `json:"msg"`
- Data any `json:"data"`
- }
- func (r *Result) Success(data any) *Result {
- r.Code = 200
- r.Msg = "success"
- r.Data = data
- return r
- }
- func (r *Result) Fail(code BusinessCode, msg string) *Result {
- r.Code = code
- r.Msg = msg
- return r
- }
复制代码
- package lib
- import "regexp"
- func CheckMobile(mobile string) bool {
- if mobile == "" {
- return false
- }
- regular := "^1[3-9]\\d{9}$"
- reg := regexp.MustCompile(regular)
- return reg.MatchString(mobile)
- }
复制代码
- package model
- const (
- NoLegalMobile BusinessCode = 2001
- )
复制代码
- 修改getCaptcha,返回123456为验证码
- package user
- import (
- "Gin_gPRC/lib"
- "Gin_gPRC/model"
- "github.com/gin-gonic/gin"
- "log"
- "time"
- )
- type HandlerUser struct{}
- func (*HandlerUser) getCaptcha(ctx *gin.Context) {
- //ctx.JSON(200, "getCaptcha test")
- rsp := &model.Result{}
- //1. 获取参数
- mobile := ctx.PostForm("mobile")
- //2. 校验参数
- if !lib.CheckMobile(mobile) {
- ctx.JSON(200, rsp.Fail(model.NoLegalMobile, "手机号码错误"))
- return
- }
- //3. 生成验证码
- code := "123456"
- //4. 调用短信平台接口
- go func() {
- time.Sleep(2 * time.Second)
- log.Println("短信平台调用成功")
- }()
- ctx.JSON(200, rsp.Success(code))
- }
复制代码 05-redis操纵
- redis.go,安装:go get github.com/go-redis/redis/v8
- package dao
- import (
- "context"
- "github.com/go-redis/redis/v8"
- "time"
- )
- var Rc *RedisCache
- type RedisCache struct {
- rdb *redis.Client
- }
- func init() {
- rdb := redis.NewClient(&redis.Options{
- Addr: "localhost:6379",
- Password: "",
- DB: 0,
- })
- Rc = &RedisCache{rdb: rdb}
- }
- func (rc *RedisCache) Put(ctx context.Context, key string, value string, expire time.Duration) error {
- err := rc.rdb.Set(ctx, key, value, expire).Err()
- return err
- }
- func (rc *RedisCache) Get(ctx context.Context, key string) (string, error) {
- result, err := rc.rdb.Get(ctx, key).Result()
- return result, err
- }
复制代码
- repo/cache.go,定义Cache的接口,再由dao里的redis.go去实现
- package repo
- import (
- "context"
- "time"
- )
- type Cache interface {
- Put(ctx context.Context, key string, value string, expire time.Duration) error
- Get(ctx context.Context, key string) (string, error)
- }
复制代码
- package user
- import (
- "Gin_gPRC/dao"
- "Gin_gPRC/lib"
- "Gin_gPRC/model"
- "Gin_gPRC/repo"
- "context"
- "github.com/gin-gonic/gin"
- "log"
- "time"
- )
- type HandlerUser struct {
- cache repo.Cache
- }
- func New() *HandlerUser {
- return &HandlerUser{
- cache: dao.Rc,
- }
- }
- func (h *HandlerUser) getCaptcha(ctx *gin.Context) {
- //ctx.JSON(200, "getCaptcha test")
- rsp := &model.Result{}
- //1. 获取参数
- mobile := ctx.PostForm("mobile")
- //2. 校验参数
- if !lib.CheckMobile(mobile) {
- ctx.JSON(200, rsp.Fail(model.NoLegalMobile, "手机号码错误"))
- return
- }
- //3. 生成验证码
- code := "123456"
- //4. 调用短信平台接口
- go func() {
- time.Sleep(2 * time.Second)
- log.Println("短信平台调用成功")
- // 制作一个超时的上下文
- c, cancel := context.WithTimeout(context.Background(), 2*time.Second)
- defer cancel()
- // redis加入
- err := h.cache.Put(c, "REGISTER"+mobile, code, 15*time.Minute)
- if err != nil {
- log.Printf("验证码存入redis出错,%v \n", err)
- }
- }()
- ctx.JSON(200, rsp.Success(code))
- }
复制代码 06-日记
安装:go get -u go.uber.org/zap
安装:go get -u github.com/natefinch/lumberjack

- package lib
- import (
- "github.com/gin-gonic/gin"
- "github.com/natefinch/lumberjack"
- "go.uber.org/zap"
- "go.uber.org/zap/zapcore"
- "net"
- "net/http"
- "net/http/httputil"
- "os"
- "runtime/debug"
- "strings"
- "time"
- )
- var lg *zap.Logger
- type LogConfig struct {
- DebugFileName string `json:"debugFileName"`
- InfoFileName string `json:"infoFileName"`
- WarnFileName string `json:"warnFileName"`
- MaxSize int `json:"maxSize"`
- MaxAge int `json:"maxAge"`
- MaxBackups int `json:"maxBackups"`
- }
- func InitLogger(cfg *LogConfig) (err error) {
- writeSyncerDebug := getLogWriter(cfg.DebugFileName, cfg.MaxSize, cfg.MaxBackups, cfg.MaxAge)
- writeSyncerInfo := getLogWriter(cfg.InfoFileName, cfg.MaxSize, cfg.MaxBackups, cfg.MaxAge)
- writeSyncerWarn := getLogWriter(cfg.WarnFileName, cfg.MaxSize, cfg.MaxBackups, cfg.MaxAge)
- encoder := getEncoder()
- debugCore := zapcore.NewCore(encoder, writeSyncerDebug, zapcore.DebugLevel)
- infoCore := zapcore.NewCore(encoder, writeSyncerInfo, zapcore.InfoLevel)
- warnCore := zapcore.NewCore(encoder, writeSyncerWarn, zapcore.WarnLevel)
- consoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())
- std := zapcore.NewCore(consoleEncoder, zapcore.Lock(os.Stdout), zapcore.DebugLevel)
- core := zapcore.NewTee(debugCore, infoCore, warnCore, std)
- lg = zap.New(core, zap.AddCaller())
- zap.ReplaceGlobals(lg)
- return
- }
- func getEncoder() zapcore.Encoder {
- encoderConfig := zap.NewProductionEncoderConfig()
- encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
- encoderConfig.TimeKey = "time"
- encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
- encoderConfig.EncodeDuration = zapcore.SecondsDurationEncoder
- encoderConfig.EncodeCaller = zapcore.ShortCallerEncoder
- return zapcore.NewJSONEncoder(encoderConfig)
- }
- func getLogWriter(filename string, maxSize, maxBackup, maxAge int) zapcore.WriteSyncer {
- lumberJackLogger := &lumberjack.Logger{
- Filename: filename,
- MaxSize: maxSize,
- MaxBackups: maxBackup,
- MaxAge: maxAge,
- }
- return zapcore.AddSync(lumberJackLogger)
- }
- func GinLogger() gin.HandlerFunc {
- return func(c *gin.Context) {
- start := time.Now()
- path := c.Request.URL.Path
- query := c.Request.URL.RawQuery
- c.Next()
- cost := time.Since(start)
- lg.Info(path,
- zap.Int("status", c.Writer.Status()),
- zap.String("method", c.Request.Method),
- zap.String("path", path),
- zap.String("query", query),
- zap.String("ip", c.ClientIP()),
- zap.String("user-agent", c.Request.UserAgent()),
- zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()),
- zap.Duration("cost", cost),
- )
- }
- }
- func GinRecovery(stack bool) gin.HandlerFunc {
- return func(c *gin.Context) {
- defer func() {
- if err := recover(); err != nil {
- var brokenPipe bool
- if ne, ok := err.(*net.OpError); ok {
- if se, ok := ne.Err.(*os.SyscallError); ok {
- if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {
- brokenPipe = true
- }
- }
- }
- httpRequest, _ := httputil.DumpRequest(c.Request, false)
- if brokenPipe {
- lg.Error(c.Request.URL.Path,
- zap.Any("error", err),
- zap.String("request", string(httpRequest)))
- c.Error(err.(error))
- c.Abort()
- return
- }
- if stack {
- lg.Error("[Recovery from panic]",
- zap.Any("error", err),
- zap.String("request", string(httpRequest)),
- zap.String("stack", string(debug.Stack())))
- } else {
- lg.Error("[Recovery from panic]",
- zap.Any("error", err),
- zap.String("request", string(httpRequest)))
- }
- c.AbortWithStatus(http.StatusInternalServerError)
- }
- }()
- c.Next()
- }
- }
复制代码
- package main
- import (
- "Gin_gPRC/lib"
- "Gin_gPRC/router"
- "context"
- "github.com/gin-gonic/gin"
- "log"
- "net/http"
- "os"
- "os/signal"
- "syscall"
- "time"
- )
- func main() {
- r := gin.Default()
- //log
- lc := &lib.LogConfig{
- DebugFileName: "./logs/debug.log",
- InfoFileName: "./logs/info.log",
- WarnFileName: "./logs/warn.log",
- MaxSize: 500,
- MaxAge: 28,
- MaxBackups: 3,
- }
- err := lib.InitLogger(lc)
- if err != nil {
- log.Fatal(err)
- }
- router.InitRouter(r)
- srv := &http.Server{
- Addr: ":5000",
- Handler: r,
- }
- go func() {
- log.Printf("server listen at %s", srv.Addr)
- if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
- log.Fatalf("listen: %s\n", err)
- }
- }()
- quit := make(chan os.Signal)
- signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
- <-quit
- log.Println("Shutdown Server ...")
- ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
- defer cancel()
- if err := srv.Shutdown(ctx); err != nil {
- log.Fatal("Server Shutdown:", err)
- }
- select {
- case <-ctx.Done():
- log.Println("timeout of 2 seconds.")
- }
- log.Println("Server exiting")
- }
复制代码
- package user
- import (
- "Gin_gPRC/dao"
- "Gin_gPRC/lib"
- "Gin_gPRC/model"
- "Gin_gPRC/repo"
- "context"
- "github.com/gin-gonic/gin"
- "go.uber.org/zap"
- "time"
- )
- type HandlerUser struct {
- cache repo.Cache
- }
- func New() *HandlerUser {
- return &HandlerUser{
- cache: dao.Rc,
- }
- }
- func (h *HandlerUser) getCaptcha(ctx *gin.Context) {
- //ctx.JSON(200, "getCaptcha test")
- rsp := &model.Result{}
- //1. 获取参数
- mobile := ctx.PostForm("mobile")
- //2. 校验参数
- if !lib.CheckMobile(mobile) {
- ctx.JSON(200, rsp.Fail(model.NoLegalMobile, "手机号码错误"))
- return
- }
- //3. 生成验证码
- code := "123456"
- //4. 调用短信平台接口
- go func() {
- time.Sleep(2 * time.Second)
- zap.L().Info("短信平台调用成功")
- // 制作一个超时的上下文
- c, cancel := context.WithTimeout(context.Background(), 2*time.Second)
- defer cancel()
- // redis加入
- err := h.cache.Put(c, "REGISTER"+mobile, code, 15*time.Minute)
- if err != nil {
- zap.L().Error("验证码存入redis出错" + err.Error())
- }
- }()
- ctx.JSON(200, rsp.Success(code))
- }
复制代码 07-配置
安装:go get github.com/spf13/viper
- server:
- name: "Gin_gRPC"
- addr: "127.0.0.1:5000"
- zap:
- debugFileName: "./logs/debug.log"
- infoFileName: "./logs/info.log"
- warnFileName: "./logs/warn.log"
- maxSize: 500,
- maxAge: 28,
- maxBackups: 3
- redis:
- host: "localhost"
- port: 6379
- password: ""
- db: 0
复制代码
- package config
- import (
- "Gin_gPRC/lib"
- "github.com/go-redis/redis/v8"
- "github.com/spf13/viper"
- "log"
- )
- var Conf = InitConfig()
- type Config struct {
- viper *viper.Viper
- SC *ServerConfig
- }
- type ServerConfig struct {
- Name string
- Addr string
- }
- func InitConfig() *Config {
- conf := &Config{viper: viper.New()}
- //workDir, _ := os.Getwd()
- // 确定配置文件的名称、类型与位置
- conf.viper.SetConfigName("config")
- conf.viper.SetConfigType("yaml")
- conf.viper.AddConfigPath("./")
- err := conf.viper.ReadInConfig()
- if err != nil {
- log.Fatalf("Fatal error config file: %s \n", err)
- }
- // 读取Server的配置
- conf.ReadServerConfig()
- // 初始化zapLog
- conf.InitZapLog()
- return conf
- }
- func (c *Config) InitZapLog() {
- lc := &lib.LogConfig{
- DebugFileName: c.viper.GetString("zap.debugFileName"),
- InfoFileName: c.viper.GetString("zap.infoFileName"),
- WarnFileName: c.viper.GetString("zap.warnFileName"),
- MaxSize: c.viper.GetInt("zap.maxSize"),
- MaxAge: c.viper.GetInt("zap.maxAge"),
- MaxBackups: c.viper.GetInt("zap.maxBackups"),
- }
- err := lib.InitLogger(lc)
- if err != nil {
- log.Fatal(err)
- }
- }
- func (c *Config) ReadServerConfig() {
- sc := &ServerConfig{}
- sc.Name = c.viper.GetString("server.name")
- sc.Addr = c.viper.GetString("server.addr")
- c.SC = sc
- }
- func (c *Config) ReadRedisConfig() *redis.Options {
- return &redis.Options{
- Addr: c.viper.GetString("redis.host") + ":" + c.viper.GetString("redis.port"),
- Password: c.viper.GetString("redis.password"),
- DB: c.viper.GetInt("redis.db"),
- }
- }
复制代码
- func main() {
- r := gin.Default()
-
- config.InitConfig()
- router.InitRouter(r)
- srv := &http.Server{
- Addr: config.Conf.SC.Addr,
- Handler: r,
- }
- ...
- }
复制代码
- func init() {
- rdb := redis.NewClient(config.Conf.ReadRedisConfig())
- Rc = &RedisCache{rdb: rdb}
- }
复制代码 结束:
对于微服务,思量实验下利用Go-micro + Gin的方式,后续继续记录
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |