├── go.mod
├── .gitignore
├── service
├── version.go
├── service.go
└── kv.go
├── utils
└── KVStore.go
├── cmd
├── main.go
└── Dockerfile
├── index.html
├── .github
└── workflows
│ ├── release.app.yaml
│ └── release.image.yaml
├── README.md
└── Makefile
/go.mod:
--------------------------------------------------------------------------------
1 | module forthxu/kv
2 |
3 | go 1.23.4
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.log
3 | *.pid
4 | *.app
5 | *.cmd
6 | *.bin
7 | *.exe
8 | build/
9 | *~
--------------------------------------------------------------------------------
/service/version.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "runtime"
5 | "fmt"
6 | )
7 |
8 | var (
9 | Version = "undefined"
10 | GitDate = "undefined"
11 | GitCommit = "undefined"
12 | BuildDate = "undefined"
13 | GoVersion = runtime.Version()
14 | )
15 |
16 | func getVersion() string{
17 | return fmt.Sprintf(
18 | "Version:%s, GitDate:%s, GitCommit:%s, BuildDate:%s, GoVersion:%s",
19 | Version,
20 | GitDate,
21 | GitCommit,
22 | BuildDate,
23 | GoVersion,
24 | )
25 | }
--------------------------------------------------------------------------------
/service/service.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "log"
5 | )
6 |
7 | type Service struct {
8 | Run bool
9 | }
10 |
11 | func New() *Service {
12 | this := &Service{}
13 | return this
14 | }
15 |
16 | //开始运行
17 | func (this *Service) Start(){
18 | //载入配置
19 | //...
20 |
21 | //启动配套服务
22 | //...
23 |
24 | //启动主服务
25 | go this.starWork()
26 |
27 | log.Println("[service] start");
28 | }
29 |
30 | //结束运行
31 | func (this *Service) Close(){
32 | //关闭配套服务
33 |
34 | //关闭主服务
35 | this.endWork()
36 |
37 | log.Println("[service] close");
38 | }
--------------------------------------------------------------------------------
/utils/KVStore.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "sync"
5 | )
6 |
7 | type KVStore struct {
8 | data map[string]string
9 | lock sync.RWMutex
10 | }
11 |
12 | func NewKVStore() *KVStore {
13 | return &KVStore{
14 | data: make(map[string]string),
15 | }
16 | }
17 |
18 | func (store *KVStore) Set(key, value string) {
19 | store.lock.Lock()
20 | defer store.lock.Unlock()
21 | store.data[key] = value
22 | }
23 |
24 | func (store *KVStore) Get(key string) (string, bool) {
25 | store.lock.RLock()
26 | defer store.lock.RUnlock()
27 | value, exists := store.data[key]
28 | return value, exists
29 | }
30 |
31 | func (store *KVStore) Keys() (keys []string) {
32 | store.lock.RLock()
33 | defer store.lock.RUnlock()
34 | for k,_ := range store.data {
35 | keys = append(keys, k)
36 | }
37 | return
38 | }
--------------------------------------------------------------------------------
/cmd/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "os"
5 | "os/signal"
6 | "syscall"
7 | "log"
8 | "runtime"
9 | "forthxu/kv/service"
10 | )
11 |
12 | func init() {
13 | //设置cpu核心数
14 | runtime.GOMAXPROCS(runtime.NumCPU() - 1)
15 | }
16 |
17 | func main() {
18 | //启用服务
19 | srv := service.New()
20 | srv.Start()
21 |
22 | //grpc服务
23 | //...
24 |
25 | c := make(chan os.Signal, 1)
26 | signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
27 | WORK:
28 | for {
29 | s := <-c
30 | log.Printf("[main] signal: %s", s.String())
31 | switch s {
32 | case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
33 | //关闭服务
34 | srv.Close()
35 | return
36 | case syscall.SIGHUP:
37 | //关闭服务
38 | srv.Close()
39 | //重载配置
40 | //...
41 | //启动服务
42 | srv.Start()
43 | continue WORK
44 | default:
45 | os.Exit(0)
46 | return
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 简易KV系统
7 |
8 |
13 |
14 |
15 | Loading README.md...
16 |
17 |
18 |
29 |
30 |
--------------------------------------------------------------------------------
/cmd/Dockerfile:
--------------------------------------------------------------------------------
1 | # 基础系统
2 | FROM alpine:3.16
3 |
4 | # 外部变量
5 | ARG app=kv
6 | ARG version=0.0.1
7 | ARG usage=http://forthxu.com
8 | ARG arch=amd64
9 | ARG date=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
10 |
11 | # 环境变量
12 | ENV APP=${app}
13 | ENV VERSION=${version}
14 | ENV USAGE=${usage}
15 | ENV ARCH=${arch}
16 | ENV DATE=${date}
17 |
18 | # 标签
19 | LABEL maintainer="forthxu "
20 |
21 | # 镜像信息
22 | LABEL org.label-schema.build-date=${date} \
23 | org.label-schema.description="service ${app}" \
24 | org.label-schema.name=${app} \
25 | org.label-schema.schema-version=${version} \
26 | org.label-schema.usage=${usage}
27 |
28 | LABEL org.opencontainers.image.source=${usage} \
29 | org.opencontainers.image.description="service ${app}" \
30 | org.opencontainers.image.licenses=MIT
31 |
32 | # 初始化环境
33 | RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
34 | RUN apk add --no-cache tzdata \
35 | && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
36 | && echo "Asia/Shanghai" > /etc/timezone \
37 | && apk del tzdata
38 |
39 | # 创建工作目录和创建用户
40 | RUN mkdir -p /app
41 |
42 | # 使用工作目录
43 | USER root
44 | WORKDIR /app
45 |
46 | # 拷贝程序
47 | COPY ${app}.${arch}.bin .
48 |
49 | # 暴露端口
50 | EXPOSE 6378
51 |
52 | # 运行程序
53 | ENTRYPOINT ["/bin/sh", "-c", "./${app}.${arch}.bin"]
54 |
--------------------------------------------------------------------------------
/.github/workflows/release.app.yaml:
--------------------------------------------------------------------------------
1 | name: release app
2 |
3 | on:
4 | push:
5 | tags:
6 | - 'v*' # 触发条件:当推送的标签以 v 开头时
7 | workflow_dispatch:
8 |
9 | jobs:
10 | build:
11 | runs-on: ubuntu-latest
12 |
13 | steps:
14 | # 拉取代码
15 | - name: Checkout code
16 | uses: actions/checkout@v3
17 |
18 | # 设置环境
19 | - name: Set up Go
20 | uses: actions/setup-go@v4
21 | with:
22 | go-version: 1.23
23 |
24 | # 编译项目
25 | - name: Build project
26 | run: |
27 | make linux ARCH=amd64
28 | make mac ARCH=amd64
29 | make windows ARCH=amd64
30 | make linux ARCH=arm64
31 | make mac ARCH=arm64
32 | make windows ARCH=arm64
33 |
34 | # 创建版本
35 | - name: Create Release
36 | id: create_release
37 | uses: actions/create-release@v1
38 | with:
39 | tag_name: ${{ github.ref_name }}
40 | release_name: Release ${{ github.ref_name }}
41 | draft: false
42 | prerelease: false
43 | env:
44 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
45 |
46 |
47 | # 上传文件
48 | - name: Upload all files
49 | run: |
50 | for file in ./build/*; do
51 | echo "Uploading $file"
52 | gh release upload "${{ github.ref_name }}" "$file" --clobber
53 | done
54 | env:
55 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
--------------------------------------------------------------------------------
/.github/workflows/release.image.yaml:
--------------------------------------------------------------------------------
1 | name: release image
2 |
3 | on:
4 | push:
5 | tags:
6 | - 'v*' # 触发条件:当推送的标签以 v 开头时
7 | workflow_dispatch:
8 |
9 | jobs:
10 | build-and-push:
11 | strategy:
12 | matrix:
13 | platform: [arm64,amd64] # 分别构建 amd 和 arm 平台
14 |
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - name: Set up QEMU
19 | uses: docker/setup-qemu-action@v3
20 |
21 | # 拉取代码
22 | - name: Checkout code
23 | uses: actions/checkout@v3
24 |
25 | # 设置环境
26 | - name: Set up Go
27 | uses: actions/setup-go@v4
28 | with:
29 | go-version: 1.23
30 |
31 | # 编译项目
32 | - name: Build project
33 | run: |
34 | docker buildx create --use
35 | docker buildx inspect --bootstrap
36 | make docker ARCH=${{ matrix.platform }}
37 |
38 | # 设置版本号
39 | - name: Set Docker image tag
40 | id: vars
41 | run: |
42 | if [ "${{ github.ref_type }}" == "tag" ]; then
43 | echo "tag=${{ github.ref_name }}" >> $GITHUB_ENV
44 | else
45 | echo "tag=latest" >> $GITHUB_ENV
46 | fi
47 |
48 | # 登录 GitHub Container Registry
49 | - name: Log in to GitHub Container Registry
50 | uses: docker/login-action@v2
51 | with:
52 | registry: ghcr.io
53 | username: ${{ github.actor }}
54 | password: ${{ secrets.GITHUB_TOKEN }}
55 |
56 | # 推送 Docker 镜像到 Docker Hub
57 | - name: Push Docker image
58 | run: |
59 | docker tag forthxu/kv:latest-${{ matrix.platform }} ghcr.io/${{ github.repository_owner }}/kv:${{ env.tag }}-${{ matrix.platform }}
60 | docker push ghcr.io/${{ github.repository_owner }}/kv:${{ env.tag }}-${{ matrix.platform }}
61 |
62 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 简易KV系统
2 |
3 | 该项目主要举例 golang项目结构设置、Makefile程序编译、Docker镜像打包、跨平台编译、amd64和arm64不同cpu架构编译、github action流程(发布release、发布package)
4 |
5 | ## redis通讯协议
6 |
7 | [http://forthxu.com/blog/article/58.html](http://forthxu.com/blog/article/58.html)
8 |
9 | 已实现命令
10 |
11 | ```
12 | ~ redis-cli -p 6378
13 | 127.0.0.1:6378> set x y
14 | OK
15 | 127.0.0.1:6378> get x
16 | "y"
17 | 127.0.0.1:6378> keys *
18 | 1) "key:__rand_int__"
19 | 2) "x"
20 | 127.0.0.1:6378> version
21 | forthxuKV Version:v1.0.3, GitDate:2025-03-14T13:13:38+00:00, GitCommit:bd9eb3f, BuildDate:2025-03-14T13:14:20:z, GoVersion:go1.24.1
22 | ```
23 |
24 | ## 压测
25 |
26 | ```
27 | MacBook Pro Apple M3 Max 36 GB
28 |
29 | ~ redis-benchmark -h 127.0.0.1 -p 6378 -t set -n 1000000 -P 1000 -q
30 |
31 | SET: 1248120.38 requests per second, p50=0.367 msec
32 |
33 | ~ redis-benchmark -h 127.0.0.1 -p 6378 -t get -n 1000000 -P 1000 -q
34 |
35 | GET: 3436426.00 requests per second, p50=0.647 msec
36 | ```
37 |
38 | ## 编译
39 |
40 | ```
41 | make mac
42 | make linux ARCH=arm64
43 | make windows ARCH=amd64
44 | make docker ARCH=amd64
45 | ```
46 |
47 | ## 推送git tag触发github action
48 | ```
49 | git tag v1.0.4
50 | git push origin v1.0.4
51 | ```
52 |
53 | ## 下载
54 |
55 | https://github.com/forthxu/kv/releases
56 |
57 | ## docker
58 |
59 | ### 编译docker镜像
60 |
61 | ```
62 | make docker
63 | make docker ARCH=arm64
64 | make docker ARCH=amd64
65 | ```
66 | ### 启动docker容器
67 |
68 | https://github.com/forthxu/kv/pkgs/container/kv
69 |
70 | ```
71 | docker pull ghcr.io/forthxu/kv:v1.0.0-amd64
72 |
73 | docker run --name kv -d -p 6378:6378 \
74 | ghcr.io/forthxu/kv:latest-amd64
75 | ```
76 |
77 | # github action
78 |
79 | release.app.yaml 编译golang程序并发布到github release,方便使用者直接下载
80 |
81 | release.images.yaml 编译golang程序并打包成docker镜像发布
82 |
83 | 程序和docker镜像同时发布amd64和arm64两个版本,并且程序有mac、linux、windows三个版本
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | #编译配置
2 | MAKEFLAGS += --no-print-directory
3 | #工作目录
4 | WORKSPACE=$(dir $(abspath $(lastword $(MAKEFILE_LIST))))
5 | #包名
6 | PACKAGE=$(shell basename $(WORKSPACE))
7 | #编译
8 | TARGET?=$(WORKSPACE)build/
9 | GOBUILD=go build
10 | #版本信息
11 | GITCOMMIT=`git describe --always`
12 | VERSION=$$(git describe --tags || echo "0.0.1")
13 | GITDATE=`TZ=UTC git show -s --date=iso-strict-local --format=%cd HEAD`
14 | BUILDDATE=`date -u +"%Y-%m-%dT%H:%M:%S%:z"`
15 | LDFLAGS="-X forthxu/kv/service.Version=${VERSION} -X forthxu/kv/service.BuildDate=${BUILDDATE} -X forthxu/kv/service.GitCommit=${GITCOMMIT} -X forthxu/kv/service.GitDate=${GITDATE}"
16 | CGO_ENABLED=0
17 | #编译目标
18 | ACTION=$(MAKECMDGOALS)
19 | #系统
20 | OS?=$(shell uname|tr A-Z a-z)
21 | ifeq ($(ACTION), mac)
22 | OS=darwin
23 | SUFFIX=cmd
24 | else ifeq ($(ACTION), darwin)
25 | OS=darwin
26 | SUFFIX=cmd
27 | else ifeq ($(ACTION), dylib)
28 | OS=darwin
29 | SUFFIX=dylib
30 | CGO_ENABLED=1
31 | else ifeq ($(ACTION), docker)
32 | OS=linux
33 | SUFFIX=bin
34 | else ifeq ($(ACTION), linux)
35 | OS=linux
36 | SUFFIX=bin
37 | CGO_ENABLED=0
38 | else ifeq ($(ACTION), so)
39 | OS=linux
40 | SUFFIX=so
41 | CGO_ENABLED=1
42 | else ifeq ($(ACTION), windows)
43 | OS=windows
44 | SUFFIX=exe
45 | else ifeq ($(ACTION), dll)
46 | OS=windows
47 | SUFFIX=dll
48 | CGO_ENABLED=1
49 | endif
50 | ifeq ($(OS), darwin)
51 | CC=gcc
52 | CXX=g++
53 | LD=ld
54 | else ifeq ($(OS), linux)
55 | CC=gcc
56 | CXX=g++
57 | LD=ld
58 | else ifeq ($(OS), windows)
59 | CC=clang
60 | CXX=clang++
61 | LD=ld
62 | endif
63 | #架构
64 | ARCHORIGIN=$(shell uname -m | tr A-Z a-z | sed 's/x86_64/amd64/' | sed 's/aarch64/arm64/')
65 | ARCH?=ARCHORIGIN
66 | ifeq ($(ARCH), arm64)
67 | ARCH=arm64
68 | ifeq ($(ARCHORIGIN), amd64)
69 | ifeq ($(OS), linux)
70 | CC=aarch64-linux-musl-gcc
71 | CXX=aarch64-linux-musl-g++
72 | LD=aarch64-linux-musl-ld
73 | endif
74 | endif
75 | else
76 | ARCH=amd64
77 | ifeq ($(ARCHORIGIN), arm64)
78 | ifeq ($(OS), linux)
79 | CC=x86_64-linux-musl-gcc
80 | CXX=x86_64-linux-musl-g++
81 | LD=x86_64-linux-musl-ld
82 | endif
83 | endif
84 | endif
85 | #docker仓库
86 | REGISTRY=forthxu
87 |
88 | #忽略目录
89 | .PHONY: $(PACKAGE)
90 |
91 | #默认
92 | default: $(OS)
93 | @[ -f "$(WORKSPACE)cmd/main.go" ] && $(TARGET)$(PACKAGE).$(ARCH).$(SUFFIX) || exit 0
94 |
95 | #帮助
96 | help: info
97 | @printf "命令格式:\n\tmake $(OS) ARCH=$(ARCH) TARGET=./build\n"
98 | info:
99 | @printf "目标:\t\t$(PACKAGE)\n"
100 | @printf "目标后缀:\t$(SUFFIX)\n"
101 | @printf "目标系统: \t$(OS)\n"
102 | @printf "目标架构: \t$(ARCH)\n"
103 | @printf "工作目录:\t$(WORKSPACE)\n"
104 | @printf "编译目录: \t$(abspath $(TARGET))/\n"
105 | #拷贝资源
106 | resource:
107 | @mkdir -p $(TARGET)
108 | @[ -d "$(WORKSPACE)cmd" ] && find $(WORKSPACE)cmd/* -maxdepth 0 -not -regex '.*\.go$$' -not -regex '.*\.sh$$' |xargs -I {} cp -r {} $(TARGET)/ || exit 0
109 | #生成protobuf
110 | protobuf:
111 | @[ -d "$(WORKSPACE)proto" ] && find $(WORKSPACE) -maxdepth 1 -type d -name proto |xargs -I {} find {} -type f -maxdepth 1 | xargs -I {} protoc {} -I $(WORKSPACE) --go_out=$(WORKSPACE) --go-grpc_out=$(WORKSPACE) --experimental_allow_proto3_optional || exit 0
112 | @[ -d "$(WORKSPACE)proto" ] && find $(WORKSPACE) -regex ".*.pb.go" -exec sh -c 'sed -i".bak" "s/,omitempty//g" "{}" && rm -f "{}.bak"' \; || exit 0
113 |
114 | #编译程序
115 | mac: darwin
116 | darwin: program
117 | linux: program
118 | windows: program
119 | program: info resource protobuf
120 | [ -f "$(WORKSPACE)cmd/main.go" ] && CGO_ENABLED=${CGO_ENABLED} CC=${CC} CXX=${CXX} LD=${LD} GOOS=${OS} GOARCH=${ARCH} $(GOBUILD) --ldflags=${LDFLAGS} -o $(TARGET)$(PACKAGE).$(ARCH).${SUFFIX} $(WORKSPACE)cmd/main.go || exit 0
121 | #编译动态库
122 | dylib: dynamic
123 | so: dynamic
124 | dll: dynamic
125 | dynamic: info protobuf
126 | @[ -d "$(WORKSPACE)plugin" ] && CGO_ENABLED=${CGO_ENABLED} CC=${CC} CXX=${CXX} LD=${LD} GOOS=${OS} GOARCH=${ARCH} $(GOBUILD) --ldflags=${LDFLAGS} -buildmode=plugin -o $(TARGET)$(PACKAGE).$(ARCH).$(SUFFIX) $(WORKSPACE)/plugin/*.go || exit 0
127 | #打包docker镜像
128 | docker: linux
129 | ifneq ($(wildcard $(WORKSPACE)cmd/Dockerfile),)
130 | @docker build --platform=linux/$(ARCH) -t $(REGISTRY)/$(PACKAGE):latest-$(ARCH) -f $(TARGET)Dockerfile --build-arg arch=$(ARCH) $(TARGET)
131 | endif
132 |
--------------------------------------------------------------------------------
/service/kv.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "os"
5 | "flag"
6 | "bufio"
7 | "fmt"
8 | "log"
9 | "io"
10 | "net"
11 | "strconv"
12 | "strings"
13 | "forthxu/kv/utils"
14 | )
15 |
16 | var (
17 | _listen net.Listener
18 | )
19 |
20 | func parseRESP(reader *bufio.Reader) ([]string, error) {
21 | line, err := reader.ReadString('\n') // 读取直到 \n
22 | if err != nil {
23 | return nil, err
24 | }
25 | //fmt.Printf("Raw start line read: %q\n", line)
26 | if len(line) < 2 || line[len(line)-2:] != "\r\n" {
27 | return nil, fmt.Errorf("protocol error: invalid start line ending")
28 | }
29 | line = line[:len(line)-2] // 去掉 \r\n
30 |
31 | if line[0] != '*' {
32 | return nil, fmt.Errorf("protocol error: expected array")
33 | }
34 |
35 | numArgs, err := strconv.Atoi(line[1:])
36 | if err != nil {
37 | return nil, fmt.Errorf("protocol error: invalid array length")
38 | }
39 |
40 | args := make([]string, numArgs)
41 | for i := 0; i < numArgs; i++ {
42 | line, err = reader.ReadString('\n')
43 | if err != nil {
44 | return nil, err
45 | }
46 | //fmt.Printf("Raw line read: %q\n", line)
47 | if len(line) < 2 || line[len(line)-2:] != "\r\n" {
48 | return nil, fmt.Errorf("protocol error: invalid line ending")
49 | }
50 | line = line[:len(line)-2] // 去掉 \r\n
51 |
52 | if line[0] != '$' {
53 | return nil, fmt.Errorf("protocol error: expected bulk string")
54 | }
55 |
56 | length, err := strconv.Atoi(line[1:])
57 | if err != nil || length < 0 {
58 | return nil, fmt.Errorf("protocol error: invalid bulk string length")
59 | }
60 |
61 | arg := make([]byte, length+2)
62 | _, err = io.ReadFull(reader, arg)
63 | if err != nil {
64 | return nil, err
65 | }
66 | if string(arg[length:]) != "\r\n" {
67 | return nil, fmt.Errorf("protocol error: invalid bulk string ending")
68 | }
69 | args[i] = string(arg[:length])
70 | }
71 |
72 | return args, nil
73 | }
74 |
75 | func handleConnection(conn net.Conn, store *utils.KVStore) {
76 | defer conn.Close()
77 | reader := bufio.NewReader(conn)
78 |
79 | for {
80 | args, err := parseRESP(reader)
81 | if err != nil {
82 | conn.Write([]byte("-ERR " + err.Error() + "\r\n"))
83 | return
84 | }
85 |
86 | if len(args) < 1 {
87 | conn.Write([]byte("-ERR Missing command\r\n"))
88 | continue
89 | }
90 |
91 | command := strings.ToUpper(args[0])
92 | switch command {
93 | case "SET":
94 | if len(args) != 3 {
95 | conn.Write([]byte("-ERR Wrong number of arguments for 'SET' command\r\n"))
96 | continue
97 | }
98 | key, value := args[1], args[2]
99 | store.Set(key, value)
100 | conn.Write([]byte("+OK\r\n"))
101 | case "GET":
102 | if len(args) != 2 {
103 | conn.Write([]byte("-ERR Wrong number of arguments for 'GET' command\r\n"))
104 | continue
105 | }
106 | key := args[1]
107 | if value, exists := store.Get(key); exists {
108 | conn.Write([]byte(fmt.Sprintf("$%d\r\n%s\r\n", len(value), value)))
109 | } else {
110 | conn.Write([]byte("$-1\r\n"))
111 | }
112 | case "SELECT":
113 | if len(args) != 2 {
114 | conn.Write([]byte("-ERR Wrong number of arguments for 'SELECT' command\r\n"))
115 | continue
116 | }
117 | db, err := strconv.Atoi(args[1])
118 | if err != nil || db < 0 {
119 | conn.Write([]byte("-ERR Wrong value of arguments for 'SELECT' command\r\n"))
120 | continue
121 | }
122 | conn.Write([]byte("+OK\r\n"))
123 | case "KEYS":
124 | if len(args) != 2 {
125 | conn.Write([]byte("-ERR Wrong number of arguments for 'KEYS' command\r\n"))
126 | continue
127 | }
128 | //key := args[1]
129 |
130 | keys := store.Keys()
131 | if len(keys)>0 {
132 | conn.Write([]byte(fmt.Sprintf("*%d\r\n", len(keys))))
133 | for _,v := range keys {
134 | conn.Write([]byte(fmt.Sprintf("$%d\r\n%s\r\n", len(v), v)))
135 | }
136 | } else {
137 | conn.Write([]byte("$-1\r\n"))
138 | }
139 | case "VERSION":
140 | conn.Write([]byte("+forthxuKV "+getVersion()+"\r\n"))
141 | default:
142 | log.Println("[service] -ERR Unknown command\r\n", args)
143 | conn.Write([]byte("-ERR Unknown command\r\n"))
144 | }
145 | }
146 | }
147 |
148 | func (this *Service) starWork() {
149 | if this.Run {
150 | return
151 | }
152 | this.Run = true
153 |
154 | port := flag.Int("p", 6378, "端口号")
155 | flag.Parse() // 解析命令行参数
156 | if *port < 1 || *port > 65535 {
157 | log.Printf("[service] 无效的端口号: %d,端口号应在 1 到 65535 之间\n", port)
158 | os.Exit(1)
159 | }
160 | address := fmt.Sprintf(":%d", *port)
161 |
162 | store := utils.NewKVStore()
163 | _listen, err := net.Listen("tcp", address)
164 | if err != nil {
165 | log.Println("[service] Failed to start server:", err)
166 | return
167 | }
168 | defer _listen.Close()
169 |
170 | log.Printf("[service] Server is running on port %d...\r\n", *port)
171 | for {
172 | conn, err := _listen.Accept()
173 | if err != nil {
174 | log.Println("[service] Failed to accept connection:", err)
175 | continue
176 | }
177 | go handleConnection(conn, store)
178 | }
179 | }
180 |
181 | func (this *Service) endWork() {
182 | if _listen!=nil {
183 | _listen.Close()
184 | }
185 | }
186 |
--------------------------------------------------------------------------------