169
读者
47
内容

从零开始教你如何基于 Gin 框架封装自己的 Web 框架,涵盖框架设计与实现。无论你经验水平如何,都可获得实际项目开发技巧,提高开发效率。 相比于我的开源项目 go-gin-api(GitHub 5.5K Star),此框架去掉了一些集成的功能和界面,使得整个框架更加简洁、轻量。此外也对代码进行了升级以确保性能和稳定性。开发者就可以更灵活地选择所需的功能,并获得更好的性能和稳定性。 原价 199 元,限时特惠,只需 59.9 元。 买过后,从置顶帖【目录合集】加微信,发源码 + 进交流群。

全部文章
作者新亮头像

新亮

2024/09/17

6.9 mongo-driver 完整用法与示例代码

1. 安装 mongo-driver

可以使用以下命令进行安装:

go get go.mongodb.org/mongo-driver/mongo

2. 基本连接与配置

Mo......

作者新亮头像

新亮

2024/09/13

6.8 go-redis/redis 完整用法与示例代码

1. 安装 go-redis

可以使用以下命令安装:

go get github.com/go-redis/redis/v8

2. 基本连接

你可以通过 redis.NewC......

作者新亮头像

新亮

2024/09/12

6.7 go-resty/resty 完整用法与示例代码

1. 安装

可以通过以下命令安装:

go get github.com/go-resty/resty/v2

2. 创建一个简单的 HTTP 客户端

通过 resty.New(......

作者新亮头像

新亮

2024/02/20
作者新亮头像

新亮

2024/01/12

3.6 包装 gin Context

Context 是一个上下文对象,它提供了许多有用的方法和属性,用于处理 HTTP 请求和响应。

代码片段:

// ./internal/pkg/core/con......
作者新亮头像

新亮

2024/01/12

3.5 包装 gin IRoutes

IRoutes 接口是定义路由组的接口。

IRoutes 接口包含了定义路由的方法,可以用于添加路由和中间件等。

代码片段:

// ./......
作者新亮头像

新亮

2024/01/12

3.4 实现链路日志记录

目前可收集日志类型包括:

  1. 当前的请求日志

  2. 当前的响应日志

  3. 自定义调试日志

  4. MySQL 操作日志

  5. Redis 操作信息

  6. Mongo 操作信息

  7. 请求三方 API 接口的请求与响应日志

日志收集,代码片段:

// ./internal/pkg/core/core.go

// region 记录日志
var t *trace.Trace
if x := context.Trace(); x != nil {
	t = x.(*trace.Trace)
} else {
	return
}

decodedURL, _ := url.QueryUnescape(ctx.Request.URL.RequestURI())

// ctx.Request.Header,精简 Header 参数
traceHeader := map[string]string{
	"Content-Type": ctx.GetHeader("Content-Type"),
}

t.WithRequest(&trace.Request{
	TTL:        "un-limit",
	Method:     ctx.Request.Method,
	DecodedURL: decodedURL,
	Header:     traceHeader,
	Body:       string(context.RawData()),
})

var responseBody interface{}

if response != nil {
	responseBody = response
}

t.WithResponse(&trace.Response{
	Header:          ctx.Writer.Header(),
	HttpCode:        ctx.Writer.Status(),
	HttpCodeMsg:     http.StatusText(ctx.Writer.Status()),
	BusinessCode:    businessCode,
	BusinessCodeMsg: businessCodeMsg,
	Body:            responseBody,
	CostSeconds:     time.Since(ts).Seconds(),
})

t.Success = !ctx.IsAborted() && (ctx.Writer.Status() == http.StatusOK)
t.CostSeconds = time.Since(ts).Seconds()

logger.Info("trace-log",
	zap.Any("method", ctx.Request.Method),
	zap.Any("path", decodedURL),
	zap.Any("http_code", ctx.Writer.Status()),
	zap.Any("business_code", businessCode),
	zap.Any("success", t.Success),
	zap.Any("cost_seconds", t.CostSeconds),
	zap.Any("trace_id", t.Identifier),
	zap.Any("trace_info", t),
	zap.Error(abortErr),
)
// endregion

使用日志组件,代码片段:

// main.go

import "gin-api-mono/internal/pkg/logger"

// 将日志输出到文件
accessLogger, err := logger.NewJSONLogger(
	logger.WithField("domain", fmt.Sprintf("%s[%s]", configs.ProjectName, env.Active().Value())),
	logger.WithTimeLayout(timeutil.CSTLayout),
	logger.WithFileP(configs.ProjectAccessLogFile),
)

// 将日志输出到文件 + 控制台
accessLogger, err := logger.NewJSONLogger(
	logger.WithOutputInConsole(),
	logger.WithField("domain", fmt.Sprintf("%s[%s]", configs.ProjectName, env.Active().Value())),
	logger.WithTimeLayout(timeutil.CSTLayout),
	logger.WithFileP(configs.ProjectAccessLogFile),
)

// 如果需要日志分割,可以使用 WithFileRotationP() 方法。

记录自定义调试日志,代码片段:

import "gin-api-mono/internal/pkg/debug"

// 示例一,记录多个数据
debug.WithContext(ctx.RequestContext()).Logger("这是调试信息A1", "这是调试信息A2")

// 示例二,记录单个数据
debug.WithContext(ctx.RequestContext()).Logger("这是调试信息B")

记录 MySQL 操作日志,代码片段:

// 查询数据,核心是使用 .WithContext() 方法
h.db.GetDbR().WithContext(ctx.RequestContext()).Where(queryWhere).Find(&resultData)

记录 Redis 操作日志,代码片段:

import "gin-api-mono/internal/repository/redis"

// get,核心是使用 ctx.RequestContext() 参数
getResult, err := redis.GetRedisClient().Get(ctx.RequestContext(), "name").Result()

记录 Mongo 操作日志,代码片段:

import "gin-api-mono/internal/repository/mongo"

// 获取 Mongo Client
client := mongo.GetMongoClient()

// 获取 Collection,例如 Database 为 gin_api_mono,Collection 为 user
collection := client.Database("gin_api_mono").Collection("user")

// 查询数据,核心是使用 ctx.RequestContext() 参数
findResult, err := collection.Find(ctx.RequestContext(), bson.D{})

记录请求三方 API 接口的日志,代码片段:

import "gin-api-mono/internal/pkg/httpclient"

// 支持在上下文记录执行日志的 httpclient
client := httpclient.GetHttpClientWithContext(ctx.RequestContext())

// 普通的 httpclient
client := httpclient.GetHttpClient()

// GET
resp, err := client.R().
      SetQueryParams(map[string]string{
          "page_no": "1",
          "limit": "20",
          "sort":"name",
          "order": "asc",
          "random":strconv.FormatInt(time.Now().Unix(), 10),
      }).
      SetHeader("Accept", "application/json").
      SetAuthToken("BC594900518B4F7EAC75BD37F019E08FBC594900518B4F7EAC75BD37F019E08F").
      Get("/search_result")

日志结构,代码片段:

// ./internal/pkg/trace/trace.go

// Trace 记录的参数
type Trace struct {
	mux                sync.Mutex
	Identifier         string     `json:"trace_id"`             // 链路ID
	Request            *Request   `json:"request"`              // 请求信息
	Response           *Response  `json:"response"`             // 返回信息
	ThirdPartyRequests []*HttpLog `json:"third_party_requests"` // 调用第三方接口的信息
	Debugs             []*Debug   `json:"debugs"`               // 调试信息
	SQLs               []*SQL     `json:"sqls"`                 // 执行的 SQL 信息
	Redis              []*Redis   `json:"redis"`                // 执行的 Redis 信息
	Mongos             []*Mongo   `json:"mongos"`               // 执行的 Mongo 信息
	Success            bool       `json:"success"`              // 请求结果 true or false
	CostSeconds        float64    `json:"cost_seconds"`         // 执行时长(单位秒)
}

不同组件能够将日志记录到上下文的核心是 拦截器,具体在第三方组件集成章节详细描述,此模块有些复杂,也可以微信找我沟通。


有启发,左下角点击“启发”告诉我呀,点我即可直接跳转到小册目录合集

作者新亮头像

新亮

2024/01/12

3.3 实现错误处理与告警

使用 IsAborted 函数来判断进行错误处理。

统一处理,代码片段:

// ./internal/pkg/core/core.go // region 发生......
作者新亮头像

新亮

2024/01/12

3.2 实现异常捕获与告警

使用 defer 和 recover 函数来实现异常捕获与告警。defer 用于延迟函数的执行,recover 用于捕获 panic 异常并进行处理。

框架已经集成,代......

作者新亮头像

新亮

2024/01/12

3.1 实现服务启动时可选的配置项

使用 Options 设计模式实现,它可以让我们在服务启动时根据需要选择性地配置应用程序。

可配置项,代码片段:

// ./internal/pkg/core/core.go

type option struct {
	enablePProf      bool
	enableSwagger    bool
	enablePrometheus bool
	enableCors       bool
	alertNotify      proposal.AlertHandler
	recordHandler    proposal.RecordHandler
}

// WithEnablePProf 启用 pprof
func WithEnablePProf() Option {
	return func(opt *option) {
		opt.enablePProf = true
	}
}

// WithEnableSwagger 启用 swagger
func WithEnableSwagger() Option {
	return func(opt *option) {
		opt.enableSwagger = true
	}
}

// WithEnablePrometheus 启用 prometheus
func WithEnablePrometheus(recordHandler proposal.RecordHandler) Option {
	return func(opt *option) {
		opt.enablePrometheus = true
		opt.recordHandler = recordHandler
	}
}

// WithAlertNotify 设置告警通知
func WithAlertNotify(alertHandler proposal.AlertHandler) Option {
	return func(opt *option) {
		opt.alertNotify = alertHandler
	}
}

// WithEnableCors 设置支持跨域
func WithEnableCors() Option {
	return func(opt *option) {
		opt.enableCors = true
	}
}

服务启动,代码片段:

// ./internal/router/router.go

mux, err := core.New(logger,
	core.WithEnableCors(),
	core.WithEnableSwagger(),
	core.WithEnablePProf(),
	core.WithAlertNotify(alert.NotifyHandler()),
	core.WithEnablePrometheus(metrics.RecordHandler()),
)

通过使用 Options 设计模式,我们可以根据需要选择性地配置服务器参数,使得应用程序更加灵活和可扩展。


有启发,左下角点击“启发”告诉我呀,点我即可直接跳转到小册目录合集

投资自己,终得回报
买了的宝子们一定要好好学习哦!!!