iT邦幫忙

2026 iThome 鐵人賽

DAY 4
0

與我們常見的 HTTP Server 不同,Redis 採用 TCP Server 實作,並且在這之上設計了自己的通訊協定 Redis serialization protocol,簡稱 RESP

對於 RESP 的細節在之後的文章會再探討,讓我們先從 TCP Server 開始

Go 標準庫:net 套件

在不同的平台或作業系統上進行網路通訊程式設計簡直就是災難,這是因為不同的作業系統對網路通訊有著風格各異的 syscall:Linux 上的 epoll、BSD 上的 kqueue,又或是 Windows 上的 IOCP 等。

所以 Go 的標準庫已經幫我們封裝好這些作業系統實作,利用 net 這個 go package 就可以輕鬆地在各平台上實作 TCP Server,而 Go by Example: TCP Server 就給出了一個很適合作為起點的範例。

以下是我基於 Go by Example: TCP Server 修改後的 TCP Server:

package main

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

func main() {
	slog.Info("Listen :16379 for TCP connections...")
	listener, err := net.Listen("tcp", ":16379")
	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) {
	defer conn.Close()

	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
	}
}
  • 將 port 改為 16379,與 Redis 的 6379 作為對應
  • 將原本 log.* 改為 slog.*,這是一個由 Go 1.21 之後開始提供的結構化日誌套件,可以參閱 Structured Logging with slog 這篇文章

註:有的時候我會習慣將 slog.Any("error", err) 封裝進一個小套件中,這樣就可以在 slog.Error("something wrong:", liblogs.ErrAttr(err)) 直接調用


上一篇
[Day 03] 專案初始化
下一篇
[Day 05] App:應用程式基礎建設
系列文
從零開始打造 Redis:以 Go 建立 Production Ready 應用程式5
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言