拟有一需求场景如下:为了监控服务器主机的运行状况,程序会定期采集主机上一些信息,例如磁盘、CPU使用情况等,同时获取主机名作为endpoint,以此标识采集的主机对象,最后将采集数据上传至监控平台。
明确一个前提,服务器主机名在采集程序运行期间是不变的。对于以上场景,本文只讨论一个点,如何获取主机名。问题似乎很简单,直接执行Go封装的系统调用函数os.Hostname()即可,so easy。但是,我们需要注意到os.Hostname()函数的返回值是两个,第二个返回值是error。这意味着,会存在获取主机名失败的情况。
讨论
1. 主机名是不变的,是否可以以初次调用os.Hostname()函数获取的主机值为准,存入全局变量hostname,后续均取此值。但是该方案存在问题:如果初次调用时,存在错误,endpoint就会为空,那岂不是此后所有的endpoint都为空了?
2. 既然主机名存在获取失败的情况,那么程序在需要主机名时,是否需要每次去调用os.Hostname()函数。但是这样频繁系统调用,有性能损耗,也没必要。
根据以上两种考量,最佳实践如下
1package common
2
3import "os"
4
5var hostname string
6
7func Hostname() string {
8 if hostname != "" {
9 return hostname
10 }
11 h, err := os.Hostname()
12 if err != nil {
13 return ""
14 }
15 hostname = h
16 return hostname
17}
以上的核心思想就是,取第一次无错误返回的hostname作为全局变量。调用common.Hostname()时判断hostname是否已经被赋值,若已赋值,直接返回hostname,否则执行os.Hostname()函数,并判断是否给hostname赋值。
“我们往往不知道错误什么时候发生,也并不能保证程序永远正确”。但是,却可以用最小的代价(最开始执行os.Hostname()可能发生错误,造成hostname值为空)换取程序最稳定的运行(后续隐式取hostname值)。
总结
本文中的解决方案其实和单例模式很接近,比较好理解。
当程序获取某信息的过程中会发生错误,且该信息是不变项,则可以参照本文的处理方案。例如,读取不变的文件信息。
当然,细心思考的读者可能会提出重试方案:当获取信息时发生错误,就重新尝试获取。上述场景中,当然可以加上重试机制,但是一定要控制好重传超时和次数,不能因为当前时间段内的失败而阻塞在重传处,进而影响到整体的程序运行。
往期精彩推荐
Golang技术分享
长按识别二维码关注我们
更多golang学习资料
回复关键词1024