├── 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 | --------------------------------------------------------------------------------