在Go中,函数允许多个返回值,因此如果函数可能失败,它很可能返回一个错误值。这种语言鼓励开发人员显式地检查错误发生的地方(而不是抛出和捕获异常),因此代码通常会有一个“if err != nil”检查。在分布式系统中,可以通过包装错误轻松地启用重试。
网络问题总是会在系统中遇到,无论是向其他内部服务发送数据,还是向第三方工具推送数据。这个来自Net包的示例强调了如何利用错误作为一种类型来区分临时网络错误和网络错误。当将内容推送到外部api时,团队使用类似的错误包装来构建增量重试。
package net
typeError interface {
error
Timeout() bool // Is the error a timeout?
Temporary() bool // Is the error temporary?
}
ifnerr, ok := err.(net.Error); ok && nerr.Temporary() {
time.Sleep(1e9)
continue
}
iferr != nil {
log.Fatal(err)
}
Go的作者认为并非所有异常都是例外。鼓励工程师明智地从错误中恢复,而不是让应用程序失败。此外,Go错误处理允许您对错误进行更多的控制。在内容平台中,Go的这个设计特性使开发人员能够围绕错误做出深思熟虑的决策,从而增强了整个系统的可靠性。
一致性
一致性是内容平台中的一个关键因素。内容是业务的核心,而内容平台的目标是确保内容可以发布一次并可以到处阅读。因此,每个产品和消费者都必须具有内容平台API的一致性。产品主要使用GraphQL查询API,这需要一个静态模式作为消费者和平台之间的契约。平台处理的内容需要符合这一模式。静态语言有助于实现这一点,并在确保数据一致性方面能轻松取胜。
Go与测试
另一个提高一致性的特性是Go的测试包。Go的快速编译能力和易于测试的特性相结合,使团队能够将强大的测试实践嵌入到工作流和构建Pipeline中。Go的测试工具使它们易于安装和运行。运行“go test”将在当前目录中运行所有测试,测试命令有几个有用的特性标志。“Cover”标志提供关于代码覆盖率的详细报告。“bench”测试运行基准测试。TestMain函数为额外的测试提供便利的功能,例如模拟身份验证服务器。
此外,Go能够使用匿名结构创建表测试,并使用接口创建模拟,从而提高测试覆盖率。尽管就语言特性而言,测试并不是什么新鲜事,但是Go使得编写健壮的测试并将其无缝地嵌入工作流变得很容易。从一开始,工程师们就能够在构建Pipeline的过程中运行测试,而不需要进行特殊的定制。
然而,该项目在实现一致性方面并非没有困难。该平台面临的个主要挑战是从不可预知的后端管理动态内容。该平台主要通过JSON端点(Endpoint)使用来自CMS系统的内容,而JSON端点不能保证数据结构和类型。这意味着平台不能使用Go的标准编码json包,该包支持将json解组到结构(Struct)中,但是如果结构字段和传入的数据字段类型不匹配,就会出现异常。
为了克服这个挑战,需要一种将后端映射到标准格式的自定义方法。在对该方法进行了几次迭代之后,团队实现了一个自定义的数据编出流程。虽然这种方法感觉有点像重新构建标准的lib包,但它为工程师提供了处理源数据的细粒度控制。
网络支持
可伸缩性是新平台关注的焦点,Go的网络和api标准库支持可伸缩性。在Go中,可以在不需要框架的情况下快速实现可伸缩的HTTP端点入口。在下面的示例中,标准库net/http包用于接受用户请求并响应。当内容平台实施时,首先尝试使用了一个API框架。随着团队认识到标准库能够满足所有的网络需求而又不增加额外的负担,终标准库取代了该框架。Golang HTTP处理程序是可伸缩的,因为处理程序上的每个请求都在一个轻量级线程Goroutine中并发运行。
package main
import(
"fmt"
"log"
"net/http"
)
funchandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello World!")
}
funcmain() {
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe(":8080", nil))
}