教程
⚙️

后端开发

Node.js、Python、Go 等服务端开发与 API 设计。

Go 入门:为什么云原生时代都在用 Go

从"Go 为什么火"讲起,装好环境,写出第一个程序,掌握变量、基本类型、常量与流程控制,并理解 Go 极简语法的设计哲学。

· 更新于 2026-07-20 阅读量 --

你可能听说过:Docker 是 Go 写的、Kubernetes 是 Go 写的、Prometheus 也是 Go 写的。一打开云原生的世界,满眼都是 Go。那它到底凭什么这么受欢迎?答案很朴素——Go 把”简单”和”高效”做到了极致:语法少到一只手数得过来,编译出来是一个静态二进制文件,扔到服务器上就能跑,不依赖任何运行时;天生支持并发,写高并发服务几乎不用你操心线程池。

打个比方:别的语言像”带全套厨具的餐厅”,开张前得先布置燃气、水电、洗碗间;Go 像”一辆出厂就装满油的房车”,拧钥匙就走,到哪都能住。对后端工程师来说,最重要的是”写完、编译、部署”这一条龙要顺,Go 刚好满足这一点。

这一篇我们搞定五件事:为什么选 Go、环境怎么装、第一个程序怎么跑、变量与基本类型、以及流程控制。跟着敲,别只看。

为什么是 Go 而不是别的

先说清楚动机,免得你学到一半怀疑人生。

  • 语法极简:没有继承、没有泛型早期版本、没有异常(用返回值代替)、没有类,关键字只有二十多个。学起来快,读别人代码也快。
  • 编译极快、部署极简go build 一下,出来一个单独的二进制文件,丢到任何同架构的 Linux 服务器都能跑,不用装 Python 解释器、不用装 JVM。
  • 原生并发goroutine 让”同时处理上万连接”变得像写普通函数一样简单,这是 Go 在微服务、网关领域封神的根本原因。
  • 标准库强:HTTP 服务器、JSON 解析、加密、网络,标准库全包了,很多小服务零依赖就能上线。

一句话:后端服务追求的是”写得快、跑得稳、部署轻”,Go 在这三点上几乎没有对手。

把环境装起来

官方安装包是最稳的方式,避免以后踩一堆路径坑。

  • 下载:去 go.dev/dl 下载对应系统的安装包(建议 1.21+)。Windows 一路下一步;macOS 用 brew install go 也行;Linux 解压到 /usr/local 即可。
  • 验证:命令行输入 go version,能打印版本号就成功了。
  • 工作区:现代 Go(1.11 之后)用”模块”管理依赖,不再强制 GOPATH。你随便建个文件夹就能开始写项目。
go version
# 输出类似 go version go1.22.0 linux/amd64 就说明装好了

小提示:Go 的代码必须放在一个”模块”里。模块就是一个带 go.mod 文件的目录,后面会讲。前期跟着例子建目录即可。

第一个 Go 程序

新建文件 hello.go,内容:

package main

import "fmt"

func main() {
    fmt.Println("你好,Go!")
}

命令行运行:

go run hello.go

屏幕上出现”你好,Go!“。和 Python 不同,Go 必须显式包一个 package 声明,而且每条语句结尾不用分号(编译器自动加,你写了也不算错,但约定不写)。入口函数固定叫 main,且必须属于 main 包。

几个要点:

  • package main 表示这是可执行程序(不是给别人 import 的库)。
  • import "fmt" 引入格式化输入输出包,Println 是它的函数。
  • func main() 是程序启动后第一个被调用的函数,没有它 go run 会报错。

变量:给数据起个名字

Go 是静态类型语言:变量一旦声明了类型,就不能再装别类型的值。声明有三种姿势:

var name string = "小明"   // 完整写法:类型写清楚
var age = 18               // 类型推断:编译器自己猜是 int
city := "北京"             // 短声明:最常用,:= 同时声明+赋值

几个要点:

  • 函数内部最常用 :=,它让编译器自己推断类型,写起来最省事。
  • 变量名只能是字母、数字、下划线,且不能以数字开头。1name 非法,user_name 合法。
  • Go 区分大小写,Namename 是两个不同的变量;而且首字母大写的标识符(如 Println)才能被其他包访问,小写只在包内可见——这是 Go 的”访问控制”,没有 public/private 关键字。

常见基本类型

后端服务里你天天会碰到这几种:

  • 字符串 string:用双引号包起来。"GET /admin"。字符串不可变,拼接用 +fmt.Sprintf
    method := "GET"
    path := "/admin"
    req := fmt.Sprintf("%s %s", method, path) // 结果:GET /admin
  • 整数int(随系统 32/64 位)、int8/int32/int64uint 等。做加减乘除,注意 5 / 2 结果是 2(整数除法向下取整),想要小数得用 float64
  • 浮点 float64 / float32:小数。
  • 布尔 booltrue / false
  • 切片 []T:Go 里最常用的”动态数组”,比如 []string{"a", "b"}。后面批量存端口、存 URL 全靠它。
  • 映射 map[K]V:键值对,map[string]int{"port": 80},用键取值 m["port"]。请求参数、配置表几乎都用 map。
scores := []int{90, 85, 77}
m := map[string]int{"http": 80, "https": 443}
fmt.Println(scores[0], m["http"])

常量与 iota

不想被改的值用 const

const Pi = 3.14159
const (
    StatusOK       = 200
    StatusNotFound = 404
)

iota 是 Go 特有的”常量计数器”,在 const 块里从 0 自动递增,特别适合定义枚举:

const (
    Red   = iota // 0
    Green        // 1
    Blue         // 2
)

流程控制:让程序会”判断”和”重复”

条件 if(注意条件加括号,且 { 必须和 if 同一行):

status := 200
if status == 200 {
    fmt.Println("请求成功")
} else if status == 404 {
    fmt.Println("页面不存在")
} else {
    fmt.Println("其他状态码")
}

Go 的 if 还支持”初始化语句”,常用于错误处理:

if err := doSomething(); err != nil {
    fmt.Println("出错了:", err)
}

循环 for:Go 只有 for 一种循环,但能玩出 while 的效果:

// 经典三段式
for i := 0; i < 3; i++ {
    fmt.Println(i)
}
// 当型(像 while)
n := 0
for n < 3 {
    n++
}
// 遍历切片
ports := []int{80, 443, 8080}
for idx, p := range ports {
    fmt.Printf("第%d个端口:%d\n", idx, p)
}

break 提前退出,continue 跳过本次——写扫描工具跳过已失败目标时经常用。range 同时给你”下标”和”值”,比手动维护计数器清爽。

常见新手坑

  • 左花括号必须同行if x { 不能写成 if x\n{,否则编译报错。这是 Go 格式化铁律。
  • 声明了不用会报错:Go 不允许”定义了变量却没使用”,编译直接失败。这是它逼你写干净代码的手段。
  • 短声明 := 只能在函数内用:包级别只能用 var
  • 字符串用双引号,字符用单引号'A' 是字符(rune),"A" 才是字符串,混用会报错。
  • nil 不能直接比较:map、切片、指针的”空”用 nil 表示,但 nil0 不是一回事。

实战:批量拼接待测 URL

把上面串起来,给一串路径批量生成完整 URL——这是目录探测、批量请求的雏形:

package main

import "fmt"

func main() {
    base := "http://192.168.1.10"
    paths := []string{"/admin", "/login", "/api", "/config"}
    var urls []string
    for _, p := range paths {
        url := base + p
        urls = append(urls, url)
        fmt.Println("已生成:", url)
    }
    fmt.Printf("共 %d 个目标\n", len(urls))
}

append 往切片里加元素,len(urls) 让你随时知道进度。这段逻辑看着简单,却是几乎所有批量工具的核心循环:遍历列表、拼装请求、收集结果。

新手怎么把基础练熟

到这里你已经认识了变量、类型、常量和流程控制,但”看懂”和”会写”之间还差大量练习。我的建议是别一上来就背语法,而是用”小目标驱动”:每天给自己一个能跑通的小任务,比如”写一个程序,接收两个数字,输出它们的和与积""写一个循环,打印一到一百里所有的偶数”。任务越小越具体,你越容易获得”跑通”的成就感,而成就感正是坚持下去的关键。

另一个容易踩的弯路是”只看不敲”。Go 是门动手的语言,眼睛扫一遍觉得自己懂了,真到键盘上写往往卡在花括号位置、:= 误用、漏 import 这些细节。务必把本文每个例子亲手敲一遍,改个数、换个条件看看结果变不变,这种”试错”比看十遍都管用。

还有一点:遇到编译错误别慌。Go 的报错非常直白,通常会告诉你”哪一行、什么错”。比如 undefined: xxx 基本就是变量名拼错或没 import;declared and not used 就是定义了没用。养成”先读错、再改”的习惯,你的调试速度会肉眼可见地变快。

小测验:看看你掌握了没

  • 问题一:var x = 10x := 10 有什么区别?分别能在哪用?
  • 问题二:Go 里有几种循环?怎么写出”当条件成立就一直跑”的效果?
  • 问题三:为什么 if x > 0 { 不能把 { 换到下一行?

(答案思路:var 包级函数级都能用,:= 只能函数内;只有 for 一种,用 for 条件 {} 当 while;Go 强制左括号同行,否则编译失败。)

这一篇你该记住的

  • 云原生时代 Go 流行,因为它语法极简、编译出单文件、部署轻、原生并发。
  • 装好 Go 并验证 go version;用 go run xxx.go 直接跑。
  • 入口固定 package main + func main();语句结尾不用分号。
  • 变量三种声明:varvar 推断:=(最常用,仅函数内)。
  • 核心类型有 string/int/float64/bool/[]T(切片)/map[K]V(映射)。
  • 流程控制只有 ifforif 条件不加括号、{ 必须同行;range 遍历切片/map。
  • Go 不允许”声明了不用”,这是它逼你写干净代码的机制。

下一篇我们讲函数:多返回值、可变参数、defer 延迟调用,这些是 Go 区别于其他语言最鲜明的语法糖。