前言

Kitex是字节跳动内部的Golang微服务RPC框架,先已开源。

Kitex文档:https://www.cloudwego.io/zh/docs/kitex/getting-started/

Kitex体验:https://juejin.cn/post/7098966260502921230

在Kitex体验的文章中,我们使用Kitex从零构建了自己的服务,只要定义好IDL(接口描述语言),按照Kitex提供的命令行规则,就可以生成支持Thrift、Protobuf的客户端和服务端相关的脚手架代码,使得我们可以直接着手编写服务端的响应实现和客户端的请求发起逻辑。

那么Kitex究竟是怎么生成脚手架代码的?这系列文章将围绕此展开源码阅读,并最终解答这个疑问。

源码分析

初览kitex命令行工具

在最初安装或者更新Kitex的时候,用到了下面这条命令下载了Kitex可执行文件(用于脚手架生成):

go install github.com/cloudwego/kitex/tool/cmd/kitex

kitex是一个可执行文件,因为go install做了两件事(编译+安装),它将github.com/cloudwego/kitex/tool/cmd/kitex目录下的main.go及其依赖库编译成了一个可执行文件,再将其下载到本地的$GOPATH/bin路径下。

换句话说,你完全可以通过下面这命令将整个kitex依赖库全部下载下来:

go get github.com/cloudwego/kitex@latest

然后进入github.com/cloudwego/kitex/tool/cmd/kitex 目录去手动执行go build命令,根据目录名(包名)将其编译成一个可执行文件kitex,再将其移动到$GOPATH/bin目录下,就能复现上面go install的工作。

go build -o ~/go/bin/kitexx # 使用-o参数可以将编译的可执行文件指定位置和名称

比如我构建了一个功能强大的kitexx工具!(可以在终端中调用,只是它还没有接受命令行参数的能力,别担心!随着源码的分析我们将会扩展kitexx的功能! )

先回归Kitex,go install之后,我们在命令行中输入下面的命令就可以实现项目脚手架代码的生成:

kitex -module example -service example echo.thrift

kitex就指代$GOPATH/bin下的可执行文件kitex,后面的-module xxx..都是指定的命令行参数。

下面让我们看一下kitex负责脚手架代码生成的可执行文件编译前的代码:

# 使用tree命令查看$GOPATH/pkg/mod/github.com/cloudwego/kitex@v0.2.1/tool/cmd/kitex的目录结构,也就是这两个文件中编写了接受命令行参数、创建服务脚手架的核心代码
.
└── kitex
  ├── args.go
  └── main.go

分析main.go的初始化函数

下面这是main.go的init函数,看到初始化过程就是args调用了addExtraFlag方法,并且传入了一个extraFlag。

那么我们来看一下extraFlag的结构,通过首行注释得知,这个结构是用于添加与代码生成无关的flag的(每一个flag可以理解成kitex工具命令行需要解析的参数,后面会讲)。

结构体有两个成员函数,第二个用于检查需要添加的flag的合法性,第一个用于添加flags到FlagSet,FlagSet出自于Go标准命令行解析库flag。

看到这你大致能猜测解析命令行的工作最终还是落到了Go标准库头上,只是kitex在此基础上定制了自己需要的功能。

这篇文章介绍了命令行解析库flag的使用:https://segmentfault.com/a/1190000021143456。

那么让我们看一下FlagSet结构的源码(这里没放出来),注释描述FlagSet是一个用于存放flags的集合,并介绍了Usage的作用和触发条件。

这时候我们已经深入源码第三层了,先不急着深入,容易迷失方向。先回到最初init函数中,我们已经知道apply方法用于添加flag到FlagSet中,那么是如何添加的呢?

我们来看一下FlagSet的BoolVar方法源码,newBoolValue的作用是将value的值赋给p然后返回(可以点进去看源码),BoolVar方法的作用由注释得知,为了定义一个bool类型的flag(由name、value、usage定义)

然后调用了f.Var方法,猜测是用于将这个定义的bool类型的flag添加入FlagSet集合,看一下源码。首先检测要添加的flag的name不能以-或者=开头,然后判断map中是否存在相同名称的flag,如果有则panic,然后按步添加flag到f.formal中(map[string]*Flag)

现在大致明白,init函数的作用就是调用了args.addExtraFlag方法,添加了一个额外的不是用于代码生成的flag,至于check部分就是当遇到指定错误的时候需要终止程序。

为os.Exit()指定状态码,0表示成功,1表示内部错误,2表示无效参数

到目前为止,init函数部分基本已经分析了一遍,你可能好奇,既然在初始化阶段已经为FlagSet添加了一个和version相关flag(其实并没有完成添加,这里先卖个关子!下面解释),那么FlagSet本身是在哪里初始化的?

这里我们留意到init函数上方的全局变量args,并且留意到args.addExtraFlag也是调用自args,那么势必要看一下arguments的源码,看看能否找到FlagSet的初始化工作。

我们的目标是找到一个类似NewFlagSet的函数,那么就进入args.go使用command+f吧!果然找到了,而且只有一处!

那么再看这个buildFlags函数在哪被调用的,没办法,看来还得接着查一下parseArgs函数的调用情况。

终于你在main.go的主函数里找到了,但是问题来了,main.go文件的init()初始化函数你分析了之后是给FlagSet添加flag的,而且应该是先于主函数体执行的,那此时FlagSet还没初始化啊?这不是惊天大BUG? 事实上这就是上面我卖的那个关子

我们再看一下args.addExtraFlag方法和args的结构体,事实上,初始化的时候只是将extraFlag创建了出来,加入了一个切片,真正为FlagSet添加flag必然是等到flag.NewFlagSet方法初始化FlagSet之后。

上面这个乌龙其实在阅读源码的时候很可能遇到,因为我们在没有全面的视角的情况下,往往很多问题的出现只是缺少对源码的熟悉,只有反复推敲,才能逐渐梳理清楚。

丰富kitexx框架的功能

事实上,main.go文件的init函数初始化只添加了一个flag,说明了这个flag的name、value还有usage,但是并没有涉及到自动化构建脚手架的工作,当然这部分我相信通过继续阅读main函数的其余部分可以得到解答。但是考虑到篇幅原因,我打算将其放在下一篇文章中。

先来丰富一下我们的kitexx框架,为其添加解析命令行的功能。(现阶段只是简单使用flag标准库的一些API,后续再作更多的解释)。

flag库也可以看这篇文章:https://segmentfault.com/a/1190000021143456

将上述代码手动编译到$GOPATH/bin目录下,并且尝试通过命令行运行kitexx,输入事先添加好的两个flag(bool类型的flag后面可以不加参数),实现用我们输入的参数值替换b和s的默认值并打印。

小结

通过这篇文章,我们初步分析了kitex框架的脚手架代码生成工具的源代码的init函数。并且体验了一下实现自己的命令行解析框架kitexx。

在后续的文章中将继续分析main函数的剩余部分,并继续扩展kitexx框架的功能。

关注公众号【程序员白泽】,将同步文章更新。

Kitex源码阅读——脚手架代码是如何通过命令行生成的(一)的更多相关文章

  1. [Go] gocron源码阅读-通过第三方cli包实现命令行参数获取和管理

    gocron源码中使用的是下面这个第三方包来实现的,下面就单独的拿出来测试以下效果,和官方flag包差不多 go get github.com/urfave/cli package main impo ...

  2. django源码(2.0.2)粗解之命令行执行

    前言 django的命令行在整个的django web开发中都会经常用到,而且是必须得用到.所以,能够了解下django的命令行实现其实是非常有帮助的. 如果大家比较关心django命令的详细说明和使 ...

  3. 【原】FMDB源码阅读(三)

    [原]FMDB源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 FMDB比较优秀的地方就在于对多线程的处理.所以这一篇主要是研究FMDB的多线程处理的实现.而 ...

  4. 【原】FMDB源码阅读(二)

    [原]FMDB源码阅读(二) 本文转载请注明出处 -- polobymulberry-博客园 1. 前言 上一篇只是简单地过了一下FMDB一个简单例子的基本流程,并没有涉及到FMDB的所有方方面面,比 ...

  5. 【原】FMDB源码阅读(一)

    [原]FMDB源码阅读(一) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 说实话,之前的SDWebImage和AFNetworking这两个组件我还是使用过的,但是对于 ...

  6. 【原】AFNetworking源码阅读(六)

    [原]AFNetworking源码阅读(六) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 这一篇的想讲的,一个就是分析一下AFSecurityPolicy文件,看看AF ...

  7. 【原】AFNetworking源码阅读(五)

    [原]AFNetworking源码阅读(五) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇中提及到了Multipart Request的构建方法- [AFHTTP ...

  8. 【原】AFNetworking源码阅读(四)

    [原]AFNetworking源码阅读(四) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇还遗留了很多问题,包括AFURLSessionManagerTaskDe ...

  9. 【原】AFNetworking源码阅读(三)

    [原]AFNetworking源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇的话,主要是讲了如何通过构建一个request来生成一个data tas ...

随机推荐

  1. mpvue打包没有app.json等配置文件的解决方法

    问题 一早上折腾了1个小时,小程序始终提示查找不到'app.json'文件.mpvue重新打包,光生成内容文件无配置文件. 解决办法 出错原因:版本问题 只需要把packpage.json里的mpvu ...

  2. Python疫情爬取输出到txt文件

    在网上搬了一个代码,现在不适用了,改了改 import requestsimport jsondef Down_data(): url = 'https://view.inews.qq.com/g2/ ...

  3. Java自定义异常类的简单实现

    学习目标: 掌握自定义异常类 例题: 需求:自定义异常类,简单判断是否注册成功 代码如下: RegisterException类: /** * @author YanYang * @projectNa ...

  4. localStorage存储返回过来的对象 显示object object的问题

    localStorage.setItem() 不会自动将Json对象转成字符串形式 用localStorage.setItem()正确存储JSON对象方法是: 存储前先用JSON.stringify( ...

  5. ABP源码分析 - 约定注册(2)

    比较随意,记录下过程,以便忘了以后重拾. 所谓约定注册是指不需要明确写代码注入,只需要按约定规则写服务类,框架自动完成服务注册. 例如,只要这样写,框架就会自动注册. public class Tax ...

  6. 蓝桥杯 贪吃蛇长度java实现

    小明在爷爷的私人收藏馆里找到一台老式电脑.居然没有图形界面,只能用控制台编程. 如上图,是游戏时画面截图. 其中,H表示蛇头,T表示蛇尾.#表示蛇的身体,@表示身体交叉重叠的地方. 你能说出现在的贪吃 ...

  7. 时间篇之centos6下修复的ntp操作(ntpd和ntpdate两个服务区别)

    系统采样,本采样和命令都是在centos6.4的系统中进行 主要比较centos7和centos6之间的差异,因为大部分都开始采用centos7但是有些老系统还采用centos6,这样我们就需要熟悉c ...

  8. 『现学现忘』Git基础 — 7、设置Git Bash终端默认路径

    目录 1.Git Bash默认路径 2.如何查看Git Bash终端默认路径 3.如何修改Git Bash终端的默认路径 4.拓展:指定目录进入Git Bash终端 5.注意事项 如果您不熟悉Git命 ...

  9. React项目中使用wangeditor以及扩展上传附件菜单

    在最近的工作中需要用到富文本编辑器,结合项目的UI样式以及业务需求,选择了wangEditor.另外在使用的过程中发现wangEditor只有上传图片和视频的功能,没有上传文本附件的功能,所以需要对其 ...

  10. kubeadm 搭建 K8s

    kubeadm 搭建 K8s 本篇主要记录一下 使用 kubeadm 搭建 k8s 详细过程 ,环境使用 VirtualBox 构建的3台虚拟机 1.环境准备 操作系统:Centos7 (CentOS ...