半年前,我决定学习Go语言。

在此之前,我主要用Java和Python开发。Go语言这几年越来越火,很多互联网公司都在用,尤其是云原生、微服务、区块链这些领域。我想着多学一门语言,拓宽一下技术栈,也为以后的职业发展做准备。

这半年来,我从零基础到能独立用Go开发项目,走了不少弯路,也积累了一些经验。这篇文章,我想分享一下我的Go语言学习路线,希望能帮到正在学Go或者准备学Go的朋友。

为什么学Go

在说学习路线之前,先说说我为什么选择学Go。

第一个原因是,Go的应用场景越来越广。Docker、Kubernetes、Terraform、Prometheus这些云原生工具都是用Go写的。现在做后端开发、云原生开发,不会Go都不好意思说自己是做后端的。

第二个原因是,Go的学习曲线相对平缓。Go的语法很简单,关键字很少,没有那么多花里胡哨的特性。有其他语言基础的人,花一两周就能上手。

第三个原因是,Go的性能很好。Go是编译型语言,性能接近C/C++,但开发效率又很高,兼顾了性能和开发效率。对于高并发的后端服务来说,Go是一个很好的选择。

第四个原因是,Go的就业前景不错。现在很多公司都在招Go开发工程师,薪资也不低。多掌握一门热门语言,对职业发展有好处。

当然,Go也不是万能的。它不适合做GUI应用、不适合做数据分析、不适合做前端。但在后端服务、云原生、命令行工具这些领域,Go是非常强的。

第一阶段:基础语法(1-2周)

学习Go的第一步,是掌握基础语法。

我是从官方教程开始的。Go的官方教程(A Tour of Go)做得非常好,交互式的,边学边练。我花了大概一周的时间,把官方教程过了一遍,对Go的基本语法有了一个整体的了解。

基础语法主要包括这些内容:

  • 变量和常量:Go的变量声明和其他语言不太一样,用var关键字或者:=短声明。常量用const。
  • 基本类型:整数、浮点数、布尔值、字符串。Go是强类型语言,类型转换要显式声明。
  • 控制流:if、for、switch。Go没有while循环,用for代替。switch不需要break,默认不穿透。
  • 函数:Go的函数可以返回多个值,这是Go的一大特色。函数也是一等公民,可以作为参数传递和返回值。
  • 数组和切片:数组是固定长度的,切片是动态长度的。切片是Go中最常用的数据结构之一,要重点掌握。
  • 映射(map):键值对集合,类似其他语言的字典。
  • 结构体和方法:Go没有类,用结构体和方法来实现面向对象。
  • 接口:Go的接口是隐式实现的,不需要显式声明implements。这是Go的一大特色,要理解好。
  • 错误处理:Go没有异常,用返回error的方式处理错误。if err != nil是Go的标志性写法。
  • 指针:Go有指针,但没有指针运算。主要用于传引用和修改原值。
  • defer:延迟执行,常用于资源释放。
  • goroutine和channel:Go的并发编程基础,这是Go的核心特性,要重点理解。

学基础语法的时候,我建议不要只看不练。每学一个知识点,就写几个小例子练一练。比如学了切片,就写一个小程序实现切片的增删改查;学了goroutine,就写一个简单的并发程序。

我当时犯了一个错误,就是看教程看得太快,以为自己懂了,但一写代码就懵。后来我放慢速度,每个知识点都动手写代码,效果好了很多。

第二阶段:进阶特性(2-3周)

掌握了基础语法之后,就可以学习一些进阶特性了。

这个阶段我主要学了这些内容:

  • 包管理:Go 1.11之后用Go Modules管理依赖。要学会go mod init、go get、go mod tidy等命令。
  • 并发编程:深入学习goroutine、channel、select、sync包(WaitGroup、Mutex、Once、Pool等)。并发是Go的核心,要花时间深入理解。
  • 上下文(context):context包用于传递请求范围的截止时间、取消信号和其他值。在Go的后端开发中,context无处不在,必须掌握。
  • 测试:Go内置了测试框架,用testing包。要学会写单元测试、表驱动测试、基准测试。
  • 反射:reflect包,用于在运行时检查类型和值。反射用得不多,但要了解基本用法。
  • 泛型:Go 1.18引入了泛型,Go 1.22之后泛型已经比较成熟了。要学会写泛型函数和泛型类型。
  • 标准库:Go的标准库非常强大,要熟悉常用的包,比如fmt、os、io、net/http、encoding/json、time、strings、strconv等。

这个阶段,我开始读一些Go的经典书籍。我读了《Go程序设计语言》(也就是Go圣经),这本书写得非常好,深入浅出,把Go的特性和设计哲学讲得很透彻。我还读了《Go语言实战》,这本书更偏实战,有很多实际的例子。

除了读书,我还看了一些优质的博客和视频。Go的官方博客、Dave Cheney的博客、Go Time播客,都是很好的学习资源。

这个阶段我还做了一个小项目:用Go写了一个命令行工具,实现了文件批量重命名的功能。虽然功能简单,但用到了文件操作、flag解析、并发处理等知识点,对巩固基础很有帮助。

第三阶段:Web开发(3-4周)

学完了语言基础,就可以开始做Web开发了。Go的Web开发非常方便,标准库的net/http包就足够强大。

这个阶段我主要学了这些内容:

  • net/http包:Go的标准HTTP库。要学会创建HTTP服务器、处理请求、返回响应、路由、中间件。
  • 路由:标准库的路由功能比较弱,一般用第三方路由库,比如gin、echo、chi。我选了gin,因为它最流行,资料最多。
  • 数据库操作:Go的数据库操作用database/sql包,配合数据库驱动。我用的是MySQL,驱动是go-sql-driver/mysql。还学了ORM库gorm,用起来更方便。
  • JSON处理:encoding/json包,用于序列化和反序列化JSON。
  • 中间件:日志、认证、CORS、限流等中间件的实现和使用。
  • RESTful API设计:学习RESTful风格的API设计,包括URL设计、HTTP方法、状态码、错误处理等。
  • 配置管理:用viper或者env来管理配置。
  • 日志:用zap或者logrus做结构化日志。

这个阶段,我做了一个完整的Web项目:一个博客系统的后端API。实现了用户注册登录、文章增删改查、评论、标签、分页等功能。用了gin + gorm + MySQL + jwt认证。

做这个项目的过程中,我遇到了很多问题,比如数据库连接池配置、并发安全、错误处理、参数校验等。通过解决这些问题,我对Go的Web开发有了更深入的理解。

我还学习了Go的项目结构。Go的项目结构和Java不太一样,没有那么多层级。比较流行的是按功能分层,或者用标准的Go项目布局(golang-standards/project-layout)。我建议不要过度设计,简单清晰就好。

第四阶段:微服务和云原生(4-6周)

Go在微服务和云原生领域是绝对的主流,学Go不能不学这些。

这个阶段我主要学了这些内容:

  • gRPC:Google开源的高性能RPC框架。Go对gRPC的支持非常好。要学会写proto文件、生成代码、实现服务端和客户端。
  • 服务发现:用etcd或者consul做服务注册和发现。
  • 配置中心:用nacos或者etcd做配置中心。
  • 消息队列:用RabbitMQ或者Kafka做异步消息处理。Go的客户端库都很成熟。
  • 容器化:用Docker打包Go应用。Go的静态编译特性让Docker镜像非常小,几MB就够了。
  • Kubernetes:学习K8s的基本概念和操作,学会把Go应用部署到K8s上。
  • 可观测性:日志、指标、链路追踪。用Prometheus做指标采集,Grafana做可视化,Jaeger做链路追踪。
  • 网关:用Kong或者APISIX做API网关。

这个阶段,我把之前的博客项目拆成了微服务:用户服务、文章服务、评论服务。用gRPC做服务间通信,用etcd做服务发现,用Docker打包,部署到本地的K8s集群(minikube)上。

做这个项目的过程中,我对微服务架构有了更深入的理解。也体会到了Go在云原生领域的优势:编译快、镜像小、启动快、内存占用低、并发性能好。

这个阶段我还读了《微服务设计》这本书,虽然不是Go-specific的,但对理解微服务架构很有帮助。

第五阶段:深入和实战(持续)

学完了上面的内容,就算是入门了。但Go的深入学习是一个持续的过程。

这个阶段我主要在做这些事情:

  • 读源码:读Go标准库的源码,读优秀的开源项目的源码。比如读gin、gorm、etcd、k8s的源码,学习它们的设计思想和实现技巧。
  • 性能优化:学习Go的性能分析工具pprof,学会分析和优化Go程序的性能。包括CPU分析、内存分析、阻塞分析、锁分析等。
  • 并发模式:学习Go的并发编程模式,比如worker pool、fan-out/fan-in、pipeline、rate limiting等。
  • 设计模式:Go的设计模式和其他语言不太一样,要学习Go风格的设计模式。
  • 参与开源:给开源项目提PR,或者自己做一个开源项目。这是提升技术最好的方式。
  • 关注社区:关注Go的最新动态,Go每个半年发布一个大版本,每个版本都有新特性。比如Go 1.22增强了for循环的变量作用域,Go 1.23引入了range over function。要保持学习,跟上版本更新。

我的学习方法和建议

总结一下我的学习方法和建议。

第一,动手实践。学编程最重要的就是动手。不要只看教程和书,一定要写代码。每个知识点都写小例子练习,每个阶段都做一个项目。光看不练,等于白学。

第二,从小项目开始。不要一开始就想做一个大而全的项目,容易半途而废。先做小项目,比如命令行工具、简单的API,然后逐渐增加复杂度。

第三,读官方文档。Go的官方文档写得非常好,是最好的学习资源。遇到问题先查官方文档,比在网上搜乱七八糟的博客靠谱得多。

第四,善用标准库。Go的标准库非常强大,很多功能不需要第三方库就能实现。先了解标准库能做什么,再考虑用第三方库。不要一上来就引入一堆依赖。

第五,理解Go的设计哲学。Go的设计哲学是简单、显式、组合优于继承。理解了这些设计哲学,就能写出更地道的Go代码,而不是用其他语言的思维来写Go。

第六,不要害怕犯错。学习过程中犯错是正常的,每一个bug都是学习的机会。遇到问题先自己想办法解决,实在解决不了再问别人。解决问题的过程,就是成长的过程。

第七,加入社区。加入Go的学习群、论坛、社区,和其他学习者交流。遇到问题可以问,也可以帮助别人。教是最好的学。

常见的坑

最后说一下我踩过的坑,希望大家能避开。

第一个坑是,for循环变量捕获。Go 1.22之前,for循环的变量是共享的,在goroutine中使用会有问题。Go 1.22修复了这个问题,但如果用旧版本要注意。

第二个坑是,nil指针和nil接口。Go的接口类型,即使底层值是nil,接口本身也不是nil。这个特性经常导致bug,要特别注意。

第三个坑是,切片的扩容和共享底层数组。切片append的时候可能会扩容,也可能不会。多个切片共享底层数组的时候,修改一个会影响另一个。要理解切片的内部实现。

第四个坑是,goroutine泄漏。goroutine如果没有正确退出,会一直占用资源。要注意用context或者channel来控制goroutine的生命周期。

第五个坑是,错误处理被忽略。Go的错误是返回值,很容易被忽略。一定要检查每一个error,不要用_忽略重要的错误。

第六个坑是,过度设计。Go的风格是简单,不要把Java那套复杂的设计模式搬过来。能用简单的方式解决,就不要用复杂的方式。

写在最后

Go是一门很优秀的语言,简单、高效、实用。学习Go的过程,不仅是学一门新语言,也是学习一种新的编程思维。

我的学习路线不一定适合所有人,每个人的基础和学习习惯都不一样。但核心的思路是一样的:从基础到进阶,从语法到实战,循序渐进,动手实践。

如果你正在学Go,或者准备学Go,希望这篇文章能给你一些参考。学习是一个持续的过程,不要着急,一步一步来,只要坚持,就一定能学会。

最后,祝大家学习顺利,早日成为Go语言高手。