Go application, 分布式系统设计

Go应用与分布式系统设计实战

在现代分布式系统架构中,Go语言凭借其出色的并发性能和简洁的语法设计,成为了构建高可用、高性能分布式系统的首选语言之一。本文将深入探讨如何使用Go语言实现分布式系统中的核心组件,包括一致性协议、高并发架构设计和分布式锁机制。

Go fundamentals, how to play?

Go Fundamentals 是更多的Go基础知识,涵盖了数据结构(基础数据结构、数组、slice、map)、语言基础(函数、接口、反射)、关键字和并发等方面。掌握这些概念和技术将帮助您编写高效、可靠和复杂的Go程序。

Go Proverbs, how to play?

需要牢记这些谚语,在使用过程中感受谚语带来的变化

  • Don’t communicate by sharing memory, share memory by communicating.
package main

import "fmt"

func main() {
    channel := make(chan int) // 创建一个通道

    go func() {
        value := 42
        channel <- value // 将值发送到通道
    }()

    receivedValue := <-channel // 从通道接收值
    fmt.Println(receivedValue) // 输出:42
}
  • Concurrency is not parallelism
package main

import (
    "fmt"
    "sync"
    "time"
)

func printNumbers(wg *sync.WaitGroup) {
    defer wg.Done()

    for i := 0; i < 5; i++ {
        fmt.Println(i)
        time.Sleep(100 * time.Millisecond)
    }
}

func printLetters(wg *sync.WaitGroup) {
    defer wg.Done()

    for i := 'a'; i < 'e'; i++ {
        fmt.Printf("%c\n", i)
        time.Sleep(100 * time.Millisecond)
    }
}

func main() {
    var wg sync.WaitGroup

    wg.Add(2)
    go printNumbers(&wg)
    go printLetters(&wg)

    wg.Wait()
}
  • Channels orchestrate; mutexes serialize
package main

import (
    "fmt"
    "sync"
)

func increment(counter *int, wg *sync.WaitGroup) {
    defer wg.Done()

    for i := 0; i < 1000; i++ {
        *counter++
    }
}

func main() {
    var wg sync.WaitGroup
    counter := 0

    wg.Add(2)
    go increment(&counter, &wg)
    go increment(&counter, &wg)

    wg.Wait()

    fmt.Println(counter) // 输出:2000
}
  • The bigger the interface, the weaker the abstraction
package main

import "fmt"

type Animal interface {
    Eat()
    Sleep()
}

type Dog struct{}

func (d Dog) Eat() {
    fmt.Println("Dog is eating")
}

func (d Dog) Sleep() {
    fmt.Println("Dog is sleeping")
}

type Cat struct{}

func (c Cat) Eat() {
    fmt.Println("Cat is eating")
}

func (c Cat) Sleep() {
    fmt.Println("Cat is sleeping")
}

func main() {
    animals := []Animal{Dog{}, Cat{}}

    for _, animal := range animals {
        animal.Eat()
        animal.Sleep()
    }
}
  • Make the zero value useful
package main

import "fmt"

type Person struct {
    Name string
    Age  int
}

func main() {
    var person Person
    fmt.Println(person.Name) // 输出空字符串
    fmt.Println(person.Age)  // 输出0

    person.Name = "Alice"
    person.Age = 30
    fmt.Println(person.Name) // 输出:Alice
    fmt.Println(person.Age)  // 输出:30
}
  • Don’t just check errors, handle them gracefully
package main

import (
    "fmt"
    "os"
)

func main() {
    file, err := os.Open("example.txt")
    if err != nil {
        fmt.Println("Error opening file:", err)
        return
    }
    defer file.Close()

    // 在这里进行文件操作
    fmt.Println("File opened successfully")
}
  • Don’t panic
package main

import "fmt"

func main() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered from panic:", r)
        }
    }()

    panic("Something went wrong") // 引发恐慌
}

docker, net?

docker桥接网络模式下开启端口映射,流量是如何绕过防火墙的

一、iptables

  • 五条链

    • PREROUTING链 PREROUTING链是最先生效的,当数据包到达网口时,即开始工作。同时由于其在raw,mangle,nat表中都存在,其执行的优先l顺序是:raw(PREROUTING)–>man gle(PREROUTING)—->mangle(nat) PREROUTING一般用作对包进行目标地址修改。比如将该包的目标地址,修改为非本机的另外的网络p,一般通过DNAT规则进行修改。
    • 路由决策(Routing Decision) 决定一个包该走哪个链。如果上述PREROUTING链对包进行了目标网络更改。那么决策会觉得这个是一个需要转发的数据包,于是会将该包转发给FORWARD链。 否则,该包会走NPUT链
    • FORWARD链 FORWARD在各表中生效的优先l顺序是:mangle(FORWARD)–>filter(FORWARD) 处理路由决策派发发过来的包,到这里的包一般目标网络地址在PREROUTING链被修改过
    • INPUT链 其生效顺序是:mangle(INPUT)–>filter(INPUT) 处理路由决策派发发过来的包,到这里的包一般目标网络地址在PREROUTING链没有被修改过。
    • OUTPUT链 在目标进程端口接收到输入数据包后,输出的数据包,将在这里进行规则应用。OUTPUT链在各表中生效的先后顺序是: raw(OUTPUT)—->mangle(OUTPUT)—-nat(OUTPUT)—->filter(OUTPUT)
  • 四张表