diff --git a/.github/workflows/docker-compose.yml b/.github/workflows/docker-compose.yml new file mode 100644 index 000000000..758de0dcb --- /dev/null +++ b/.github/workflows/docker-compose.yml @@ -0,0 +1,93 @@ +version: '2' + +services: + redis-master: + container_name: redis-master + image: 'bitnami/redis:latest' + environment: + - REDIS_REPLICATION_MODE=master + - REDIS_PASSWORD=111111 + ports: + - 6380:6379 + redis-slave1: + container_name: redis-slave1 + image: 'bitnami/redis:latest' + environment: + - REDIS_REPLICATION_MODE=slave + - REDIS_MASTER_HOST=redis-master + - REDIS_MASTER_PASSWORD=111111 + - REDIS_PASSWORD=111111 + ports: + - 6381:6379 + depends_on: + - redis-master + links: + - redis-master + redis-slave2: + container_name: redis-slave2 + image: 'bitnami/redis:latest' + environment: + - REDIS_REPLICATION_MODE=slave + - REDIS_MASTER_HOST=redis-master + - REDIS_MASTER_PASSWORD=111111 + - REDIS_PASSWORD=111111 + ports: + - 6382:6379 + depends_on: + - redis-master + links: + - redis-master + redis-sentinel-1: + container_name: redis-sentinel-1 + image: 'bitnami/redis-sentinel:latest' + environment: + - REDIS_MASTER_HOST=redis-master + - REDIS_MASTER_PORT_NUMBER=6379 + - REDIS_MASTER_PASSWORD=111111 + depends_on: + - redis-master + - redis-slave1 + - redis-slave2 + ports: + - 26379:26379 + links: + - redis-master + - redis-slave1 + - redis-slave2 + + redis-sentinel-2: + container_name: redis-sentinel-2 + image: 'bitnami/redis-sentinel:latest' + environment: + - REDIS_MASTER_HOST=redis-master + - REDIS_MASTER_PORT_NUMBER=6379 + - REDIS_MASTER_PASSWORD=111111 + depends_on: + - redis-master + - redis-slave1 + - redis-slave2 + links: + - redis-master + - redis-slave1 + - redis-slave2 + ports: + - 26380:26379 + + + redis-sentinel-3: + container_name: redis-sentinel-3 + image: 'bitnami/redis-sentinel:latest' + environment: + - REDIS_MASTER_HOST=redis-master + - REDIS_MASTER_PORT_NUMBER=6379 + - REDIS_MASTER_PASSWORD=111111 + depends_on: + - redis-master + - redis-slave1 + - redis-slave2 + ports: + - 26381:26379 + links: + - redis-master + - redis-slave1 + - redis-slave2 \ No newline at end of file diff --git a/.github/workflows/gf.yml b/.github/workflows/gf.yml index 72ba148de..63253acc9 100644 --- a/.github/workflows/gf.yml +++ b/.github/workflows/gf.yml @@ -132,6 +132,9 @@ jobs: with: go-version: ${{ matrix.go }} + - name: Start containers + run: docker-compose -f ".github/workflows/docker-compose.yml" up -d --build + - name: Before Script run: | find . -name "*.go" | xargs gofmt -w @@ -161,6 +164,9 @@ jobs: cd - done + - name: Stop containers + run: docker-compose -f ".github/workflows/docker-compose.yml" down + - name: Report Coverage uses: codecov/codecov-action@v2 with: diff --git a/database/gredis/gredis_adapter_goredis.go b/database/gredis/gredis_adapter_goredis.go index 28ac0ebf1..73f045f9b 100644 --- a/database/gredis/gredis_adapter_goredis.go +++ b/database/gredis/gredis_adapter_goredis.go @@ -11,8 +11,8 @@ import ( "time" "github.com/go-redis/redis/v8" - "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/text/gstr" ) @@ -34,7 +34,7 @@ const ( // NewAdapterGoRedis creates and returns a redis adapter using go-redis. func NewAdapterGoRedis(config *Config) *AdapterGoRedis { fillWithDefaultConfiguration(config) - client := redis.NewUniversalClient(&redis.UniversalOptions{ + opts := &redis.UniversalOptions{ Addrs: gstr.SplitAndTrim(config.Address, ","), Password: config.Pass, DB: config.Db, @@ -48,7 +48,20 @@ func NewAdapterGoRedis(config *Config) *AdapterGoRedis { WriteTimeout: config.WriteTimeout, MasterName: config.MasterName, TLSConfig: config.TLSConfig, - }) + } + + var client redis.UniversalClient + + if opts.MasterName != "" { + redisSentinel := opts.Failover() + redisSentinel.SlaveOnly = config.SlaveOnly + client = redis.NewFailoverClient(redisSentinel) + } else if len(opts.Addrs) > 1 { + client = redis.NewClusterClient(opts.Cluster()) + } else { + client = redis.NewClient(opts.Simple()) + } + return &AdapterGoRedis{ client: client, config: config, diff --git a/database/gredis/gredis_config.go b/database/gredis/gredis_config.go index d381332ff..3b7814237 100644 --- a/database/gredis/gredis_config.go +++ b/database/gredis/gredis_config.go @@ -36,6 +36,7 @@ type Config struct { TLS bool `json:"tls"` // Specifies whether TLS should be used when connecting to the server. TLSSkipVerify bool `json:"tlsSkipVerify"` // Disables server name verification when connecting over TLS. TLSConfig *tls.Config `json:"-"` // TLS Config to use. When set TLS will be negotiated. + SlaveOnly bool `json:"slaveOnly"` // Route all commands to slave read-only nodes. } const ( diff --git a/database/gredis/gredis_z_unit_conn_sentinel_test.go b/database/gredis/gredis_z_unit_conn_sentinel_test.go new file mode 100644 index 000000000..343f818eb --- /dev/null +++ b/database/gredis/gredis_z_unit_conn_sentinel_test.go @@ -0,0 +1,63 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gredis_test + +import ( + "context" + "testing" + + "github.com/gogf/gf/v2/database/gredis" + "github.com/gogf/gf/v2/test/gtest" +) + +var ( + sentinelCtx = context.TODO() + sentinelConfig = &gredis.Config{ + Address: `127.0.0.1:26379,127.0.0.1:26380,127.0.0.1:26381`, + MasterName: `mymaster`, + Pass: "111111", + } +) + +func TestConn_sentinel_master(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + sentinelConfig.SlaveOnly = false + redis, err := gredis.New(sentinelConfig) + t.AssertNil(err) + t.AssertNE(redis, nil) + defer redis.Close(sentinelCtx) + + conn, err := redis.Conn(sentinelCtx) + t.AssertNil(err) + defer conn.Close(sentinelCtx) + + _, err = conn.Do(sentinelCtx, "set", "test", "123") + t.AssertNil(err) + defer conn.Do(sentinelCtx, "del", "test") + + r, err := conn.Do(sentinelCtx, "get", "test") + t.AssertNil(err) + t.Assert(r.String(), "123") + }) +} + +func TestConn_sentinel_slave(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + sentinelConfig.SlaveOnly = true + redis, err := gredis.New(sentinelConfig) + t.AssertNil(err) + t.AssertNE(redis, nil) + defer redis.Close(sentinelCtx) + + conn, err := redis.Conn(sentinelCtx) + t.AssertNil(err) + defer conn.Close(sentinelCtx) + + _, err = conn.Do(sentinelCtx, "set", "test", "123") + t.AssertNQ(err, nil) + }) +}