feat: set buff_send as default
This commit is contained in:
parent
7675749b67
commit
f263dd4b7f
|
@ -8,8 +8,6 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"RedisShake/internal/client/proto"
|
"RedisShake/internal/client/proto"
|
||||||
|
@ -22,13 +20,6 @@ type Redis struct {
|
||||||
writer *bufio.Writer
|
writer *bufio.Writer
|
||||||
protoReader *proto.Reader
|
protoReader *proto.Reader
|
||||||
protoWriter *proto.Writer
|
protoWriter *proto.Writer
|
||||||
timer *time.Timer
|
|
||||||
sendBytes uint64
|
|
||||||
mu sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSentinelMasterClient(ctx context.Context, address string, username string, password string, Tls bool) *Redis {
|
|
||||||
return NewRedisClient(ctx, address, username, password, Tls, false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRedisClient(ctx context.Context, address string, username string, password string, Tls bool, replica bool) *Redis {
|
func NewRedisClient(ctx context.Context, address string, username string, password string, Tls bool, replica bool) *Redis {
|
||||||
|
@ -56,7 +47,7 @@ func NewRedisClient(ctx context.Context, address string, username string, passwo
|
||||||
|
|
||||||
r.conn = conn
|
r.conn = conn
|
||||||
r.reader = bufio.NewReader(conn)
|
r.reader = bufio.NewReader(conn)
|
||||||
r.writer = bufio.NewWriter(conn)
|
r.writer = bufio.NewWriterSize(conn, 16*1024*1024) // size is 16MB
|
||||||
r.protoReader = proto.NewReader(r.reader)
|
r.protoReader = proto.NewReader(r.reader)
|
||||||
r.protoWriter = proto.NewWriter(r.writer)
|
r.protoWriter = proto.NewWriter(r.writer)
|
||||||
|
|
||||||
|
@ -86,9 +77,6 @@ func NewRedisClient(ctx context.Context, address string, username string, passwo
|
||||||
r = NewRedisClient(ctx, replicaInfo.BestReplica, username, password, Tls, false)
|
r = NewRedisClient(ctx, replicaInfo.BestReplica, username, password, Tls, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
r.timer = time.NewTimer(time.Second)
|
|
||||||
go r.autoFlush(ctx)
|
|
||||||
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,71 +170,22 @@ func (r *Redis) Send(args ...interface{}) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panicf(err.Error())
|
log.Panicf(err.Error())
|
||||||
}
|
}
|
||||||
r.flush()
|
r.Flush()
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Redis) SendBytes(buf []byte) {
|
|
||||||
_, err := r.writer.Write(buf)
|
|
||||||
if err != nil {
|
|
||||||
log.Panicf(err.Error())
|
|
||||||
}
|
|
||||||
r.flush()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendBytesBuff send bytes to buffer, need to call Flush() to send the buffer
|
||||||
func (r *Redis) SendBytesBuff(buf []byte) {
|
func (r *Redis) SendBytesBuff(buf []byte) {
|
||||||
r.mu.Lock()
|
|
||||||
defer r.mu.Unlock()
|
|
||||||
_, err := r.writer.Write(buf)
|
_, err := r.writer.Write(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panicf(err.Error())
|
log.Panicf(err.Error())
|
||||||
}
|
}
|
||||||
r.flushBuff(len(buf))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Redis) resetTimer() {
|
func (r *Redis) Flush() {
|
||||||
if !r.timer.Stop() {
|
|
||||||
select {
|
|
||||||
case <-r.timer.C:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
r.timer.Reset(time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Redis) flushBuff(l int) {
|
|
||||||
// if the data size is too small, no need to flush
|
|
||||||
if atomic.AddUint64(&r.sendBytes, uint64(l)) > 64*1024 {
|
|
||||||
r.flush()
|
|
||||||
r.resetTimer()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r.resetTimer()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Redis) flush() {
|
|
||||||
err := r.writer.Flush()
|
err := r.writer.Flush()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panicf(err.Error())
|
log.Panicf(err.Error())
|
||||||
}
|
}
|
||||||
atomic.StoreUint64(&r.sendBytes, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Redis) autoFlush(ctx context.Context) {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
|
||||||
case <-r.timer.C:
|
|
||||||
if atomic.LoadUint64(&r.sendBytes) > 0 {
|
|
||||||
r.mu.Lock()
|
|
||||||
err := r.writer.Flush()
|
|
||||||
r.mu.Unlock()
|
|
||||||
if err != nil {
|
|
||||||
log.Panicf(err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Redis) Receive() (interface{}, error) {
|
func (r *Redis) Receive() (interface{}, error) {
|
||||||
|
@ -285,13 +224,6 @@ func (r *Redis) Close() {
|
||||||
if err := r.conn.Close(); err != nil {
|
if err := r.conn.Close(); err != nil {
|
||||||
log.Infof("close redis conn err: %s\n", err.Error())
|
log.Infof("close redis conn err: %s\n", err.Error())
|
||||||
}
|
}
|
||||||
// release the timer
|
|
||||||
if !r.timer.Stop() {
|
|
||||||
select {
|
|
||||||
case <-r.timer.C:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Commands */
|
/* Commands */
|
||||||
|
|
|
@ -24,7 +24,6 @@ type RedisWriterOptions struct {
|
||||||
Password string `mapstructure:"password" default:""`
|
Password string `mapstructure:"password" default:""`
|
||||||
Tls bool `mapstructure:"tls" default:"false"`
|
Tls bool `mapstructure:"tls" default:"false"`
|
||||||
OffReply bool `mapstructure:"off_reply" default:"false"`
|
OffReply bool `mapstructure:"off_reply" default:"false"`
|
||||||
BuffSend bool `mapstructure:"buff_send" default:"false"`
|
|
||||||
Sentinel client.SentinelOptions `mapstructure:"sentinel"`
|
Sentinel client.SentinelOptions `mapstructure:"sentinel"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,8 +38,6 @@ type redisStandaloneWriter struct {
|
||||||
ch chan *entry.Entry
|
ch chan *entry.Entry
|
||||||
chWg sync.WaitGroup
|
chWg sync.WaitGroup
|
||||||
|
|
||||||
buffSend bool
|
|
||||||
|
|
||||||
stat struct {
|
stat struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
UnansweredBytes int64 `json:"unanswered_bytes"`
|
UnansweredBytes int64 `json:"unanswered_bytes"`
|
||||||
|
@ -54,7 +51,6 @@ func NewRedisStandaloneWriter(ctx context.Context, opts *RedisWriterOptions) Wri
|
||||||
rw.stat.Name = "writer_" + strings.Replace(opts.Address, ":", "_", -1)
|
rw.stat.Name = "writer_" + strings.Replace(opts.Address, ":", "_", -1)
|
||||||
rw.client = client.NewRedisClient(ctx, opts.Address, opts.Username, opts.Password, opts.Tls, false)
|
rw.client = client.NewRedisClient(ctx, opts.Address, opts.Username, opts.Password, opts.Tls, false)
|
||||||
rw.ch = make(chan *entry.Entry, 1024)
|
rw.ch = make(chan *entry.Entry, 1024)
|
||||||
rw.buffSend = opts.BuffSend
|
|
||||||
if opts.OffReply {
|
if opts.OffReply {
|
||||||
log.Infof("turn off the reply of write")
|
log.Infof("turn off the reply of write")
|
||||||
rw.offReply = true
|
rw.offReply = true
|
||||||
|
@ -79,31 +75,38 @@ func (w *redisStandaloneWriter) Close() {
|
||||||
func (w *redisStandaloneWriter) StartWrite(ctx context.Context) chan *entry.Entry {
|
func (w *redisStandaloneWriter) StartWrite(ctx context.Context) chan *entry.Entry {
|
||||||
w.chWg = sync.WaitGroup{}
|
w.chWg = sync.WaitGroup{}
|
||||||
w.chWg.Add(1)
|
w.chWg.Add(1)
|
||||||
|
timer := time.NewTicker(100 * time.Millisecond)
|
||||||
go func() {
|
go func() {
|
||||||
for e := range w.ch {
|
for {
|
||||||
// switch db if we need
|
select {
|
||||||
if w.DbId != e.DbId {
|
case <-ctx.Done():
|
||||||
w.switchDbTo(e.DbId)
|
// do nothing until w.ch is closed
|
||||||
}
|
case <-timer.C:
|
||||||
// send
|
w.client.Flush()
|
||||||
bytes := e.Serialize()
|
case e, ok := <-w.ch:
|
||||||
for e.SerializedSize+atomic.LoadInt64(&w.stat.UnansweredBytes) > config.Opt.Advanced.TargetRedisClientMaxQuerybufLen {
|
if !ok {
|
||||||
time.Sleep(1 * time.Nanosecond)
|
w.client.Flush()
|
||||||
}
|
w.chWg.Done()
|
||||||
log.Debugf("[%s] send cmd. cmd=[%s]", w.stat.Name, e.String())
|
return
|
||||||
if !w.offReply {
|
}
|
||||||
w.chWaitReply <- e
|
// switch db if we need
|
||||||
atomic.AddInt64(&w.stat.UnansweredBytes, e.SerializedSize)
|
if w.DbId != e.DbId {
|
||||||
atomic.AddInt64(&w.stat.UnansweredEntries, 1)
|
w.switchDbTo(e.DbId)
|
||||||
}
|
}
|
||||||
if w.buffSend {
|
// send
|
||||||
|
bytes := e.Serialize()
|
||||||
|
for e.SerializedSize+atomic.LoadInt64(&w.stat.UnansweredBytes) > config.Opt.Advanced.TargetRedisClientMaxQuerybufLen {
|
||||||
|
time.Sleep(1 * time.Nanosecond)
|
||||||
|
}
|
||||||
|
log.Debugf("[%s] send cmd. cmd=[%s]", w.stat.Name, e.String())
|
||||||
|
if !w.offReply {
|
||||||
|
w.chWaitReply <- e
|
||||||
|
atomic.AddInt64(&w.stat.UnansweredBytes, e.SerializedSize)
|
||||||
|
atomic.AddInt64(&w.stat.UnansweredEntries, 1)
|
||||||
|
}
|
||||||
w.client.SendBytesBuff(bytes)
|
w.client.SendBytesBuff(bytes)
|
||||||
} else {
|
|
||||||
w.client.SendBytes(bytes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
w.chWg.Done()
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return w.ch
|
return w.ch
|
||||||
|
|
|
@ -34,7 +34,6 @@ username = "" # keep empty if not using ACL
|
||||||
password = "" # keep empty if no authentication is required
|
password = "" # keep empty if no authentication is required
|
||||||
tls = false
|
tls = false
|
||||||
off_reply = false # turn off the server reply
|
off_reply = false # turn off the server reply
|
||||||
buff_send = false # buffer send, default false. may be a sync delay when true, but it can greatly improve the speed
|
|
||||||
|
|
||||||
[filter]
|
[filter]
|
||||||
# Allow keys with specific prefixes or suffixes
|
# Allow keys with specific prefixes or suffixes
|
||||||
|
|
Loading…
Reference in New Issue