Go (Golang)

技术栈
后端框架
gogolang后端并发微服务高性能

概览

Go (Golang) 技术栈概览

Go 是由 Google 的 Robert Griesemer、Rob Pike 和 Ken Thompson 于 2009 年发布的编译型、并发优先的系统编程语言。它融合了 C 语言的性能与 Python 的开发效率,被称为「21 世纪的 C 语言」。

解决什么问题

  • 高并发服务端:goroutine + channel 原生并发模型,轻松处理百万级并发
  • 编译部署简单:静态编译为单一二进制文件,无依赖地狱,Docker 镜像仅需 scratch
  • 微服务首选:极快的启动速度、极低的内存占用,Kubernetes/Docker 等基础设施皆由 Go 编写
  • 毕设利器:简洁语法快速上手,Gin/Echo/Fiber 等框架一两天即可搭建 API 服务

关键特性

  • goroutine:轻量级协程,栈仅 2KB,可同时运行数百万个
  • channel:CSP 并发模型,不用锁就能安全传递数据
  • 静态类型 + 垃圾回收,兼顾安全与生产力
  • 内置 gofmt、go test、go mod,工具链完善
  • 编译速度极快,适合大型工程迭代

安装

环境准备

  • 操作系统:Windows 10+ / macOS 12+ / Ubuntu 20.04+
  • 运行时版本:Go 1.22+(推荐最新 stable 1.23)
  • 依赖项:git(用于 go mod 拉取依赖)

安装命令

Windows

  1. 访问 https://go.dev/dl/ 下载 .msi 安装包
  2. 双击安装,默认路径 C:\Go
  3. 验证:
go version   # go version go1.23.x windows/amd64

macOS

# Homebrew 安装(推荐)
brew install go
go version

# 或下载官方 pkg 安装包

Linux (Ubuntu)

# 官方 tarball 安装(推荐,版本最新)
wget https://go.dev/dl/go1.23.4.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.23.4.linux-amd64.tar.gz
# 加入环境变量
echo 'export PATH=$PATH:/usr/local/go/bin' >;> ~/.bashrc
echo 'export GOPATH=$HOME/go' >;> ~/.bashrc
source ~/.bashrc
go version

国内代理(七牛/GOPROXY)

go env -w GOPROXY=https://goproxy.cn,direct
go env -w GO111MODULE=on

常见安装问题

问题 解决方案
go: command not found 确认 /usr/local/go/binC:\Go\bin 在 PATH 中
go get 拉取超时 设置国内代理:go env -w GOPROXY=https://goproxy.cn,direct
GOPATH 相关问题 Go 1.16+ 默认使用 Module 模式,go mod init 即可,GOPATH 不再是强依赖
编译出 exe 无法双击运行 Go 默认编译为 CLI 程序,Windows GUI 需加 -ldflags -H=windowsgui

示例

Go 并发爬虫 — goroutine + channel 实战

目标

演示 Go 核心卖点:goroutine 轻量并发 + channel 通信。编写一个并发检查多个 URL 响应状态的工具。

完整代码

package main

import (
	"fmt"
	"net/http"
	"sync"
	"time"
)

// URLResult 封装单个 URL 检测结果
type URLResult struct {
	URL        string
	StatusCode int
	Duration   time.Duration
	Error      error
}

// checkURL 检测单个 URL,结果写入 channel
func checkURL(url string, resultCh chan<- URLResult) {
	start := time.Now()
	resp, err := http.Get(url)
	duration := time.Since(start)

	if err != nil {
		resultCh <- URLResult{URL: url, Duration: duration, Error: err}
		return
	}
	defer resp.Body.Close()
	resultCh <- URLResult{URL: url, StatusCode: resp.StatusCode, Duration: duration}
}

func main() {
	urls := []string{
		"https://www.baidu.com",
		"https://www.github.com",
		"https://www.google.com",
		"https://httpbin.org/delay/2",
		"https://www.zhihu.com",
	}

	resultCh := make(chan URLResult, len(urls))

	// 🔥 启动 goroutine:每个 URL 一个轻量级线程
	var wg sync.WaitGroup
	for _, url := range urls {
		wg.Add(1)
		go func(u string) {
			defer wg.Done()
			checkURL(u, resultCh)
		}(url)
	}

	// 等待所有 goroutine 完成后关闭 channel
	go func() {
		wg.Wait()
		close(resultCh)
	}()

	// 从 channel 读取结果
	fmt.Println("🌐 URL 健康检查结果:")
	fmt.Println("-----------------------------")
	successCount, failCount := 0, 0
	for result := range resultCh {
		if result.Error != nil {
			fmt.Printf("❌ %-35s | 错误: %v\n", result.URL, result.Error)
			failCount++
		} else {
			fmt.Printf("✅ %-35s | %3d | %v\n", result.URL, result.StatusCode, result.Duration)
			successCount++
		}
	}
	fmt.Println("-----------------------------")
	fmt.Printf("成功: %d  失败: %d  总计: %d\n", successCount, failCount, len(urls))
}

运行步骤

go mod init healthcheck
go run main.go

预期输出

🌐 URL 健康检查结果:
-----------------------------
✅ https://www.baidu.com               | 200 | 156ms
✅ https://www.github.com              | 200 | 890ms
❌ https://www.google.com              | 错误: context deadline exceeded
✅ https://httpbin.org/delay/2         | 200 | 2.1s
✅ https://www.zhihu.com               | 200 | 420ms
-----------------------------
成功: 4  失败: 1  总计: 5

关键知识点

  • go func() 创建 goroutine,开销仅 2KB 栈
  • chan 安全地在 goroutine 间传递数据
  • sync.WaitGroup 等待所有 goroutine 完成
  • 所有 URL 并发检测,总耗时约等于最慢的单个请求

Go JWT 认证中间件

目标

golang-jwt/jwt 实现 Token 签发和验证,作为 Gin 中间件保护私有 API。

完整代码

package main

import (
	"errors"
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v5"
)

var jwtSecret = []byte("your-secret-key-change-in-production")

type Claims struct {
	UserID   int    `json:"userId"`
	Username string `json:"username"`
	Role     string `json:"role"`
	jwt.RegisteredClaims
}

// GenerateToken 签发 Token
func GenerateToken(userID int, username, role string) (string, error) {
	claims := Claims{
		UserID:   userID,
		Username: username,
		Role:     role,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(7 * 24 * time.Hour)),
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			Issuer:    "my-bishe",
		},
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return token.SignedString(jwtSecret)
}

// ParseToken 解析并验证 Token
func ParseToken(tokenStr string) (*Claims, error) {
	token, err := jwt.ParseWithClaims(tokenStr, &Claims{}, func(t *jwt.Token) (any, error) {
		if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, errors.New("无效签名算法")
		}
		return jwtSecret, nil
	})
	if err != nil {
		return nil, err
	}
	claims, ok := token.Claims.(*Claims)
	if !ok || !token.Valid {
		return nil, errors.New("无效 Token")
	}
	return claims, nil
}

// AuthMiddleware JWT 认证中间件
func AuthMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		tokenStr := c.GetHeader("Authorization")
		if len(tokenStr) < 8 || tokenStr[:7] != "Bearer " {
			c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "未提供 Token"})
			return
		}
		claims, err := ParseToken(tokenStr[7:])
		if err != nil {
			c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Token 无效或已过期"})
			return
		}
		c.Set("userId", claims.UserID)
		c.Set("username", claims.Username)
		c.Set("role", claims.Role)
		c.Next()
	}
}

// RoleMiddleware 角色权限中间件(工厂函数)
func RoleMiddleware(roles ...string) gin.HandlerFunc {
	return func(c *gin.Context) {
		role, _ := c.Get("role")
		for _, r := range roles {
			if role == r {
				c.Next()
				return
			}
		}
		c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "无权访问"})
	}
}

func main() {
	r := gin.Default()

	// 公开接口:登录获取 Token
	r.POST("/login", func(c *gin.Context) {
		var req struct {
			Username string `json:"username"`
			Password string `json:"password"`
		}
		c.ShouldBindJSON(&req)

		// 模拟验证(实际应查数据库)
		if req.Username == "admin" && req.Password == "123456" {
			token, _ := GenerateToken(1, req.Username, "admin")
			c.JSON(200, gin.H{"token": token, "expiresIn": "7天"})
		} else {
			c.JSON(401, gin.H{"error": "账号或密码错误"})
		}
	})

	// 受保护路由组
	auth := r.Group("/api").Use(AuthMiddleware())
	{
		auth.GET("/profile", func(c *gin.Context) {
			c.JSON(200, gin.H{
				"userId":   c.GetInt("userId"),
				"username": c.GetString("username"),
				"role":     c.GetString("role"),
			})
		})

		// 仅管理员可访问
		admin := auth.Group("/admin").Use(RoleMiddleware("admin"))
		{
			admin.GET("/dashboard", func(c *gin.Context) {
				c.JSON(200, gin.H{"message": "管理员面板"})
			})
		}
	}

	r.Run(":8080")
}

测试

# 登录获取 Token
curl -X POST http://localhost:8080/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"123456"}'

# 使用 Token 访问受保护接口
curl http://localhost:8080/api/profile \
  -H "Authorization: Bearer <TOKEN>"

# 非管理员访问 admin 接口 → 403

关键要点

  • HS256 对称加密,简单够用;生产环境用 RS256(非对称)
  • Token 存在客户端,服务端不存储状态
  • 过期时间设置 7 天,可选实现 Refresh Token 续期

教程

Go 语言毕设入门教程

前言

Go 语言在国内互联网大厂(字节跳动、腾讯、B站)广泛使用,毕选用 Go 写后端能体现扎实的工程能力。本教程带你快速掌握 Go 的核心概念。

第一章:Go 的哲学 — 少即是多

Go 的设计哲学是 简洁实用

  • 没有类继承 → 用组合 + interface
  • 没有泛型(Go 1.18 以前)→ 简洁但有局限
  • 没有异常 → 用 error 返回值
  • 只有 25 个关键字
// ❌ Java 风格
try {
    result = doSomething();
} catch (Exception e) {
    log.error(e);
}

// ✅ Go 风格 — 显式处理每个错误
result, err := doSomething()
if err != nil {
    log.Printf("doSomething failed: %v", err)
    return err
}

第二章:指针与内存

Go 有指针但没有指针运算,安全且高效:

// 值传递 — 拷贝整个结构体
func updateBad(u User) {
    u.Name = "new"  // 不影响外部
}

// 指针传递 — 零拷贝
func updateGood(u *User) {
    u.Name = "new"  // 修改原值
}

// 常见模式:构造函数返回指针
func NewUser(name string) *User {
    return &User{Name: name, CreatedAt: time.Now()}
}

毕设实践:DTO 传值,模型传指针,大切片传指针。

第三章:并发才是杀手锏

// 经典模式:Worker Pool
func workerPool(jobs <-chan Job, results chan<- Result, numWorkers int) {
    var wg sync.WaitGroup
    for i := 0; i < numWorkers; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            for job := range jobs {
                results <- process(job)
            }
        }()
    }
    wg.Wait()
    close(results)
}

毕设场景

  • 批量数据处理(论文数据分析)
  • 并发爬虫(数据采集模块)
  • 实时推送(WebSocket 服务)

第四章:结构体标签与验证

type Student struct {
    ID     int    `json:"id" gorm:"primaryKey"`
    Name   string `json:"name" binding:"required" gorm:"size:50"`
    Email  string `json:"email" binding:"required,email" gorm:"uniqueIndex"`
    Major  string `json:"major"`
}

结构体标签是 Go 的元编程方式,一个结构体可以通过标签同时描述 JSON 序列化、字段验证、数据库映射。

第五章:项目结构

go-project/
├── cmd/
│   └── server/
│       └── main.go        # 入口
├── internal/
│   ├── handler/           # HTTP 处理器(Controller)
│   ├── service/           # 业务逻辑
│   ├── repository/        # 数据访问
│   └── model/             # 数据模型
├── config/
├── go.mod
└── go.sum

internal/ 下的包无法被外部项目引用,这是 Go 的访问控制机制。

思考题

  1. goroutine 和 OS 线程的本质区别是什么?
  2. channel 关闭后还能读取吗?往已关闭的 channel 写数据会怎样?
  3. Go 的 GC 如何做到 STW(Stop The World)低于 1ms?