公司线上运行的Go服务存在多个版本
荔波网站制作公司哪家好,找成都创新互联公司!从网页设计、网站建设、微信开发、APP开发、成都响应式网站建设等网站项目制作,到程序开发,运营维护。成都创新互联公司从2013年成立到现在10年的时间,我们拥有了丰富的建站经验和运维经验,来保证我们的工作的顺利进行。专注于网站建设就选成都创新互联公司。
时间:某天凌晨
事情:线上Go服务突然间 crash
紧急处理:重启Go服务
故障排查:查询日志,找出可能出现的堆栈信息以及追溯源码
问题:线上同时存在多个版本,如何知道当前 crash 的程序属于哪个版本?
方案1,手动添加版本信息:
- package main
- import (
- "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.1
- dateTime is: 2021-08-14 10:00:00
- gitTag is: v0.0.1
分析:
在很多公司甚至开源项目都会采用该方式,在代码中显式地添加版本等信息。
针对以上情况,提出一个问题:Go是编译型语言,版本等信息是否可以在编译时,自动地打包到二进制文件中?
方案2,自动打包版本信息:
- package main
- import (
- "flag"
- "fmt"
- )
- var version = "v0.0.0"// 此处暂时只填写大的版本号
- var gitTag string
- var dateTime string
- 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")
- }
在编译时,打包版本等信息到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
build 通过 -ldflags 的 -X 参数可以在编译时将值写入变量
变量格式:包名称.变量名称=值
查看版本信息
- code ./client -ver
- version is: v0.0.1
- dateTime is: 2021-08-14 10:00:00
- gitTag 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中的string类型结构体大小。
- (gdb) ptype version
- type = struct string {
- uint8 *str;
- int len;
- }
- (gdb) ptype dateTime
- type = struct string {
- uint8 *str;
- int len;
- }
- (gdb) ptype gitTag
- type = struct string {
- uint8 *str;
- int len;
- }
不知细心的你是否发现,在符号表显示的变量具体值 main.version.str、main.dateTime.str、main.gitTag.str长度都比实际多一个字节。
虽然目前Go实现了自举,但是编译Go编译器的编译器还是用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"
当前标题:Go编译时写入数据的原理
网站URL:http://www.36103.cn/qtweb/news47/9597.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联