page contents

golang语言有哪些优势

Go 确实有一些很棒的特性,所以我在这篇文章中展示了它的优点。

attachments-2021-11-pRH67Kwv6195be57eca38.png

Go 确实有一些很棒的特性,所以我在这篇文章中展示了它的优点。

Go 很容易学习

这是事实:如果你了解任何一种编程语言,那么通过在「Go 语言之旅」学习几个小时就能够掌握 Go 的大部分语法,并在几天后写出你的第一个真正的程序。阅读并理解 实效 Go 编程,浏览一下「包文档」,玩一玩 Gorilla 或者 Go Kit 这样的网络工具包,然后你将成为一个相当不错的 Go 开发者。

这是因为 Go 的首要目标是简单。当我开始学习 Go,它让我想起我第一次 发现 Java:一个简单的语言和一个丰富但不臃肿的标准库。对比当前 Java 沉重的环境,学习 Go 是一个耳目一新的体验。因为 Go 的简易性,Go 程序可读性非常高,虽然错误处理添加了一些麻烦(更多的内容在下面)。

Go 语言的简单可能是错误的。引用 Rob Pike 的话,简单既是复杂,我们会看到简单背后有很多的陷阱等着我们去踩,极简主义会让我们违背 DRY(Don't Repeat Yourself) 原则。

基于 goroutines 和 channels 的简单并发编程

Goroutines 可能是 Go 的最佳特性了。它们是轻量级的计算线程,与操作系统线程截然不同。

当 Go 程序执行看似阻塞 I/O 的操作时,实际上 Go 运行时挂起了 goroutine ,当一个事件指示某个结果可用时恢复它。与此同时,其他的 goroutines 已被安排执行。因此在同步编程模型下,我们具有了异步编程的可伸缩性优势。

Goroutines 也是轻量级的:它们的堆栈 随需求增长和收缩,这意味着有 100 个甚至 1000 个 goroutines 都不是问题。

我以前的应用程序中有一个 goroutine 漏洞:这些 goroutines 结束之前正在等待一个 channel 关闭,而这个 channel 永远不会关闭(一个常见的死锁问题)。这个进程毫无任何理由吃掉了 90 % 的 CPU ,而检查 expvars 显示有 600 k 空闲的 goroutines! 我猜测 goroutine 调度程序占用了 CPU。

当然,像 Akka 这样的 Actor 系统可以轻松 处理数百万的 Actors,部分原因是 actors 没有堆栈,但是他们远没有像 goroutines 那样简单地编写大量并发的请求/响应应用程序(即 http APIs)。

channel 是 goroutines 的通信方式:它们提供了一个便利的编程模型,可以在 goroutines 之间发送和接收数据,而不必依赖脆弱的低级别同步基本体。channels 有它们自己的一套 用法 模式。

但是,channels 必须仔细考虑,因为错误大小的 channels (默认情况下没有缓冲) 会导致死锁。下面我们还将看到,使用通道并不能阻止竞争情况,因为它缺乏不可变性。

丰富的标准库

Go 的 标准库 非常丰富,特别是对于所有与网络协议或 API 开发相关的: http 客户端和服务器,加密,档案格式,压缩,发送电子邮件等等。甚至还有一个html解析器和相当强大的模板引擎去生成 text & html,它会自动过滤 XSS 攻击(例如在 Hugo 中的使用)。

各种 APIs 一般都简单易懂。它们有时看起来过于简单:这个某种程度上是因为 goroutine 编程模型意味着我们只需要关心“看似同步”的操作。这也是因为一些通用的函数也可以替换许多专门的函数,就像 我最近发现的关于时间计算的问题。

Go 性能优越

Go 编译为本地可执行文件。许多 Go 的用户来自 Python、Ruby 或 Node.js。对他们来说,这是一种令人兴奋的体验,因为他们看到服务器可以处理的并发请求数量大幅增加。当您使用非并发(Node.js)或全局解释器锁定的解释型语言时,这实际上是相当正常的。结合语言的简易性,这解释了 Go 令人兴奋的原因。

然而与 Java 相比,在 原始性能基准测试 中,情况并不是那么清晰。Go 打败 Java 地方是内存使用和垃圾回收。

Go 的垃圾回收器的设计目的是 优先考虑延迟,并避免停机,这在服务器中尤其重要。这可能会带来更高的 CPU 成本,但是在水平可伸缩的体系结构中,这很容易通过添加更多的机器来解决。请记住,Go 是由谷歌设计的,他们从不会在资源上面短缺。

与 Java 相比,Go 的垃圾回收器(GC)需要做的更少:切片是一个连续的数组结构,而不是像 Java 那样的指针数组。类似地,Go maps 也使用小数组作为 buckets,以实现相同的目的。这意味着垃圾回收器的工作量减少,并且 CPU 缓存本地化也更好。

Go 同样在命令行实用程序中优于 Java :作为本地可执行文件,Go 程序没有启动消耗,反之 Java 首先需要加载和编译的字节码。

语言层面定义源代码的格式化

我职业生涯中一些最激烈的辩论发生在团队代码格式的定义上。 Go 通过为代码定义规范格式来解决这个问题。 gofmt 工具会重新格式化您的代码,并且没有选项。

不管你喜欢与否,gofmt 定义了如何对代码进行格式化,一次性解决了这个问题。

标准化的测试框架

Go 在其标准库中提供了一个很好的 测试框架。它支持并行测试、基准测试,并包含许多实用程序,可以轻松测试网络客户端和服务器。

Go 程序方便操作

与 Python,Ruby 或 Node.js 相比,必须安装单个可执行文件对于运维工程师来说是一个梦想。 随着越来越多的 Docker 的使用,这个问题越来越少,但独立的可执行文件也意味着小型的 Docker 镜像。

Go还具有一些内置的观察性功能,可以使用 expvar 包发布内部状态和指标,并易于添加新内容。但要小心,因为它们在默认的 http 请求处理程序中 自动公开,不受保护。Java 有类似的 JMX ,但它要复杂得多。

Defer 声明,防止忘记清理

defer 语句的目的类似于 Java 的 finally:在当前函数的末尾执行一些清理代码,而不管此函数如何退出。defer 的有趣之处在于它跟代码块没有联系,可以随时出现。这使得清理代码尽可能接近需要清理的代码:

file, err := os.Open(fileName)
if err != nil {
    return
}
defer file.Close()

// 用文件资源的时候,我们再也不需要考虑何时关闭它

当然,Java的 试用资源 没那么冗长,而且 Rust 在其所有者被删除时会 自动声明资源,但是由于 Go 要求您清楚地了解资源清理情况,因此让它接近资源分配很不错。

新类型

我喜欢类型,因为有些事情让我感到恼火和害怕,举个例子,我们到处把持久对象标识符当做 string 或 long 类型传递使用。 我们通常会在参数名称中对 id 的类型进行编码,但是当函数具有多个标识符作为参数并且某些调用不匹配参数顺序时,会造成细微的错误。

Go 对新类型有一等支持,即类型为现有类型并赋予其独立身份,与原有类型不同。 与包装相反,新类型没有运行时间开销。 这允许编译器捕捉这种错误:

type UserId string // <-- new type
type ProductId string

func AddProduct(userId UserId, productId ProductId) {}

func main() {
    userId := UserId("some-user-id")
    productId := ProductId("some-product-id")

    // 正确的顺序: 没有问题
    AddProduct(userId, productId)

    // 错误的顺序:将会编译错误 
    AddProduct(productId, userId)
    // 编译错误:
    // AddProduct 不能用 productId(type ProductId) 作为 type UserId的参数
    // Addproduct 不能用 userId(type UserId) 作为type ProfuctId 的参数 
}

不幸的是,缺乏泛型使得使用新类型变得麻烦,因为为它们编写可重用代码需要从原始类型转换值。

更多相关技术内容咨询欢迎前往并持续关注六星社区了解详情。

如果你想用Python开辟副业赚钱,但不熟悉爬虫与反爬虫技术,没有接单途径,也缺乏兼职经验
关注下方微信公众号:Python编程学习圈,获取价值999元全套Python入门到进阶的学习资料以及教程,还有Python技术交流群一起交流学习哦。

attachments-2022-06-Gi9gO3iZ62b12e341cb8c.jpeg

  • 发表于 2021-11-18 10:46
  • 阅读 ( 366 )
  • 分类:Golang

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
轩辕小不懂
轩辕小不懂

2403 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 1470 文章
  3. Pack 1135 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章