Skip to content

Commit

Permalink
Merge pull request #73 from RealFax/dev
Browse files Browse the repository at this point in the history
feat. better README, grpc basic auth support
  • Loading branch information
PotatoCloud authored Feb 3, 2024
2 parents 8e6e66b + ec31f21 commit ed9095f
Show file tree
Hide file tree
Showing 14 changed files with 241 additions and 28 deletions.
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ This is a reliable distributed key-value store based on the raft algorithm, and
go get github.com/RealFax/RedQueen@latest
```

[Code example](https://github.com/RealFax/RedQueen/tree/master/client/example)
[Code example](https://github.com/RealFax/RedQueen/tree/master/pkg/client/example)

## Write & Read
_RedQueen based on raft algorithm has the characteristics of single node write (Leader node) and multiple node read (Follower node)._
Expand Down Expand Up @@ -53,31 +53,42 @@ Read order: `Environment Variables | Program Arguments -> Configuration File`
- `RQ_DATA_DIR <string>` Node data storage directory
- `RQ_LISTEN_PEER_ADDR <string>` Node-to-node communication (Raft RPC) listening address, cannot be `0.0.0.0`
- `RQ_LISTEN_CLIENT_ADDR <string>` Node service listening (gRPC API) address
- `RQ_LISTEN_HTTP_ADDR <string>` Node service listening (http API) address
- `RQ_MAX_SNAPSHOTS <uint32>` Maximum number of snapshots
- `RQ_REQUESTS_MERGED <bool>` Whether to enable request merging
- `RQ_AUTO_TLS <bool>` Whether to enable auto tls
- `RQ_TLS_CERT_FILE <string>` TLS certificate path
- `RQ_TLS_KEY_FILE <string>` TLS key path
- `RQ_STORE_BACKEND <string [nuts]>` Storage backend (default: nuts)
- `RQ_NUTS_NODE_NUM <int64>`
- `RQ_NUTS_SYNC <bool>` Whether to enable synchronous disk writes
- `RQ_NUTS_STRICT_MODE <bool>` Whether to enable call checking
- `RQ_NUTS_RW_MODE <string [fileio, mmap]>` Write mode
- `RQ_CLUSTER_BOOTSTRAP <string>` Cluster information (e.g., node-1@127.0.0.1:5290, node-2@127.0.0.1:4290)
- `RQ_DEBUG_PPROF <bool>` Enable pprof debugging
- `RQ_BASIC_AUTH <string>` Basic auth list (e.g., admin:123456,root:toor)


### Program Arguments
- `-config-file <string>` Configuration file path. Note: If this parameter is set, the following parameters will be ignored, and the configuration file will be used.
- `-node-id <string>` Node ID
- `-data-dir <string>` Node data storage directory
- `-listen-peer-addr <string>` Node-to-node communication (Raft RPC) listening address, cannot be `0.0.0.0`
- `-listen-client-addr <string>` Node service listening (gRPC API) address
- `-listen-http-addr <string>` Node service listening (http API) address
- `-max-snapshots <uint32>` Maximum number of snapshots
- `-requests-merged <bool>` Whether to enable request merging
- `-auto-tls <bool>` Whether to enable auto tls
- `-tls-cert-file <string>` TLS certificate path
- `-tls-key-file <string>` TLS key path
- `-store-backend <string [nuts]>` Storage backend (default: nuts)
- `-nuts-node-num <int64>`
- `-nuts-sync <bool>` Whether to enable synchronous disk writes
- `-nuts-strict-mode <bool>` Whether to enable call checking
- `-nuts-rw-mode <string [fileio, mmap]>` Write mode
- `-cluster-bootstrap <string>` Cluster information (e.g., node-1@127.0.0.1:5290, node-2@127.0.0.1:4290)
- `-d-pprof <bool>` Enable pprof debugging
- `-basic-auth <string>` Basic auth list (e.g., admin:123456,root:toor)

### Configuration File
```toml
Expand All @@ -89,6 +100,11 @@ listen-client-addr = "127.0.0.1:5230"
max-snapshots = 5
requests-merged = false

[node.tls]
auto = true
cert-file = ""
key-file = ""

[store]
# backend options
# nuts
Expand All @@ -110,6 +126,10 @@ backend = "nuts"

[misc]
pprof = false

[basic-auth]
root = "toor"
admin = "123456"
```

### _About More Usage (e.g., Docker Single/Multi-node Deployment), Please Refer to [**Wiki**](https://github.com/RealFax/RedQueen/wiki)_ 🤩
Expand Down
22 changes: 21 additions & 1 deletion README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ _灵感来源于《生化危机》中的超级计算机(Red Queen), 分布式key
go get github.com/RealFax/RedQueen@latest
```

[代码示例](https://github.com/RealFax/RedQueen/tree/master/client/example)
[代码示例](https://github.com/RealFax/RedQueen/tree/master/pkg/client/example)

## 写入 & 读取
_基于raft算法实现的RedQueen具备单节点写入(Leader node)多节点读取(Follower node)的特性_
Expand Down Expand Up @@ -53,31 +53,41 @@ RedQueen在内部实现了一个互斥锁, 并提供grpc接口调用
- `RQ_DATA_DIR <string>` 节点数据存储目录
- `RQ_LISTEN_PEER_ADDR <string>` 节点间通信监听(raft rpc)地址, 不可为 `0.0.0.0`
- `RQ_LISTEN_CLIENT_ADDR <string>` 节点服务监听(grpc api)地址
- `RQ_LISTEN_HTTP_ADDR <string>` 节点服务监听(http api)地址
- `RQ_MAX_SNAPSHOTS <uint32>` 最大快照数量
- `RQ_REQUESTS_MERGED <bool>` 是否开启合并请求
- `RQ_AUTO_TLS <bool>` 是否启用auto tls
- `RQ_TLS_CERT_FILE <string>` tls certificate文件路径
- `RQ_TLS_KEY_FILE <string>` tls key文件路径
- `RQ_STORE_BACKEND <string [nuts]>` 存储后端(默认nuts)
- `RQ_NUTS_NODE_NUM <int64>`
- `RQ_NUTS_SYNC <bool>` 是否启用同步写入磁盘
- `RQ_NUTS_STRICT_MODE <bool>` 是否启用调用检查
- `RQ_NUTS_RW_MODE <string [fileio, mmap]>` 写入模式
- `RQ_CLUSTER_BOOTSTRAP <string>` 集群信息 (例如 node-1@127.0.0.1:5290, node-2@127.0.0.1:4290)
- `RQ_DEBUG_PPROF <bool>` 启用pprof调试
- `RQ_BASIC_AUTH <string>` basic auth的信息 (例如 admin:123456,root:toor)

### 程序参数
- `-config-file <string>` 配置文件路径. note: 设置该参数后, 将会忽略以下参数, 使用配置文件
- `-node-id <string>` 节点ID
- `-data-dir <string>` 节点数据存储目录
- `-listen-peer-addr <string>` 节点间通信监听(raft rpc)地址, 不可为 `0.0.0.0`
- `-listen-client-addr <string>` 节点服务监听(grpc api)地址
- `-listen-http-addr <string>` 节点服务监听(http api)地址
- `-max-snapshots <uint32>` 最大快照数量
- `-requests-merged <bool>` 是否开启合并请求
- `-auto-tls <bool>` 是否启用auto tls
- `-tls-cert-file <string>` tls certificate文件路径
- `-tls-key-file <string>` tls key文件路径
- `-store-backend <string [nuts]>` 存储后端(默认nuts)
- `-nuts-node-num <int64>`
- `-nuts-sync <bool>` 是否启用同步写入磁盘
- `-nuts-strict-mode <bool>` 是否启用调用检查
- `-nuts-rw-mode <string [fileio, mmap]>` 写入模式
- `-cluster-bootstrap <string>` 集群信息 (例如 node-1@127.0.0.1:5290, node-2@127.0.0.1:4290)
- `-d-pprof <bool>` 启用pprof调试
- `-basic-auth <string>` basic auth信息 (例如 admin:123456,root:toor)

### 配置文件
```toml
Expand All @@ -86,9 +96,15 @@ id = "node-1"
data-dir = "/tmp/red_queen"
listen-peer-addr = "127.0.0.1:5290"
listen-client-addr = "127.0.0.1:5230"
listen-http-addr = "127.0.0.1:5231"
max-snapshots = 5
requests-merged = false

[node.tls]
auto = true
cert-file = ""
key-file = ""

[store]
# backend options
# nuts
Expand All @@ -110,6 +126,10 @@ backend = "nuts"

[misc]
pprof = false

[basic-auth]
root = "toor"
admin = "123456"
```

### _关于更多用法(例如docker单/多节点部署), 请参考 [**Wiki**](https://github.com/RealFax/RedQueen/wiki)_ 🤩
Expand Down
6 changes: 0 additions & 6 deletions config.toml.exam
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,6 @@ backend = "nuts"
name = "node-1"
peer-addr = "127.0.0.1:3290"

[log]
# logger options
# zap, internal
logger = ""
debug = false

[misc]
pprof = false

Expand Down
7 changes: 0 additions & 7 deletions internal/rqd/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ type ClusterBootstrap struct {
}

type Cluster struct {
Token string `toml:"token"`
Bootstrap []ClusterBootstrap `toml:"bootstrap"`
}

Expand Down Expand Up @@ -147,9 +146,6 @@ func bindServerFromArgs(cfg *Config, args ...string) error {
f.BoolVar(&cfg.Store.Nuts.StrictMode, "nuts-strict-mode", DefaultStoreNutsStrictMode, "enable strict mode")
f.Var(newValidatorStringValue[EnumNutsRWMode](DefaultStoreNutsRWMode, &cfg.Store.Nuts.RWMode), "nuts-rw-mode", "select read & write mode, options: fileio, mmap")

// main config::cluster
f.StringVar(&cfg.Cluster.Token, "cluster-token", "", "")

// main config::cluster::bootstrap(s)
// in cli: node-1@peer_addr,node-2@peer_addr
f.Var(newClusterBootstrapsValue("", &cfg.Cluster.Bootstrap), "cluster-bootstrap", "bootstrap at cluster startup, e.g. : node-1@peer_addr,node-2@peer_addr")
Expand Down Expand Up @@ -189,9 +185,6 @@ func bindServerFromEnv(cfg *Config) {
EnvBoolVar(&cfg.Store.Nuts.StrictMode, "RQ_NUTS_STRICT_MODE", DefaultStoreNutsStrictMode)
BindEnvVar(newValidatorStringValue[EnumNutsRWMode](DefaultStoreNutsRWMode, &cfg.Store.Nuts.RWMode), "RQ_NUTS_RW_MODE")

// main config::cluster
EnvStringVar(&cfg.Cluster.Token, "RQ_CLUSTER_TOKEN", "")

// main config::cluster::bootstrap(s)
BindEnvVar(newClusterBootstrapsValue("", &cfg.Cluster.Bootstrap), "RQ_CLUSTER_BOOTSTRAP")

Expand Down
6 changes: 6 additions & 0 deletions internal/rqd/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/RealFax/RedQueen/internal/rqd/store"
"github.com/RealFax/RedQueen/pkg/dlocker"
"github.com/RealFax/RedQueen/pkg/expr"
"github.com/RealFax/RedQueen/pkg/grpcutil"
"github.com/RealFax/RedQueen/pkg/httputil"
"github.com/RealFax/RedQueen/pkg/tlsutil"
"github.com/hashicorp/go-hclog"
Expand Down Expand Up @@ -141,8 +142,13 @@ func (s *Server) registerRPCServer() {
opts := make([]grpc.ServerOption, 0, 8)
if s.tlsConfig != nil {
opts = append(opts, grpc.Creds(credentials.NewTLS(s.tlsConfig)))
}

if s.cfg.BasicAuth != nil && len(s.cfg.BasicAuth) != 0 {
auth := grpcutil.NewBasicAuth(grpcutil.NewMemoryBasicAuthFunc(s.cfg.BasicAuth))
opts = append(opts, grpc.UnaryInterceptor(auth.Unary), grpc.StreamInterceptor(auth.Stream))
}

if s.grpcServer == nil {
s.grpcServer = grpc.NewServer(opts...)
}
Expand Down
2 changes: 1 addition & 1 deletion internal/version/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import "strings"

const (
Major = "0"
Minor = "7"
Minor = "8"
Patch = "1"
)

Expand Down
4 changes: 2 additions & 2 deletions pkg/balancer/lb_rand.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package balancer

import (
"crypto/rand"
"github.com/RealFax/RedQueen/pkg/expr"
"github.com/pkg/errors"
"math/big"
)
Expand All @@ -13,8 +14,7 @@ type randomBalance[K comparable, V any] struct {
func (b *randomBalance[K, V]) Next() (V, error) {
size := b.Size()
if size == 0 {
var empty V
return empty, errors.New("empty load balance list")
return expr.Zero[V](), errors.New("empty load balance list")
}
idx, _ := rand.Int(rand.Reader, big.NewInt(int64(size)))

Expand Down
4 changes: 2 additions & 2 deletions pkg/balancer/lb_round_robin.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package balancer

import (
"github.com/RealFax/RedQueen/pkg/expr"
"github.com/pkg/errors"
"sync/atomic"
)
Expand All @@ -13,8 +14,7 @@ type roundRobinBalance[K comparable, V any] struct {
func (b *roundRobinBalance[K, V]) Next() (V, error) {
size := b.Size()
if size == 0 {
var empty V
return empty, errors.New("empty load balance list")
return expr.Zero[V](), errors.New("empty load balance list")
}
next := b.current.Add(1) % size
b.current.CompareAndSwap(b.current.Load(), next)
Expand Down
10 changes: 5 additions & 5 deletions pkg/client/example/basic-key-value/case.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package main

import (
"context"
client2 "github.com/RealFax/RedQueen/pkg/client"
"github.com/RealFax/RedQueen/pkg/client"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"log"
Expand All @@ -12,9 +12,9 @@ import (
)

func main() {
c, err := client2.New(context.Background(), []string{
//"127.0.0.1:3230",
//"127.0.0.1:4230",
c, err := client.New(context.Background(), []string{
"127.0.0.1:3230",
"127.0.0.1:4230",
"127.0.0.1:5230",
}, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
Expand All @@ -23,7 +23,7 @@ func main() {
defer c.Close()

// watch case
watcher := client2.NewWatcher([]byte("Key1"))
watcher := client.NewWatcher([]byte("Key1"))
go func() {
if wErr := c.Watch(context.Background(), watcher); err != nil {
log.Fatal("client watch error:", wErr)
Expand Down
6 changes: 3 additions & 3 deletions pkg/client/example/distributed-lock/case.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package main

import (
"context"
client2 "github.com/RealFax/RedQueen/pkg/client"
"github.com/RealFax/RedQueen/pkg/client"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"log"
Expand All @@ -13,7 +13,7 @@ func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

c, err := client2.New(ctx, []string{
c, err := client.New(ctx, []string{
"127.0.0.1:3230",
"127.0.0.1:4230",
"127.0.0.1:5230",
Expand All @@ -23,7 +23,7 @@ func main() {
}
defer c.Close()

mu := client2.NewMutexLock(ctx, c, 120, "lock_object")
mu := client.NewMutexLock(ctx, c, 120, "lock_object")

if err = mu.Lock(); err != nil {
log.Fatal("client lock error:", err)
Expand Down
34 changes: 34 additions & 0 deletions pkg/client/example/with-auth/case.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package main

import (
"context"
"github.com/RealFax/RedQueen/pkg/client"
"github.com/RealFax/RedQueen/pkg/grpcutil"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"log"
)

func main() {
// create basic-auth client
authBroker := grpcutil.NewBasicAuthClient("admin", "123456")

c, err := client.New(context.Background(), []string{
"127.0.0.1:3230",
"127.0.0.1:4230",
"127.0.0.1:5230",
},
grpc.WithTransportCredentials(insecure.NewCredentials()),
// setup auth client interceptor
grpc.WithUnaryInterceptor(authBroker.Unary),
grpc.WithStreamInterceptor(authBroker.Stream),
)
if err != nil {
log.Fatal(err)
}
defer c.Close()

if _, err = c.Get(context.Background(), []byte("Key1"), nil); err != nil {
log.Fatal("client get error:", err)
}
}
42 changes: 42 additions & 0 deletions pkg/grpcutil/client_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package grpcutil

import (
"context"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
)

type BasicAuthClient struct {
authKey string
}

func (c BasicAuthClient) ctxWrap(ctx context.Context) context.Context {
return metadata.NewOutgoingContext(ctx, metadata.MD{
MetadataAuthorization: []string{c.authKey},
})
}

func (c BasicAuthClient) Unary(
ctx context.Context,
method string, req, reply any,
cc *grpc.ClientConn,
invoker grpc.UnaryInvoker,
opts ...grpc.CallOption,
) error {
return invoker(c.ctxWrap(ctx), method, req, reply, cc, opts...)
}

func (c BasicAuthClient) Stream(
ctx context.Context,
desc *grpc.StreamDesc,
cc *grpc.ClientConn,
method string,
streamer grpc.Streamer,
opts ...grpc.CallOption,
) (grpc.ClientStream, error) {
return streamer(c.ctxWrap(ctx), desc, cc, method, opts...)
}

func NewBasicAuthClient(username, password string) *BasicAuthClient {
return &BasicAuthClient{authKey: BuildAuthorization(username, password)}
}
Loading

0 comments on commit ed9095f

Please sign in to comment.