事情

企业有好几个版本升级的Go服务项目线上运作。

時间:一天早晨。

事儿:线上Go服务项目忽然奔溃。

应急解决:重新启动Go服务项目。

常见故障清除:查看日志,找到很有可能的局部变量信息内容和追踪源码。

难题:在网上与此同时有好几个版本号。你怎么知道当今奔溃程序流程归属于哪一个版本号?

加上版本信息的二种计划方案。

计划方案一,手动式加上版本信息:

package mainimport ("flag""fmt")// 下边三个自变量,每一次发版都需要改动var version = "v0.0.1" // 程序流程版本信息var gitTag = "v0.0.1" // git tag 号var dateTime = "2021-08-14 10:00:00" // 编译程序转化成時间func main() {debugVerInfo := flag.Bool("ver", false, "show app version info")flag.Parse()if *debugVerInfo {fmt.Println("version is:", version)fmt.Println("dateTime is:", dateTime)fmt.Println("gitTag is:", gitTag)return}fmt.Println("do other thing")}

因为版本信息是手动式加上到指令中的,因而在常见故障清除全过程中能够查询对应的信息内容。

➜ code ✗ ./client -ver version is: v0.0.1dateTime is: 2021-08-14 10:00:00gitTag is: v0.0.1

剖析:

在很多企业乃至开源软件中,这类办法用以显式地向编码中加上版本号和别的信息内容。

假定不常常发版或是发版周期时间较为长,则完全没问题假定发版经常,非常大几率会发生版本信息的忽略,不正确假定版本信息忘掉变更,则查看出去的信息内容便是错的

对于上述所说情况,文中明确提出一个难题:Go是编程语言,编译程序时是不是能够将版本号等信息内容全自动包装成二进制文件?

计划方案二,全自动封裝版本信息:

package mainimport ("flag""fmt")var version = "v0.0.0"// 这里临时只填好大的版本信息var gitTag stringvar dateTime stringfunc main() {debugVerInfo := flag.Bool("ver", false, "show app version info")flag.Parse()if *debugVerInfo {fmt.Println("version is:", version)fmt.Println("dateTime is:", dateTime)fmt.Println("gitTag is:", gitTag)return}fmt.Println("do other thing")}

在编译程序时,将版本号和别的信息内容装包到Go的二进制文件中:

go build -ldflags \"-X main.version=v0.0.1 -X main.dateTime=`date %Y-%m-%d,%H:%M:%S` -X main.gitTag=`git tag`" \ -o client

-ldflags的-X主要参数容许您在编译程序时将值载入自变量。

自变量文件格式:包名。用户标识符=值。

查询版本信息。

➜ code ✗ ./client -ver version is: v0.0.1dateTime is: 2021-08-14 10:00:00gitTag is: v0.0.1

优势:

不用编码中显式加上版本号等信息内容防止手动式加上版本信息时,忽略或是不正确等状况产生可应用持续交付专用工具全自动把版本号等信息内容装包到二进制文件中

标准

二进制文件载入到运行内存后,全部运行内存空将被分为好多个段。除开编码区,数据信息区,堆和栈,还有一个段做为符号表。

编译程序时,版本号和别的数据被整理到符号表中,供程序执行时应用。

[root@localhost demo]# readelf -s client | grep main...... 1686: 00000000005608b0 16 OBJECT GLOBAL DEFAULT 10 main.version 1687: 00000000005608a0 16 OBJECT GLOBAL DEFAULT 10 main.gitTag 1688: 0000000000560890 16 OBJECT GLOBAL DEFAULT 10 main.dateTime...... 2320: 00000000004eb2e8 7 OBJECT GLOBAL DEFAULT 2 main.version.str 2321: 00000000004ebba0 20 OBJECT GLOBAL DEFAULT 2 main.dateTime.str 2322: 00000000004eb2e0 7 OBJECT GLOBAL DEFAULT 2 main.gitTag.str

应用readelf -s指令查询编译程序后的Go二进制文件的符号表的信息内容,能够了解的见到编译程序时写的三个自变量。

在其中main.version,main.gitTag,main.dateTime的大小均为16,指Go中字符串数组构造的尺寸。

(gdb) ptype versiontype = struct string { uint8 *str; int len;}(gdb) ptype dateTimetype = struct string { uint8 *str; int len;}(gdb) ptype gitTagtype = struct string { uint8 *str; int len;}

不清楚各位是否有注意到,符号表中表明的main.version.str,main.dateTime.str,main.gitTag.str的实际值都比具体长一个字节。

现阶段,尽管Go早已完成了自举,可是编译程序Goc语言编译器的c语言编译器依然是用C语言撰写的。

c语言字符串数组(字节数二维数组)不是可靠的种类,尾端零用以标识字符串数组的末尾。在其中,跟随零也占有一个字节。

尾端零是ASCII的第一个原素0,也就是NUL。

(gdb) p version$1 = "v0.0.1"(gdb) p dateTime$2 = "2021-08-13,23:26:44"(gdb) p gitTag$3 = "v0.0.1"

评论(0条)

刀客源码 游客评论