go #源码分析 #v1.0.0 #学习
概述
首先将代码切换到 v1.0.0 标签, 这里我从 cmd/iam-apiservice/apiservice.go 文件开始分析, 并跟随所有的 run 函数跟进学习如何运行。
入口
cmd/iam-apiservice/apiservice.go
入口文件是 cmd/iam-apiservice/apiservice.go 这是只包括一个 main 函数
里面的内容较少,主要功能函数只有 5 行左右
func main() {rand.Seed(time.Now().UTC().UnixNano())if len(os.Getenv("GOMAXPROCS")) == 0 {runtime.GOMAXPROCS(runtime.NumCPU())}apiserver.NewApp("iam-apiserver").Run()}
- 第 2 行 设置随机种子为当前启动时间, 没有啥别特的
- 第 3 行 使用
os.Getenv获取 GOMAXPROCS 环境变量的值, 若其为 0 则使用runtime.GOMAXPROCS设置程序的并发性, 当环境变量设置时会使用环境变量. 参考 Go语言GOMAXPROCS(调整并发的运行性能) 在 go 1.5 版本以上就默认执行这个操作了. 不过这里设置为 0 的时候是不是就是单线程运行?? - 第 7 行 调用
internal中的apiserver.NewApp构建 APP 并 运行. 从这里我们扩展去看NewApp的实现apiserver.NewApp
internal/apiserver/apiserver.go
这个函数完成了一个通用 APP 对象的构建
// NewApp creates a App object with default parameters.func NewApp(basename string) *app.App {opts := options.NewOptions()application := app.NewApp("IAM API Server",basename,app.WithOptions(opts),app.WithDescription(commandDesc),app.WithDefaultValidArgs(),app.WithRunFunc(run(opts)),)return application}
- 第 1 行 它写的注释是说此函数是利用默认参数创建一个 app 对象.
- 第 2 行 函数定义中的返回值
app.APP应该是定义在pkg中的一个通用 APP 启动 SDK,后面详细分析. - 第 3 行 建立一个新的选项信息,所有的选项信息被定义在
options包中. - 第 4 - 10 行 这里是利用 选项模式 实现的一个 app 构造函数. 后面将逐一对
WithOptions,WithDescription,WithDefaultValidArgs,WithRunFunc进行分析. - 第 10 行 这里调用了本身的
run函数,应该是启动 server 的关键函数。
下面基于我目前的开发需求, 首先对 run 函数进行分析. app 和 options 包在以后时间多的情况下进行详细分析.
run
internal/apiserver/apiserver.go
这个函数返回了一个 app.RunFunc , 这是一个函数类型的定义 **type RunFunc func(basename string) error**.
func run(opts *options.Options) app.RunFunc {return func(basename string) error {log.Init(opts.Log)defer log.Flush()cfg, err := config.CreateConfigFromOptions(opts)if err != nil {return err}return Run(cfg)}}
- 第 3 行 初始化 log
- 第 4 行 在整个程序被退出时, 执行
Flush将数据刷入文件. 这里是一个巧妙的设计, 由于这个函数是一个返回值, 其本质是在_main_函数中才被调用, 所以这里的 defer 一定是在程序退出之后执行。 - 第 6 - 9 行 利用 option 生成 config.
- 第 11 行 调用
Run函数启动服务.
Run
internal/apiserver/run.go
这是一个永远运行不会退出的函数, 个人认为这个函数的好处是可以让 service 的启动运行变的简单, 隔离了 service 运行和命令行输入相关功能, 之间使用 config 做桥梁。
// Run runs the specified APIServer. This should never exit.func Run(cfg *config.Config) error {server, err := createAPIServer(cfg)if err != nil {return err}return server.PrepareRun().Run()}
- 第 3 行 创建 APIServer 实例
- 第 8 行 Server 启动
下面紧跟 Run 和 PrepareRun 函数研究执行过程
apiServer.PrepareRun
internal/apiserver/server.go
server 运行前的准备, 返回一个准备好的 service。为什么不直接返回 server 而是要再处理一步,是为了函数地职责单一吗? 这里对server的状态也做了划分, 目前看可以是 _**利用配置创建 -> server启动准备 -> server 启动**_
func (s *apiServer) PrepareRun() preparedAPIServer {initRouter(s.genericAPIServer.Engine)s.initRedisStore()s.gs.AddShutdownCallback(shutdown.ShutdownFunc(func(string) error {mysqlStore, _ := mysql.GetMySQLFactoryOr(nil)if mysqlStore != nil {return mysqlStore.Close()}s.gRPCAPIServer.Close()s.genericAPIServer.Close()return nil}))return preparedAPIServer{s}}
- 第 2 行 初始化路由
- 第 4 行 初始化 Redis
- 第 6 - 15 行 建立一个在程序被 shutdown 后的资源处理函数
- 第 18 行 返回一个名为准备好的 APIServer 结构
preparedAPIServer.Run
internal/apiserver/server.go
func (s preparedAPIServer) Run() error {go s.gRPCAPIServer.Run()// start shutdown managersif err := s.gs.Start(); err != nil {log.Fatalf("start shutdown manager failed: %s", err.Error())}return s.genericAPIServer.Run()}
