去找内部服务

2020-11-09 15:07:15

Go/Golang是Google的开源编程语言,通常用于后端/系统工程。它的主要优点是静态类型、快速编译以及通过其有限的特性集实现的简单性。

几年来,我一直在GO做内部服务的日常工作,从各种成功案例、事故事后回顾以及与其他工程师的对话中,我注意到了一些共同的主题。这篇帖子启发我写下了其中的一些经历。

Go VET是一个静态分析器,它检查常见的缺陷,比如使用对迭代器变量的引用。

自动将其作为Linteror或CI运行,以便在问题投入生产之前将其捕获。

Staticcheck是另一种静态分析器,可以捕获死代码等问题。根据我的经验,在本地运行大型项目太慢了,但作为预提交的CI签入很有用。

在实践中,通常会假设如果返回的错误为nil,则返回值为非nil,反之亦然。如果错误为nil,则尝试在所有情况下验证返回值是否为非nil会增加冗长,但好处很小。因此,如果您正在编写一个具有许多不同用例的库,那么如果err对于调用者来说是nil,那么保证一个非nil值是很有用的。我不知道有什么很好的解决方案来自动标记它,因为在某些情况下同时返回两个Nil是有效的。

在大多数情况下,服务应该因恐慌而终止。由于死机可能源自代码库中的任何位置并暂停执行,因此不会运行错误处理代码(恢复除外),内部状态可能会变得不一致。换句话说,“所有的赌注都取消了”。死机处理程序应该是一个顶级函数,它会尽最大努力向外部系统报告异常,然后终止。确定性恐慌应该通过SLO警报(如可用性)或在任务级别检查崩溃循环来捕获。

按照惯例,在有效错误场景中死机的函数必须以‘Must`为前缀。这向调用者发出信号,他们正在调用的函数应该经过彻底的审查,并且在没有经过清理的情况下不能传递任意输入。我曾经遇到过任意输入导致一小部分机队崩溃的事件,这些事件很难追踪(因为我们没有通过向异常报告系统报告来处理恐慌)。

这个问题特别值得一提的是--即使您已经在Go中工作了几年,也需要花费很多小时来调试它们,所以这是值得记住的。

For_,val:=范围值{go func(){fmt.Println(Val)//没有做您期望的事情}()}。

Go提供了一个竞争检测器来捕获并发编程错误。它通常会使内存使用量翻倍,并减慢程序的运行速度。用户可以在竞争模式下在CI中运行测试以捕获问题。

偶尔,bug在被竞争检测器捕获之前就被部署到生产中,因为它们不确定地失败了。对更改进行几个小时的“烘焙”/运行压力测试,直到某些自动化可以捕捉到这些薄片,这是很有用的。通常,竞争检测器会捕捉到仅限测试的问题,因为开发人员通常不会过多地考虑测试的并发性。

尝试在竞争模式下部署Canary实例来主动检测问题似乎是个好主意,但这会导致显著的延迟倒退,因此再也没有尝试过。

注意呼叫者的最后期限--他们可能没有为您的服务提供足够的时间来完成它自己的工作。例如,您的服务可能需要500ms才能完成一项操作,但调用者可能会出于任何原因将截止时间指定为100ms(通常嵌套调用者的截止时间很短)。这些将在您的服务中表现为超时,并影响其SLA。如果您的服务没有提供有意义的时间来完成工作,那么您的服务应该失败。如果到处检查这一点听起来有点过分--它值得记录调用者收到的截止日期,这样你就可以在下一次调试神秘的超时时追踪到来源。

通过库记录错误堆栈跟踪,这样您就不会纠结于错误是从哪里来的。

围棋是我在生产中使用的“围棋”语言。希望这些经验能为你在Go中开发下一项服务时提供一些思考的素材。