Я пытаюсь создать службу в Go, которая задерживает прямой эфир (socketio / signalR) на ~ 7 минут. Он также должен разрешать поток без задержки. Таким образом, у службы Go должно быть что-то вроде буфера или очереди, которая заставляет данные ждать в течение указанного времени, прежде чем они будут разрешены к использованию. Как бы вы сделали что-то подобное в Go? Будет ли отложенный поток отдельной горутиной? Какую структуру данных следует использовать для задержки данных?

Моя текущая идея - использовать пакет time для ожидания / отметки в течение 7 минут, прежде чем данные будут разрешены к использованию, но такое поведение блокировки может быть неоптимальным в этом сценарии.

Вот код, объясняющий, что я пытаюсь сделать. FakeStream - это фиктивная функция, которая имитирует данные в реальном времени, которые я получаю от внешней службы.

package main

import (
    "fmt"
    "time"
)

func DelayStream(input chan string, output chan string, delay string) {

    // not working for some reason
    // delayDuration, _ := time.ParseDuration(delay)
    // fmt.Println(delayDuration.Seconds())

    if delay == "5s" {
        fmt.Println("sleeping")
        time.Sleep(5 * time.Second)
    }
    data := <-input
    output <- data
}

func FakeStream(live chan string) {

    ticks := time.Tick(2 * time.Second)
    for now := range ticks {
        live <- fmt.Sprintf("%v", now.Format(time.UnixDate))
    }
}

func main() {
    liveData := make(chan string)
    delayedData := make(chan string)

    go FakeStream(liveData)
    go DelayStream(liveData, delayedData, "5s")

    for {
        select {
        case live := <-liveData:
            fmt.Println("live: ", live)
        case delayed := <-delayedData:
            fmt.Println("delayed: ", delayed)
        }
    }
}

По какой-то причине канал с задержкой выводит только один раз и не выводит ожидаемые данные. Он должен выводить первое, что есть в прямом эфире, но этого не происходит.

go
2
ninesalt 8 Мар 2019 в 14:34

1 ответ

Лучший ответ

Вам нужен буфер достаточного размера. В простых случаях может работать буферизованный канал Go.

Спросите себя - сколько данных нужно хранить во время этой задержки - у вас должен быть разумный верхний предел. Например, если ваш поток доставляет до N пакетов в секунду, то для задержки на 7 минут вам необходимо сохранить 420N пакетов.

Спросите себя - что произойдет, если в течение окна задержки поступит больше данных, чем ожидалось? Вы можете выбросить новые данные, выбросить старые данные или просто заблокировать входной поток. Что из этого возможно для вашего сценария? Каждое приводит к немного иному решению.

Спросите себя - как рассчитывается задержка? С момента создания потока? С момента прихода каждого пакета? Задержка для каждого пакета отдельно или только для первого пакета в потоке?

Вам нужно будет значительно сузить выбор дизайна здесь, чтобы разработать образец кода.

Для некоторого подмножества этих вариантов дизайна есть простой способ добавить задержку между каналами для каждого сообщения:


package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    // in is a channel of strings with a buffer size of 10
    in := make(chan string, 10)

    // out is an unbuffered channel
    out := make(chan string)

    // this goroutine forwards messages from in to out, ading a delay
    // to each message.
    const delay = 3 * time.Second
    go func() {
        for msg := range in {
            time.Sleep(delay)
            out <- msg
        }
        close(out)
    }()

    var wg sync.WaitGroup
    wg.Add(1)
    // this goroutine drains the out channel
    go func() {
        for msg := range out {
            fmt.Printf("Got '%s' at time %s\n", msg, time.Now().Format(time.Stamp))
        }
        wg.Done()
    }()

    // Send some messages into the in channel
    fmt.Printf("Sending '%s' at time %s\n", "joe", time.Now().Format(time.Stamp))
    in <- "joe"

    time.Sleep(2 * time.Second)
    fmt.Printf("Sending '%s' at time %s\n", "hello", time.Now().Format(time.Stamp))
    in <- "hello"

    time.Sleep(4 * time.Second)
    fmt.Printf("Sending '%s' at time %s\n", "bye", time.Now().Format(time.Stamp))
    in <- "bye"
    close(in)

    wg.Wait()
}
3
Eli Bendersky 8 Мар 2019 в 14:19