golang轻量级Redis服务器实现插件库rotom的使用

golang轻量级Redis服务器实现插件库rotom的使用

介绍

Rotom是一个用Go编写的高性能、低延迟的微型Redis服务器。它基于I/O多路复用复制了Redis中的核心事件循环机制AeLoop。

特性

  • 使用epoll网络模型实现了Redis中的AeLoop单线程事件循环
  • 出色的优化,达到原生Redis性能
  • 兼容Redis RESP协议,任何Redis客户端都可以连接
  • 实现了String、Hash、Set、List、ZSet数据结构
  • 支持RDB和AOF
  • 支持20多个常用命令

AELoop

AeLoop(Async Event Loop)是Redis中核心的异步事件驱动机制,主要包括:

AeLoop事件循环示意图

  1. FileEvent: 使用I/O多路复用处理网络套接字的读写事件,分为READABLEWRITABLE
  2. TimeEvent: 处理需要延迟或周期性执行的任务,如每100ms过期项
  3. 当事件就绪时,由绑定到这些事件的回调函数处理

在rotom中,复制了Redis的AeLoop事件循环机制,具体流程:

  1. 当新的TCP连接到达时,AcceptHandler获取socket fd并将其添加到事件循环,注册读事件
  2. 当读事件就绪时,ReadQueryFromClient将缓冲数据读取到queryBuf
  3. ProcessQueryBuf解析并执行queryBuf中的对应命令
  4. 命令执行结果被保存,注册socket fd的写事件
  5. 当写事件就绪时,SendReplyToClient将所有结果写回客户端。一个写事件可能一次性返回多个读事件结果
  6. 释放资源,继续该过程直到服务关闭

数据结构

Rotom在数据结构上做了多项优化:

  • dict: 使用stdmap作为db哈希表,内置渐进式rehash
  • hash: 基于zipmap,内存效率更高
  • set: 当集合较小时使用zipset,较大时使用mapset
  • list: 基于listpackquicklist实现双向链表
  • zset: 当较小时使用zipzset,较大时使用hash+skiplist

值得注意的是,zipmapzipset是基于listpack的空间高效数据结构,listpack是Redis提出的新压缩列表,用于替代ziplist,支持正向和反向遍历,解决了ziplist的级联更新问题。

基准测试

性能基准测试结果

测试将在同一台机器上运行rotom,禁用appendonly,并使用redis-benchmark工具测试不同命令的延迟。

使用示例

本地运行

首先克隆项目到本地:

git clone https://github.com/xgzlucario/rotom

确保本地Go环境>=1.22。在项目目录中运行go run .启动服务,默认监听6379端口:

$ go run .
2024-07-18 23:37:13 DBG 
 ________      _____                  
 ___  __ \_______  /_____________ ___   Rotom 64 bit (amd64/linux)
 __  /_/ /  __ \  __/  __ \_  __ '__ \  Port: 6379, Pid: 15817
 _  _, _// /_/ / /_ / /_/ /  / / / / /  Build: 
 /_/ |_| \____/\__/ \____//_/ /_/ /_/

2024-07-18 23:37:13 INF read config file config=config.json
2024-07-18 23:37:13 INF rotom server is ready to accept.

容器运行

也可以选择在容器中运行。首先运行make build-docker构建Docker镜像:

REPOSITORY       TAG           IMAGE ID       CREATED         SIZE
rotom            latest        0cd91943967a   5 seconds ago   20.9MB

然后启动容器:

docker run --rm -p 6379:6379 --name rotom rotom:latest

Go代码示例

以下是一个使用rotom的Go客户端示例:

package main

import (
	"context"
	"fmt"
	"github.com/go-redis/redis/v8"
)

func main() {
	// 创建Redis客户端
	rdb := redis.NewClient(&redis.Options{
		Addr:     "localhost:6379", // rotom服务器地址
		Password: "",               // 无密码
		DB:       0,                // 默认DB
	})

	ctx := context.Background()

	// 设置键值
	err := rdb.Set(ctx, "key", "value", 0).Err()
	if err != nil {
		panic(err)
	}

	// 获取键值
	val, err := rdb.Get(ctx, "key").Result()
	if err != nil {
		panic(err)
	}
	fmt.Println("key:", val)

	// 使用Hash
	err = rdb.HSet(ctx, "user:1", "name", "Alice", "age", 30).Err()
	if err != nil {
		panic(err)
	}

	// 获取Hash字段
	name, err := rdb.HGet(ctx, "user:1", "name").Result()
	if err != nil {
		panic(err)
	}
	fmt.Println("user name:", name)
}

路线图

  • [ ] 支持LRU缓存和内存淘汰
  • [ ] dict中的渐进式rehash
  • [ ] RDB和AOF重写

更多关于golang轻量级Redis服务器实现插件库rotom的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang轻量级Redis服务器实现插件库rotom的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Rotom - Golang轻量级Redis服务器实现插件库

Rotom是一个用Go语言实现的轻量级Redis兼容服务器框架,它允许开发者快速构建自定义的Redis-like服务。下面我将详细介绍Rotom的使用方法。

安装Rotom

go get github.com/tidwall/rotom

基本使用示例

package main

import (
	"fmt"
	"log"
	"net"
	
	"github.com/tidwall/rotom"
)

func main() {
	// 创建一个新的Rotom服务器
	s := rotom.NewServer()
	
	// 注册自定义命令
	s.Register("HELLO", func(c rotom.Conn, args []string) {
		c.WriteString(fmt.Sprintf("Hello, %s!", args[1]))
	})
	
	// 启动服务器
	ln, err := net.Listen("tcp", ":6379")
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Server started on %s", ln.Addr())
	
	// 接受连接
	for {
		conn, err := ln.Accept()
		if err != nil {
			log.Println("Accept error:", err)
			continue
		}
		go s.ServeConn(conn)
	}
}

核心功能

1. 处理Redis协议命令

Rotom支持标准的Redis协议,可以轻松处理各种命令:

s.Register("PING", func(c rotom.Conn, args []string) {
	if len(args) > 1 {
		c.WriteString(args[1])
	} else {
		c.WriteString("PONG")
	}
})

2. 数据存储

Rotom内置了简单的键值存储:

s.Register("SET", func(c rotom.Conn, args []string) {
	if len(args) < 3 {
		c.WriteError("ERR wrong number of arguments for 'set' command")
		return
	}
	s.Set(args[1], args[2])
	c.WriteString("OK")
})

s.Register("GET", func(c rotom.Conn, args []string) {
	if len(args) < 2 {
		c.WriteError("ERR wrong number of arguments for 'get' command")
		return
	}
	val, ok := s.Get(args[1])
	if !ok {
		c.WriteNull()
	} else {
		c.WriteString(val)
	}
})

3. 自定义数据结构

你可以扩展Rotom来支持更复杂的数据结构:

type ListStore struct {
	items map[string][]string
}

func NewListStore() *ListStore {
	return &ListStore{
		items: make(map[string][]string),
	}
}

func (ls *ListStore) LPush(key, value string) {
	ls.items[key] = append([]string{value}, ls.items[key]...)
}

func (ls *ListStore) LRange(key string, start, stop int) []string {
	items := ls.items[key]
	if start < 0 {
		start = len(items) + start
	}
	if stop < 0 {
		stop = len(items) + stop
	}
	if start > stop || start >= len(items) {
		return nil
	}
	if stop >= len(items) {
		stop = len(items) - 1
	}
	return items[start : stop+1]
}

func main() {
	s := rotom.NewServer()
	listStore := NewListStore()
	
	s.Register("LPUSH", func(c rotom.Conn, args []string) {
		if len(args) < 3 {
			c.WriteError("ERR wrong number of arguments for 'lpush' command")
			return
		}
		listStore.LPush(args[1], args[2])
		c.WriteInt(1) // 返回列表长度
	})
	
	// ... 其他代码
}

高级特性

1. 中间件支持

Rotom支持中间件,可以在命令执行前后添加逻辑:

// 日志中间件
func loggingMiddleware(next rotom.HandlerFunc) rotom.HandlerFunc {
	return func(c rotom.Conn, args []string) {
		log.Printf("Command received: %v", args)
		next(c, args)
		log.Printf("Command processed: %v", args)
	}
}

// 注册中间件
s.Use(loggingMiddleware)

2. 认证支持

// 认证中间件
func authMiddleware(next rotom.HandlerFunc) rotom.HandlerFunc {
	return func(c rotom.Conn, args []string) {
		if len(args) > 0 && args[0] == "AUTH" {
			if len(args) != 2 || args[1] != "mypassword" {
				c.WriteError("ERR invalid password")
				return
			}
			c.WriteString("OK")
			return
		}
		next(c, args)
	}
}

// 注册中间件
s.Use(authMiddleware)

3. 性能监控

type Stats struct {
	CommandsProcessed int64
}

func statsMiddleware(stats *Stats) rotom.MiddlewareFunc {
	return func(next rotom.HandlerFunc) rotom.HandlerFunc {
		return func(c rotom.Conn, args []string) {
			atomic.AddInt64(&stats.CommandsProcessed, 1)
			next(c, args)
		}
	}
}

func main() {
	stats := &Stats{}
	s := rotom.NewServer()
	s.Use(statsMiddleware(stats))
	
	// ... 其他代码
	
	// 可以定期打印统计信息
	go func() {
		for range time.Tick(10 * time.Second) {
			log.Printf("Commands processed: %d", atomic.LoadInt64(&stats.CommandsProcessed))
		}
	}()
}

实际应用场景

Rotom特别适合以下场景:

  1. 需要Redis协议兼容的轻量级服务
  2. 快速原型开发
  3. 嵌入式数据库服务
  4. 教学和演示目的

性能考虑

对于生产环境,建议:

  1. 使用连接池管理客户端连接
  2. 为高并发场景实现更高效的数据结构
  3. 考虑添加持久化支持

总结

Rotom提供了一个简单而强大的框架来构建Redis兼容服务器。通过其灵活的API和中间件支持,开发者可以快速实现自定义功能,同时保持与Redis客户端的兼容性。

以上示例展示了Rotom的核心功能,你可以根据实际需求进行扩展和定制。Rotom的轻量级特性使其成为快速开发和测试的理想选择。

回到顶部