第一章:Go语言入门及基础概念
Go语言,也叫Golang,是Google开发的一种静态类型、编译型语言。它的设计哲学是简洁、高效和可靠,非常适合用于构建大型系统和服务端应用。在开始深入并发编程和微服务开发之前,我们需要先打好基础。
1.1 环境搭建与第一个程序
安装Go语言环境非常简单,只需要从官网下载对应操作系统的安装包即可。安装完成后,可以通过以下命令验证安装是否成功:
go version
现在我们来写一个最简单的”Hello, World”程序:
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
将代码保存为main.go文件,然后运行:
go run main.go
输出:
Hello, World!
这就是Go程序的起点。你会注意到代码非常简洁,没有复杂的外壳,体现了Go语言”少即是多”的设计理念。
1.2 基本数据类型与变量
Go语言有几种基本数据类型:整数、浮点数、布尔值和字符串。让我们看一些示例:
package main
import (
"fmt"
)
func main() {
// 整数类型
var age int = 25
var population int64 = 8000000000
// 浮点数类型
var price float64 = 19.99
var pi float32 = 3.14
// 布尔类型
var isAdult bool = true
// 字符串类型
var greeting string = "Hello, Go!"
fmt.Printf("年龄:%d\n", age)
fmt.Printf("人口:%d\n", population)
fmt.Printf("价格:%.2f\n", price)
fmt.Printf("圆周率:%.2f\n", pi)
fmt.Printf("是否成年:%v\n", isAdult)
fmt.Printf("问候语:%s\n", greeting)
}
1.3 控制结构
Go语言的控制结构与C语言类似,但更加简洁:
package main
import "fmt"
func main() {
// 条件判断
age := 20
if age >= 18 {
fmt.Println("成年人")
} else {
fmt.Println("未成年人")
}
// 循环
for i := 0; i < 5; i++ {
fmt.Printf("迭代 %d\n", i)
}
// while风格的循环
count := 0
for count < 3 {
fmt.Printf("计数:%d\n", count)
count++
}
// 无限循环
/*
for {
// 需要break退出
}
*/
}
1.4 函数与包
在Go中,函数是一等公民,可以像变量一样传递:
package main
import "fmt"
// 定义一个函数
func add(a, b int) int {
return a + b
}
// 定义多个返回值的函数
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("除数不能为零")
}
return a / b, nil
}
// 可变参数函数
func sum(numbers ...int) int {
total := 0
for _, num := range numbers {
total += num
}
return total
}
func main() {
result := add(3, 5)
fmt.Printf("3 + 5 = %d\n", result)
quotient, err := divide(10, 3)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("10 / 3 = %.2f\n", quotient)
}
total := sum(1, 2, 3, 4, 5)
fmt.Printf("总和:%d\n", total)
}
第二章:Go并发编程核心
Go语言最大的特色之一就是它的并发模型。通过goroutine和channel,我们可以轻松编写高效并发的程序。
2.1 Goroutine:轻量级线程
Goroutine是Go语言并发编程的核心概念,它是一种轻量级的线程,由Go运行时管理,而不是由操作系统管理。创建goroutine非常简单,只需要在函数调用前加上go关键字:
package main
import (
"fmt"
"time"
)
func sayHello(name string) {
for i := 0; i < 5; i++ {
fmt.Printf("Hello, %s! 第%d次\n", name, i+1)
time.Sleep(100 * time.Millisecond)
}
}
func main() {
// 创建两个goroutine
go sayHello("Alice")
go sayHello("Bob")
// 等待goroutine完成(实际项目中应该使用更优雅的方式)
time.Sleep(1 * time.Second)
fmt.Println("主程序结束")
}
你会发现输出是交错的,这表明两个goroutine确实在并发执行。
2.2 Channel:goroutine之间的通信
Channel是goroutine之间传递数据的通道,它使得并发编程更加安全和简洁:
package main
import "fmt"
func worker(id int, jobs <-chan int, results chan<- int) {
for job := range jobs {
fmt.Printf("Worker %d 处理任务 %d\n", id, job)
results <- job * 2
}
}
func main() {
jobs := make(chan int, 100)
results := make(chan int, 100)
// 启动3个工作者goroutine
for w := 1; w <= 3; w++ {
go worker(w, jobs, results)
}
// 发送任务
for j := 1; j <= 9; j++ {
jobs <- j
}
close(jobs)
// 收集结果
for a := 1; a <= 9; a++ {
<-results
}
fmt.Println("所有任务完成")
}
在这个例子中,我们使用了一个缓冲channel来传递工作,避免了goroutine之间的阻塞。
2.3 WaitGroup:同步多个goroutine
当我们需要等待多个goroutine完成时,可以使用sync.WaitGroup:
package main
import (
"fmt"
"sync"
"time"
)
func task(id int, wg *sync.WaitGroup) {
defer wg.Done() // 确保任务完成后减少计数器
fmt.Printf("任务 %d 开始\n", id)
time.Sleep(time.Duration(id) * 100 * time.Millisecond)
fmt.Printf("任务 %d 完成\n", id)
}
func main() {
var wg sync.WaitGroup
// 启动5个任务
for i := 1; i <= 5; i++ {
wg.Add(1) // 增加计数器
go task(i, &wg)
}
// 等待所有任务完成
wg.Wait()
fmt.Println("所有任务已完成")
}
2.4 Mutex与RWMutex:并发安全的数据访问
当多个goroutine需要访问共享数据时,我们需要使用互斥锁来保证数据的一致性:
package main
import (
"fmt"
"sync"
"time"
)
type SafeCounter struct {
mu sync.Mutex
counter int
}
func (c *SafeCounter) Increment() {
c.mu.Lock()
defer c.mu.Unlock()
c.counter++
}
func (c *SafeCounter) Value() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.counter
}
func main() {
counter := &SafeCounter{}
var wg sync.WaitGroup
// 启动10个goroutine并发递增计数器
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := 0; j < 1000; j++ {
counter.Increment()
}
}()
}
wg.Wait()
fmt.Printf("最终计数:%d\n", counter.Value())
}
2.5 Context:控制goroutine生命周期
在微服务开发中,我们需要一种机制来控制goroutine的生命周期,这就是context的作用:
package main
import (
"context"
"fmt"
"time"
)
func worker(ctx context.Context, id int) {
for {
select {
case <-ctx.Done():
fmt.Printf("Worker %d 收到取消信号\n", id)
return
default:
fmt.Printf("Worker %d 执行任务\n", id)
time.Sleep(100 * time.Millisecond)
}
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
defer cancel()
for i := 1; i <= 3; i++ {
go worker(ctx, i)
}
time.Sleep(1 * time.Second)
fmt.Println("主程序结束")
}
2.6 Select:处理多个channel
select语句用于监听多个channel操作,它是实现多路复用的关键:
package main
import (
"fmt"
"time"
)
func main() {
c1 := make(chan string)
c2 := make(chan string)
// 启动两个goroutine,分别向channel发送消息
go func() {
time.Sleep(1 * time.Second)
c1 <- "来自c1的消息"
}()
go func() {
time.Sleep(2 * time.Second)
c2 <- "来自c2的消息"
}()
// 使用select监听多个channel
for i := 0; i < 2; i++ {
select {
case msg1 := <-c1:
fmt.Printf("收到: %s\n", msg1)
case msg2 := <-c2:
fmt.Printf("收到: %s\n", msg2)
case <-time.After(3 * time.Second):
fmt.Println("超时")
}
}
}
2.7 实际案例:并发文件下载器
让我们来看一个更实际的并发编程案例——并发文件下载器:
package main
import (
"fmt"
"io"
"net/http"
"os"
"sync"
"time"
)
type Downloader struct {
urls []string
mu sync.Mutex
success int
failed int
}
func (d *Downloader) download(url string, wg *sync.WaitGroup) {
defer wg.Done()
resp, err := http.Get(url)
if err != nil {
d.mu.Lock()
d.failed++
d.mu.Unlock()
fmt.Printf("下载失败 %s: %v\n", url, err)
return
}
defer resp.Body.Close()
filename := fmt.Sprintf("file_%d.tmp", time.Now().UnixNano())
file, err := os.Create(filename)
if err != nil {
d.mu.Lock()
d.failed++
d.mu.Unlock()
fmt.Printf("创建文件失败 %s: %v\n", url, err)
return
}
defer file.Close()
_, err = io.Copy(file, resp.Body)
if err != nil {
d.mu.Lock()
d.failed++
d.mu.Unlock()
fmt.Printf("写入文件失败 %s: %v\n", url, err)
return
}
d.mu.Lock()
d.success++
d.mu.Unlock()
fmt.Printf("成功下载: %s -> %s\n", url, filename)
}
func main() {
urls := []string{
"https://example.com/file1.zip",
"https://example.com/file2.zip",
"https://example.com/file3.zip",
}
downloader := &Downloader{
urls: urls,
}
var wg sync.WaitGroup
startTime := time.Now()
// 并发下载所有文件
for _, url := range urls {
wg.Add(1)
go downloader.download(url, &wg)
}
wg.Wait()
fmt.Printf("\n下载完成! 成功: %d, 失败: %d, 耗时: %v\n",
downloader.success, downloader.failed, time.Since(startTime))
}
这个案例展示了如何使用goroutine、channel(虽然没有直接使用,但逻辑上是并发的)、WaitGroup和Mutex来构建一个实际的并发应用。
第三章:微服务架构基础
微服务架构是一种将单个应用程序开发为一组小型服务的方法,每个服务都在自己的进程中运行,并与轻量级机制(通常是HTTP资源API)进行通信。
3.1 为什么选择微服务
在开始编码之前,让我们思考一下微服务的优势:
- 独立部署:每个服务可以独立部署,不会相互影响
- 技术多样性:不同服务可以使用不同的技术栈
- 可扩展性:可以单独扩展某个服务
- 容错性:单个服务的故障不会影响整个系统
- 团队自治:不同团队可以负责不同的服务
3.2 微服务通信模式
微服务之间需要通信,主要有两种模式:
同步通信(REST/gRPC)
// 使用标准的http.Client进行REST调用
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
func getUser(userID int) (*User, error) {
resp, err := http.Get(fmt.Sprintf("http://user-service:8080/users/%d", userID))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var user User
err = json.Unmarshal(body, &user)
if err != nil {
return nil, err
}
return &user, nil
}
func main() {
user, err := getUser(1)
if err != nil {
fmt.Printf("获取用户失败: %v\n", err)
return
}
fmt.Printf("用户: %+v\n", user)
}
异步通信(消息队列)
// 使用RabbitMQ进行异步通信
package main
import (
"fmt"
"log"
"github.com/streadway/amqp"
)
const queueName = "order_queue"
func publishMessage(message string) error {
conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
if err != nil {
return err
}
defer conn.Close()
ch, err := conn.Channel()
if err != nil {
return err
}
defer ch.Close()
q, err := ch.QueueDeclare(
queueName, // name
false, // durable
false, // delete when unused
false, // exclusive
false, // no-wait
nil, // arguments
)
if err != nil {
return err
}
err = ch.Publish(
"", // exchange
q.Name, // routing key
false, // mandatory
false, // immediate
amqp.Publishing{
ContentType: "text/plain",
Body: []byte(message),
},
)
return err
}
func consumeMessage() error {
conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
if err != nil {
return err
}
defer conn.Close()
ch, err := conn.Channel()
if err != nil {
return err
}
defer ch.Close()
msgs, err := ch.Consume(
queueName, // queue
"", // consumer
true, // auto-ack
false, // exclusive
false, // no-local
false, // no-wait
nil, // args
)
if err != nil {
return err
}
forever := make(chan bool)
go func() {
for d := range msgs {
log.Printf("收到消息: %s", d.Body)
}
}()
log.Printf("等待消息...")
<-forever
return nil
}
func main() {
// 发布消息
err := publishMessage("新订单: 订单12345")
if err != nil {
log.Printf("发布消息失败: %v", err)
}
// 消费消息
err = consumeMessage()
if err != nil {
log.Printf("消费消息失败: %v", err)
}
}
3.3 服务注册与发现
在微服务架构
