10、实现统一的错误返回
10、实现统一的错误返回在 Go 项目开发中,为了方便客户端处理返回,排查错误,还需要实现统一的错误返回。统一的错误返回包括以下 2 个方面:
错误格式统一:返回统一的错误格式,方便客户端解析,并获取错误;
自定义业务错误码:HTTP 的错误码有限,并且不适合业务错误码。所以,在实际开发中,还需要自定义业务错误码。
为了实现统一的错误返回,接下来还需要实现错误包和自定义错误码。
错误返回方法先来看下错误返回的方式。在 Go 项目开发中,错误的返回方式通常有以下两种
始终返回 HTTP 200 状态码,并在 HTTP 返回体中返回错误信息;
返回 HTTP 400 状态码 (Bad Request),并在 HTTP 返回体中返回错误信息。
方式一: 成功返回,返回体中返回错误信息例如 Facebook API 的错误返回设计,始终返回 200 HTTP 状态码:
在上述错误返回的实现方式中,HTTP 状态码始终固定返回 200,仅需关注业务错误码,整体实现较为简单。然而,此方式存在一个明显的缺点:对于每一次 HTTP 请求,既需要检查 HTTP 状态码以判断请求是否成功,还需要 ...
9、给应用添加优雅关停功能
9、给应用添加优雅关停功能在成功实现一个简单的 Web 服务器之后,接下来需要进一步完善其核心功能,使其更贴合实际需求并满足企业级应用场景的要求。例如,需要实现功能丰富的中间件、处理跨域访问、以及支持优雅关停等功能。本节课将深入个绍这些关键功能的实现,帮助打造一个更加健壮、高效、且易于维护的 Web 服务器。
添加优雅关停功能Web 服务器通常都需要实现优雅关停功能。优雅关停服务器可以带来很多好处,例如提高 API 接口的成功率,减少系统脏数据的出现概率等。本节会详细介绍如何实现优雅关停功能。
优雅关停的必要性在应用程序的生命周期中,新功能发布、缺陷修复、配置变更等操作都需要重启服务。在服务进程停止时,可能需要执行一些必要的处理工作,例如:
正在执行的 HTTP 请求需要等待其完成并返回结果,否则可能会导致请求报错或产生脏数据
异步处理任务需要将缓存中的数据处理完成,否则可能会导致数据丢失或不一致:
关闭数据库连接,否则数据库连接池可能会保留无效连接,浪费宝贵的连接资源,
为了解决上述问题,建议的做法是给应用添加优雅关停功能,以提高系统的健壮性。
优雅关停的实现思路实现优雅关停的最 ...
8、给Gin服务器添加中间件
8、给Gin服务器添加中间件Web中间件介绍中间件(Middleware) 是位于应用程序请求-响应处理循环中的一个特殊函数。它可以在请求到达业务逻辑处理之前修改/处理请求,或是在响应返回给客户端之前修改/处理响应。中间件根据使用方又可分为客户端中间件和服务端中间件,两者在实现原理和使用方式上是一致的。
中间件的核心作用是对请求或响应进行预处理、后处理或监控。它允许在请求和响应被发送或接收之前或之后插入自定义逻辑,从而实现多种功能,例如认证、授权、日志记录、性能监控、错误处理、请求验证、跨域支持、限流等。以下是核心使用场景的详细说明:
认证和授权:使用中间件可以实现认证和授权逻辑。在中间件中,可以验证请求者的身份、权限等信息,并根据情况决定是否允许请求继续进行;
日志记录:中间件可以用于记录请求和响应的详细信息,从而实现日志记录和监控。可以记录请求的内容、调用的方法、响应的结果等,以便于调试和分析;
错误处理:在中间件中可以捕获和处理 9RPC 调用过程中可能发生的错误,以提供更友好的错误信息或进行恢复操作;
性能监视:使用中间件可以监视 Web 调用的性能指标, ...
7、基于Gin实现HTTP服务器
7、基于Gin实现HTTP服务器在go项目开发中,开发场景很多的是开发一个web服务器、web服务器种类有很多,例如:Http服务器、RPC服务器、webSocket服务器等。
其中Http服务器是最常需要开发的服务器类型。HTTP就是一个对外提供API接口的Web服务器。API接口其实是有规范的,当前用的最多的REST规范。
REST Web框架选择要编写一个RESTful风格的API服务器,你可以自己调用 net/http 包手动实现一个,但这样耗时且效果不好,所以通常会使用web框架来开发一个REST服务器
Web框架有很多,例如:Gin、Hertz、Echo、Eiber等。用的最多的是Gin,查看性能对比
开发一个简单的REST服务器使用Gin框架开发一个REST服务器分为以下几步:
配置REST服务器(配置监听端口)
创建Gin引擎
设置Gin路由
启动Gin服务器
步骤一:配置REST服务器修改cmd/fg-apiserver/app/options/options.go 文件,给ServerOptions结构体添加Ad ...
6、实现日志打印功能
6、实现日志打印功能打印日志需要一个日志包,日志包的选择通常有以下几种手段
自研:根据需要从0自研一个日志包
复用团队/公司基本的日志包:更多的情况,是复用公司级别的日志包
复用开源日志包:如sirupsen/logrus、uber-go/zap
go标准库也自带了一个生产可用的日志包log/slog
Go代码使用日志包的步骤
初始化日志包
调用日志包
步骤一:初始化日志包修改cmd/fg-apiserver/server.go文件
func initLog() {
format := viper.GetString("log.format") // 日志格式,支持:json、text
level := viper.GetString("log.level") // 日志级别,支持:debug、info、warn、error
output := viper.GetString("log.output") // 日志输出路径,支持:标准输出stdout和文件
// 转换日志级别
var slevel slo ...
6、实现版本号打印功能
5、实现版本号打印功能go项目中,为了方便排障等,需要知道某个线上应用的具体版本。另外,在开发命令行工具的时候,也需要支持-v / --version 之类的命令行参数。这时候就需要给应用添加版本号打印功能。
如何添加版本号?实际开发中国,当完成一个应用特性开发后,会编译 应用源码并发布到生产环境。为了定位问题或出于安全考虑(确认发布的是正确的版本),开发者通常需要了解应用的版本信息及一些编译时的详细信息,例如编译时使用的go版本、git目录是否干净,以及哪个git提交id进行的编译。在一个编译好的可执行程序中,通常可以通过类似/appname -v 的方式来获取版本信息。
可以将这些信息写入版本号配置文件中,程序运行时从版本号配置文件中读取并显示。然而,在程序部署时,除了二进制文件外,还需要额外的版本号配置文件,这种方式既不方便,又面临版本号配置文件被篡改的风险,另一种方式是将这些信息直接写入代码,这样无需额外的版本号配置文件,但每次编译时都需要修改代码以更新版本号,这种方式同样不够优雅。
Go官方提供了一种更优的方式:通过编译时指定-ldflags -X impor ...
4、框架代码和业务代码分离
4、框架代码和业务代码分离在go项目中,项目代码通常分为2类:
应用启动框架:例如:命令行框架、配置文件解析和读取、配置项校验等。
业务相关的代码:业务相关的代码变更相较于应用框架部分,变更频繁,而且可能会影响业务。
为了提高项目可维护性,将两类代码分类拆分,从目录级别隔离,减少2类代码变更时互相影响的概率
优化ServerOptions结构体定义分别存放在以下两个目录:
应用框架代码: cmd/fg-apiserver/app
运行时代码:internal/appiserver
2部分代码有些配置会共享,为了提高代码复用性,需要将cmd/fg-apiserver/app/options/options.go 文件中可复用配置拆分成一个独立的包,共两种类别的代码引用。
将MYSQLOptions结构体定义放在pkg/options/mysql_options.go文件中。因为我们经常要基于MySQLOptions 结构体的字段创建*gorm.DB类型的数据库实例。为了提高代码调用效率,给MYSQLOption ...
3、使用viper添加配置文件解析功能
3、使用viper添加配置文件解析功能因为配置几乎是每个服务都需要的能力,所以,在使用cobra开发完二进制命令的主题框架后,还需要实现配置文件解析能力。
go项目开发中配置解析源有多种,例如:命令行参数、环境变量、配置文件等。但是推荐的配置解析源是配置文件,因为配置文件更易维护。
Go社区提供了很多优秀的Go包可以读取yaml、json等格式的配置文件,但是目前使用最多的是spf13/viper包
配置文件解析思路在Go项目开发中,服务的配置能力一般通过以下2步实现:
解析配置文件
读取配置
其中配置文件考虑到易读性,通常的使用格式更加易读的YAML格式。并且使用spf13/viper包来解析。
所以,可以通过设置钩子函数,来让程序运行时加载并读取配置。更新cmd/fg-apiserver/app/server.go文件
package app
import (
"encoding/json"
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"gitlab.com/onexstack/fastg ...
2、使用Cobra包构建go项
2、使用Cobra包构建go项目Go程序运行入口是main函数,首先要开发出main函数
可以使用以下两个方式开发一个main函数
手动写一个main函数,实现业务逻辑,并启动程序。
使用应用框架,实现一个main函数
手动写一个main函数并启动
package main
import (
"flag"
"fmt"
)
func main() {
// 解析命令行参数
option1 := flag.String("option1", "default_value", "Description of option 1")
option2 := flag.Int("option2", 0, "Description of option 2")
flag.Parse()
// 执行简单的业务逻辑
fmt.Println("Option 1 value:", *option1)
fmt.Println("Option 2 value:", *option2)
// 在这里添加您的业务逻辑代码
}
这种mian不能实现很复杂的功能,当应用程 ...
1、初始化Fastgo项目仓库
1、初始化fastgo项目仓库项目开发第一步,初始化一个项目目录,并根据这个目录规范,添加必要的目录及文件 golang-standards/project-layout
初始化一个go项目,分为以下几步:
创建项目目录
初始化目录为go模块
初始化目录为git仓库
创建需要的目录
创建helloworld 程序
步骤一:创建项目目录开发Go项目第一步就是创建一个项目目录。现在go模块管理的都用Go Modules。虽然在使用gomodules的亲看过下,不需要在设置GOPATH环境变量。但是为了提高项目维护性,这里还是建议将项目放在GOPATH目录下。
初始化目录,操作命令如下:
(base) dujie@MacBook-Pro GolandProjects % mkdir -p $GOPATH/src/gitlab.com/onexstack/fastgo
(base) dujie@MacBook-Pro GolandProjects % cd $GOPATH/src/gitlab.com/onexstack/fastgo
(base) dujie@MacBook-P ...