简体   繁体   English

为什么数据被推入通道但从未从接收器 goroutine 中读取?

[英]Why is data being pushed into the channel but never read from the receiver goroutine?

I am building a daemon and I have two services that will be sending data to and from each other.我正在构建一个守护进程,我有两个服务将相互发送数据。 Service A is what produces the data and service B a is Data Buffer service or like a queue.服务 A 产生数据,服务 B a 是数据缓冲区服务或类似队列。 So from the main.go file, service B is instantiated and started.所以从main.go文件中,服务 B 被实例化并启动。 The Start() method will perform the buffer() function as a goroutine because this function waits for data to be passed onto a channel and I don't want the main process to halt waiting for buffer to complete. Start()方法将作为 goroutine 执行buffer()函数,因为该函数等待数据传递到通道上,我不希望主进程停止等待buffer完成。 Then Service A is instantiated and started.然后服务 A 被实例化并启动。 It is then also "registered" with Service B.然后它也向服务 B“注册”。

I created a method called RegisterWithBufferService for Service A that creates two new channels.我为服务 A 创建了一个名为RegisterWithBufferService的方法,该方法创建了两个新通道。 It will store those channels as it's own attributes and also provide them to Service B.它将这些通道存储为它自己的属性,并将它们提供给服务 B。

func (s *ServiceA) RegisterWithBufferService(bufService *data.DataBuffer) error {
    newIncomingChan := make(chan *data.DataFrame, 1)
    newOutgoingChan := make(chan []byte, 1)
    s.IncomingBuffChan = newIncomingChan
    s.OutgoingDataChannels = append(s.OutgoingDataChannels, newOutgoingChan)
    bufService.DataProviders[s.ServiceName()] = data.DataProviderInfo{
        IncomingChan: newOutgoingChan, //our outGoing channel is their incoming
        OutgoingChan: newIncomingChan, // our incoming channel is their outgoing
    }
    s.DataBufferService = bufService
    bufService.NewProvider <- s.ServiceName() //The DataBuffer service listens for new services and creates a new goroutine for buffering
    s.Logger.Info().Msg("Registeration completed.")
    return nil
}

Buffer essentially listens for incoming data from Service A, decodes it using Decode() and then adds it to a slice called buf . Buffer 本质上侦听来自服务 A 的传入数据,使用Decode()对其进行Decode() ,然后将其添加到名为buf的切片中。 If the slice is greater in length than bufferPeriod then it will send the first item in the slice in the Outgoing channel back to Service A.如果切片的长度大于bufferPeriod那么它会将 Outgoing 通道中切片中的第一项发送回服务 A。

func (b* DataBuffer) buffer(bufferPeriod int) {
    for {
        select {
        case newProvider := <- b.NewProvider:
            b.wg.Add(1)
            /*
            newProvider is a string
            DataProviders is a map the value it returns is a struct containing the Incoming and 
            Outgoing channels for this service
            */
            p := b.DataProviders[newProvider]
            go func(prov string, in chan []byte, out chan *DataFrame) {
                defer b.wg.Done()
                var buf []*DataFrame
                for {
                    select {
                    case rawData := <-in:
                        tmp := Decode(rawData) //custom decoding function. Returns a *DataFrame
                        buf = append(buf, tmp)
                        if len(buf) < bufferPeriod {
                            b.Logger.Info().Msg("Sending decoded data out.")
                            out <- buf[0]
                            buf = buf[1:] //pop
                        }
                    case <- b.Quit:
                        return
                    }
                }
            }(newProvider, p.IncomingChan, p.OutgoingChan)
        }
    case <- b.Quit:
        return
    }
}

Now Service A has a method called record that will periodically push data to all the channels in it's OutgoingDataChannels attribute.现在服务 A 有一个名为record的方法,它会定期将数据推送到它的OutgoingDataChannels属性中的所有通道。

func (s *ServiceA) record() error {
    ...
    if atomic.LoadInt32(&s.Listeners) != 0 {
        s.Logger.Info().Msg("Sending raw data to data buffer")
        for _, outChan := range s.OutgoingDataChannels {
            outChan <- dataBytes // the receiver (Service B) is already listening and this doesn't hang
        }
        s.Logger.Info().Msg("Raw data sent and received") // The logger will output this so I know it's not hanging 
    }
}

The problem is that Service A seems to push the data successfully using record but Service B never goes into the case rawData := <-in: case in the buffer sub-goroutine.问题是服务 A 似乎使用record成功推送数据,但服务 B 从未进入case rawData := <-in: case in the buffer sub-goroutine。 Is this because I have nested goroutines?这是因为我嵌套了 goroutines 吗? Incase it's not clear, when Service B is started, it calls buffer but because it would hang otherwise, I made the call to buffer a goroutine.如果不清楚,当服务 B 启动时,它调用buffer但因为否则它会挂起,我调用了一个 goroutine 来buffer So then when Service A calls RegisterWithBufferService , the buffer goroutine creates a goroutine to listen for new data from Service B and push it back to Service A once the buffer is filled.因此,当服务 A 调用RegisterWithBufferServicebuffer goroutine 创建一个 goroutine 来侦听来自服务 B 的新数据,并在缓冲区填满后将其推回服务 A。 I hope I explained it clearly.我希望我解释清楚。

EDIT 1 I've made a minimal, reproducible example.编辑 1我做了一个最小的、可重现的例子。

package main

import (
    "fmt"
    "sync"
    "sync/atomic"
    "time"
)

var (
    defaultBufferingPeriod int = 3
    DefaultPollingInterval int64 = 10
)

type DataObject struct{
    Data    string
}

type DataProvider interface {
    RegisterWithBufferService(*DataBuffer) error
    ServiceName() string
}

type DataProviderInfo struct{
    IncomingChan    chan *DataObject
    OutgoingChan    chan *DataObject
}

type DataBuffer struct{
    Running         int32 //used atomically
    DataProviders   map[string]DataProviderInfo
    Quit            chan struct{}
    NewProvider     chan string
    wg              sync.WaitGroup
}

func NewDataBuffer() *DataBuffer{
    var (
        wg sync.WaitGroup
    )
    return &DataBuffer{
        DataProviders: make(map[string]DataProviderInfo),
        Quit: make(chan struct{}),
        NewProvider: make(chan string),
        wg: wg,
    }
}

func (b *DataBuffer) Start() error {
    if ok := atomic.CompareAndSwapInt32(&b.Running, 0, 1); !ok {
        return fmt.Errorf("Could not start Data Buffer Service.")
    }
    go b.buffer(defaultBufferingPeriod)
    return nil
}

func (b *DataBuffer) Stop() error {
    if ok := atomic.CompareAndSwapInt32(&b.Running, 1, 0); !ok {
        return fmt.Errorf("Could not stop Data Buffer Service.")
    }
    for _, p := range b.DataProviders {
        close(p.IncomingChan)
        close(p.OutgoingChan)
    }
    close(b.Quit)
    b.wg.Wait()
    return nil
}

// buffer creates goroutines for each incoming, outgoing data pair and decodes the incoming bytes into outgoing DataFrames
func (b *DataBuffer) buffer(bufferPeriod int) {
    for {
        select {
        case newProvider := <- b.NewProvider:
            fmt.Println("Received new Data provider.")
            if _, ok := b.DataProviders[newProvider]; ok { 
                b.wg.Add(1)
                p := b.DataProviders[newProvider]
                go func(prov string, in chan *DataObject, out chan *DataObject) {
                    defer b.wg.Done()
                    var (
                        buf []*DataObject
                    )
                    fmt.Printf("Waiting for data from: %s\n", prov)
                    for {
                        select {
                        case inData := <-in:
                            fmt.Printf("Received data from: %s\n", prov)
                            buf = append(buf, inData)
                            if len(buf) > bufferPeriod {
                                fmt.Printf("Queue is filled, sending data back to %s\n", prov)
                                out <- buf[0]
                                fmt.Println("Data Sent")
                                buf = buf[1:] //pop
                            }
                        case <- b.Quit:
                            return
                        }
                    }
                }(newProvider, p.IncomingChan, p.OutgoingChan)
            }
        case <- b.Quit:
            return
        }
    }
}

type ServiceA struct{
    Active                  int32 // atomic
    Stopping                int32 // atomic
    Recording               int32 // atomic
    Listeners               int32 // atomic
    name                    string
    QuitChan                chan struct{}
    IncomingBuffChan        chan *DataObject
    OutgoingBuffChans       []chan *DataObject
    DataBufferService       *DataBuffer
}

// A compile time check to ensure ServiceA fully implements the DataProvider interface
var _ DataProvider = (*ServiceA)(nil)

func NewServiceA() (*ServiceA, error) {
    var newSliceOutChans []chan *DataObject
    return &ServiceA{
        QuitChan:  make(chan struct{}),
        OutgoingBuffChans: newSliceOutChans,
        name:   "SERVICEA",
    }, nil
}

// Start starts the service. Returns an error if any issues occur
func (s *ServiceA) Start() error {
    atomic.StoreInt32(&s.Active, 1)
    return nil
}

// Stop stops the service. Returns an error if any issues occur
func (s *ServiceA) Stop() error {
    atomic.StoreInt32(&s.Stopping, 1)
    close(s.QuitChan)
    return nil
}

func (s *ServiceA) StartRecording(pol_int int64) error {
    if ok := atomic.CompareAndSwapInt32(&s.Recording, 0, 1); !ok {
        return fmt.Errorf("Could not start recording. Data recording already started")
    }
    ticker := time.NewTicker(time.Duration(pol_int) * time.Second)
    go func() {
        for {
            select {
            case <-ticker.C:
                fmt.Println("Time to record...")
                err := s.record()
                if err != nil {
                    return
                }
            case <-s.QuitChan:
                ticker.Stop()
                return
            }
        }
    }()
    return nil
}

func (s *ServiceA) record() error {
    current_time := time.Now()
    ct := fmt.Sprintf("%02d-%02d-%d", current_time.Day(), current_time.Month(), current_time.Year())
    dataObject := &DataObject{
        Data: ct,
    }
    if atomic.LoadInt32(&s.Listeners) != 0 {
        fmt.Println("Sending data to Data buffer...")
        for _, outChan := range s.OutgoingBuffChans {
            outChan <- dataObject // the receivers should already be listening
        }
        fmt.Println("Data sent.")
    }
    return nil
}

// RegisterWithBufferService satisfies the DataProvider interface. It provides the bufService with new incoming and outgoing channels along with a polling interval
func (s ServiceA) RegisterWithBufferService(bufService *DataBuffer) error {
    if _, ok := bufService.DataProviders[s.ServiceName()]; ok {
        return fmt.Errorf("%v data provider already registered with Data Buffer.", s.ServiceName())
    }
    newIncomingChan := make(chan *DataObject, 1)
    newOutgoingChan := make(chan *DataObject, 1)
    s.IncomingBuffChan = newIncomingChan
    s.OutgoingBuffChans = append(s.OutgoingBuffChans, newOutgoingChan)
    bufService.DataProviders[s.ServiceName()] = DataProviderInfo{
        IncomingChan: newOutgoingChan, //our outGoing channel is their incoming
        OutgoingChan: newIncomingChan, // our incoming channel is their outgoing
    }
    s.DataBufferService = bufService
    bufService.NewProvider <- s.ServiceName() //The DataBuffer service listens for new services and creates a new goroutine for buffering
    return nil
}

// ServiceName satisfies the DataProvider interface. It returns the name of the service.
func (s ServiceA) ServiceName() string {
    return s.name
}

func main() {
    var BufferedServices []DataProvider
    fmt.Println("Instantiating and Starting Data Buffer Service...")
    bufService := NewDataBuffer()
    err := bufService.Start()
    if err != nil {
        panic(fmt.Sprintf("%v", err))
    }
    defer bufService.Stop()
    fmt.Println("Data Buffer Service successfully started.")

    fmt.Println("Instantiating and Starting Service A...")
    serviceA, err := NewServiceA()
    if err != nil {
        panic(fmt.Sprintf("%v", err))
    }
    BufferedServices = append(BufferedServices, *serviceA)
    err = serviceA.Start()
    if err != nil {
        panic(fmt.Sprintf("%v", err))
    }
    defer serviceA.Stop()
    fmt.Println("Service A successfully started.")

    fmt.Println("Registering services with Data Buffer...")
    for _, s := range BufferedServices {
        _ = s.RegisterWithBufferService(bufService) // ignoring error msgs for base case
    }
    fmt.Println("Registration complete.")

    fmt.Println("Beginning recording...")
    _ = atomic.AddInt32(&serviceA.Listeners, 1)
    err = serviceA.StartRecording(DefaultPollingInterval)
    if err != nil {
        panic(fmt.Sprintf("%v", err))
    }
    for {
        select {
        case RTD := <-serviceA.IncomingBuffChan:
            fmt.Println(RTD)
        case <-serviceA.QuitChan:
            atomic.StoreInt32(&serviceA.Listeners, 0)
            bufService.Quit<-struct{}{}
        }
    }
}

Running on Go 1.17.在 Go 上运行 1.17。 When running the example, it should print the following every 10 seconds:运行示例时,它应该每 10 秒打印一次:

Time to record...
Sending data to Data buffer...
Data sent.

But then Data buffer never goes into the inData := <-in case.但是数据缓冲区永远不会进入inData := <-in case。

To diagnose this I changed fmt.Println("Sending data to Data buffer...") to fmt.Println("Sending data to Data buffer...", s.OutgoingBuffChans) and the output was:为了诊断这一点,我将fmt.Println("Sending data to Data buffer...")更改为fmt.Println("Sending data to Data buffer...", s.OutgoingBuffChans) ,输出为:

Time to record...
Sending data to Data buffer... []

So you are not actually sending the data to any channels.因此,您实际上并未将数据发送到任何通道。 The reason for this is:这样做的原因是:

func (s ServiceA) RegisterWithBufferService(bufService *DataBuffer) error {

As the receiver is not a pointer when you do the s.OutgoingBuffChans = append(s.OutgoingBuffChans, newOutgoingChan) you are changing s.OutgoingBuffChans in a copy of the ServiceA which is discarded when the function exits.由于在执行s.OutgoingBuffChans = append(s.OutgoingBuffChans, newOutgoingChan)时接收者不是指针s.OutgoingBuffChans = append(s.OutgoingBuffChans, newOutgoingChan)您正在更改ServiceA副本中的s.OutgoingBuffChans ,该副本在函数退出时被丢弃。 To fix this change:要修复此更改:

func (s ServiceA) RegisterWithBufferService(bufService *DataBuffer) error {

to

func (s *ServiceA) RegisterWithBufferService(bufService *DataBuffer) error {

and

BufferedServices = append(BufferedServices, *serviceA)

to

BufferedServices = append(BufferedServices, serviceA)

The amended version outputs:修改后的版本输出:

Time to record...
Sending data to Data buffer... [0xc0000d8060]
Data sent.
Received data from: SERVICEA
Time to record...
Sending data to Data buffer... [0xc0000d8060]
Data sent.
Received data from: SERVICEA

So this resolves the reported issue (I would not be suprised if there are other issues but hopefully this points you in the right direction).因此,这解决了报告的问题(如果还有其他问题,我不会感到惊讶,但希望这可以为您指明正确的方向)。 I did notice that the code you originally posted does use a pointer receiver so that might have suffered from another issue (but its difficult to comment on code fragments in a case like this).我确实注意到您最初发布的代码确实使用了指针接收器,因此可能会遇到另一个问题(但在这种情况下很难对代码片段进行评论)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM