Using the blocking mechanism of chan to realize the start, blocking and return controller of the cooperation process

I. use scenarios

The background is to read oplog from kafka for incremental processing, but when I want to send a command to block the incremental process, and then start a full synchronization, I will continue incremental processing.

Therefore, multiple processes need to be controlled.

II. Use knowledge

1. Reading from an uninitialized pipeline will block

2. Reading from a closed pipe will not block

Control with two pipes and select

III. code above

Controller code

package util

import (
	"errors"
	"sync"
)

const (
	//STOP stop
	STOP = iota
	//START start
	START
	//PAUSE pause
	PAUSE
)

//Control controller
type Control struct {
	ch1  chan struct{}
	ch2  chan struct{}
	stat int64
	lock sync.RWMutex
}

var (
	//ErrStat error status
	ErrStat = errors.New("stat error")
)

//NewControl gets a new Control
func NewControl() *Control {
	return &Control{
		ch1:  make(chan struct{}),
		ch2:  nil,
		stat: START,
		lock: sync.RWMutex{},
	}
}

//Stop stop
func (c *Control) Stop() error {
	c.lock.Lock()
	defer c.lock.Unlock()
	if c.stat == START {
		c.ch2 = nil
		close(c.ch1)
		c.stat = STOP
	} else if c.stat == PAUSE {
		ch2 := c.ch2
		c.ch2 = nil
		close(c.ch1)
		close(ch2)
		c.stat = STOP
	} else {
		return ErrStat
	}
	return nil
}

//Pause pause
func (c *Control) Pause() error {
	c.lock.Lock()
	defer c.lock.Unlock()
	if c.stat == START {
		c.ch2 = make(chan struct{})
		close(c.ch1)
		c.stat = PAUSE
	} else {
		return ErrStat
	}
	return nil
}

//Start start
func (c *Control) Start() error {
	c.lock.Lock()
	defer c.lock.Unlock()
	if c.stat == PAUSE {
		c.ch1 = make(chan struct{})
		close(c.ch2)
		c.stat = START
	} else {
		return ErrStat
	}
	return nil
}

//C control pipe
func (c *Control) C() <-chan struct{} {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.ch1
}

//Wait waiting
func (c *Control) Wait() bool {
	c.lock.RLock()
	ch2 := c.ch2
	c.lock.RUnlock()
	if ch2 == nil {  //Send stop push command by assigning nil
		return false
	}
	<-ch2  //Will block
	return true
}

 

Use code

	for {
		select {
		case part, ok := <-c.Partitions():
			if !ok {
				conf.Logger.Error("get kafka Partitions not ok", regular.Name)
				return
			}
			go readFromPart(c, part, regular, respChan)
		case <-regular.C():   //regular is a Control class
			if !regular.Wait() {
				conf.Logger.Debug("Stop! ")
				return
			}
			conf.Logger.Debug("Start! ")
		}
	}

In this way, you can control the cooperation in the project anytime and anywhere

regular  := util.NewControl()
regular.Pause()
regular.Start()
regular.Stop()

Tags: Go kafka

Posted on Tue, 03 Dec 2019 20:43:41 -0500 by renojim