知识点
重构、调整结构
本文目标
这个应用程序跑了那么久了,越来越大,越来越壮,仿佛我们的产品一样,现在它需要进行小范围重构了,以便于后续的使用,这非常重要。
前言
一天,产品经理突然跟你说文章列表,没有封面图,不够美观,!)&¥*!&)#&¥*!加一个吧,几分钟的事
你打开你的程序,分析了一波写了个清单:
优化配置结构(因为配置项越来越多)
抽离 原 logging 的 File 便于公用(logging、upload 各保有一份并不合适)
实现上传图片接口(需限制文件格式、大小)
修改文章接口(需支持封面地址参数)
增加 blog_article (文章)的数据库字段
实现 http.FileServer
嗯,你发现要较优的话,需要调整部分的应用程序结构,因为功能越来越多,原本的设计也要跟上节奏
也就是在适当的时候,及时优化
优化配置结构
一、讲解
在先前章节中,采用了直接读取 KEY 的方式去存储配置项,而本次需求中,需要增加图片的配置项,总体就有些冗余了
我们采用以下解决方法:
映射结构体:使用 MapTo 来设置配置参数
配置统管:所有的配置项统管到 setting 中
映射结构体(示例)
在 go-ini 中可以采用 MapTo 的方式来映射结构体,例如:
type Server struct {
RunMode string
HttpPort int
ReadTimeout time.Duration
WriteTimeout time.Duration
}
var ServerSetting = &Server{}
func main() {
Cfg, err := ini.Load("conf/app.ini")
if err != nil {
log.Fatalf("Fail to parse 'conf/app.ini': %v", err)
}
err = Cfg.Section("server").MapTo(ServerSetting)
if err != nil {
log.Fatalf("Cfg.MapTo ServerSetting err: %v", err)
}
}
在这段代码中,可以注意 ServerSetting 取了地址,为什么 MapTo 必须地址入参呢?
// MapTo maps p to given struct.
func (s *Section) MapTo(v interface{}) error {
typ := reflect.TypeOf(v)
val := reflect.ValueOf(v)
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
val = val.Elem()
} else {
return errors.New("cannot map to non-pointer struct")
}
return s.mapTo(val, false)
}
在 MapTo 中 typ.Kind() == reflect.Ptr
约束了必须使用指针,否则会返回 cannot map to non-pointer struct
的错误。这个是表面原因
更往内探究,可以认为是 field.Set
的原因,当执行 val := reflect.ValueOf(v)
,函数通过传递 v
拷贝创建了 val
,但是 val
的改变并不能更改原始的 v
,要想 val
的更改能作用到 v
,则必须传递 v
的地址
显然 go-ini 里也是包含修改原始值这一项功能的,你觉得是什么原因呢?
配置统管
在先前的版本中,models 和 file 的配置是在自己的文件中解析的,而其他在 setting.go 中,因此我们需要将其在 setting 中统一接管
你可能会想,直接把两者的配置项复制粘贴到 setting.go 的 init 中,一下子就完事了,搞那么麻烦?
但你在想想,先前的代码中存在多个 init 函数,执行顺序存在问题,无法达到我们的要求,你可以试试
(此处是一个基础知识点)
在 Go 中,当存在多个 init 函数时,执行顺序为:
相同包下的 init 函数:按照源文件编译顺序决定执行顺序(默认按文件名排序)
不同包下的 init 函数:按照包导入的依赖关系决定先后顺序
所以要避免多 init 的情况,尽量由程序把控初始化的先后顺序
二、落实
修改配置文件
打开 conf/app.ini 将配置文件修改为大驼峰命名,另外我们增加了 5 个配置项用于上传图片的功能,4 个文件日志方面的配置项
[app]
PageSize = 10
JwtSecret = 233
RuntimeRootPath = runtime/
ImagePrefixUrl = http://127.0.0.1:8000
ImageSavePath = upload/images/
# MB
ImageMaxSize = 5
ImageAllowExts = .jpg,.jpeg,.png
LogSavePath = logs/
LogSaveName = log
LogFileExt = log
TimeFormat = 20060102
[server]
#debug or release
RunMode = debug
HttpPort = 8000
ReadTimeout = 60
WriteTimeout = 60
[database]
Type = mysql
User = root
Password = rootroot
Host = 127.0.0.1:3306
Name = blog
TablePrefix = blog_
优化配置读取及设置初始化顺序
第一步
将散落在其他文件里的配置都删掉,统一在 setting 中处理以及修改 init 函数为 Setup 方法
打开 pkg/setting/setting.go 文件,修改如下:
package setting
import (
"log"
"time"
"github.com/go-ini/ini"
)
type App struct {
JwtSecr