Olivine:以 Go 建構與 Redis 相容的服務 · 章節目錄

第 1 章

Day 1:專案初始化

建立 Go module

Go module 是 Go 的依賴管理機制,也是 Go 1.13 之後的內建功能。

Go 團隊提供了教學:Tutorial: Create a Go module。

在專案根目錄執行 go mod init olivine,會自動產生 go.mod。

注意:許多專案以 github.com/{username}/{repo_name} 作為 module 名稱,這適合大部分專案,也讓其他人更容易找到 GitHub repository。

main 函式

main 是代表程式進入點的特殊函式。我建立了 cmd/olivine/main.go。

package main

import "fmt"

func main() {
	fmt.Println("Hello World")
}

直接執行 go run ./cmd/olivine,就會在 stdout 看到「Hello World」。

$ go run ./cmd/olivine
Hello World

提示:只要使用 package main,就能將 main 放在 module 下的任何目錄,包含專案根目錄。我偏好將執行檔原始碼放在 cmd/{binary_name},透過目錄結構管理專案。

Makefile

make 是軟體開發的建置工具,可以透過 Makefile 定義指令,協助開發者設定、編譯與執行其他工作。

先在專案根目錄建立 Makefile:

BIN_DIR := bin
CMD_DIR := cmd

build:
	@echo "Building binaries..."
	@mkdir -p $(BIN_DIR)
	go build -o $(BIN_DIR)/olivine ./$(CMD_DIR)/olivine

clean:
	@echo "Cleaning..."
	@rm -rf $(BIN_DIR)

這裡定義兩個指令:

  • build:若執行檔目錄不存在就建立它,再將 Go 程式編譯成執行檔。
  • clean:清除執行檔目錄。

接著執行 make build:

$ make build
$ ./bin/olivine # run the olivine binary
Hello World

注意:別忘了將 bin/ 加入 .gitignore。

TCP Server

開始建立 TCP server!可以參考 Go by Example: TCP Server。

package main

import (
	"bufio"
	"log/slog"
	"net"
	"strings"
)

func main() {
	slog.Info("Listen :16879 for TCP connections...")
	listener, err := net.Listen("tcp", ":16879")
	if err != nil {
		slog.Error("failed to listen tcp:", slog.Any("error", err))
		return
	}
	defer listener.Close()

	for {
		connection, err := listener.Accept()
		if err != nil {
			slog.Error("failed to accept connection:", slog.Any("error", err))
			continue
		}
		go handleConnection(connection)
	}
}

func handleConnection(conn net.Conn) {
	reader := bufio.NewReader(conn)
	message, err := reader.ReadString('\n')
	if err != nil {
		slog.Error("failed to read from conn:", slog.Any("error", err))
		return
	}

	ackmsg := strings.ToUpper(strings.ToUpper(message))
	response := "ACK: " + ackmsg
	if _, err := conn.Write([]byte(response)); err != nil {
		slog.Error("failed to write to conn:", slog.Any("error", err))
		return
	}
}

使用 nc 測試:

$ echo "hello" | nc 127.0.0.1 16879
> ACK: HELLO
$ echo "hello from netcat" | nc 127.0.0.1 16879
> ACK: HELLO FROM NETCAT

恭喜,這就是以 Go 撰寫的第一個 TCP server。