Проблема параллелизма в облачном потоке данных с использованием Go SDK - PullRequest
1 голос
/ 11 марта 2019

У меня есть реализация кода Apache Beam на Go SDK, как описано ниже. Трубопровод имеет 3 ступени. Один - textio.Read, другой - CountLines, а последний шаг - ProcessLines. ProcessLines шаг занимает около 10 секунд. Я просто добавил функцию Sleep для краткости.

Я звоню в трубопровод с 20 работниками. Когда я запускал конвейер, я ожидал, что 20 рабочих будут работать параллельно, а textio.Read будет читать 20 строк из файла, а ProcessLines будет выполнять 20 параллельных выполнений за 10 секунд. Однако конвейер не работал так. В настоящее время он работает таким образом, что textio.Read читает одну строку из файла, передает данные на следующий шаг и ожидает, пока шаг ProcessLines не завершит свою 10-секундную работу. Нет параллелизма, и есть только одна строка строки из файла по всему конвейеру. Не могли бы вы объяснить мне, что я делаю неправильно для параллелизма? Как мне обновить код для достижения параллелизма, как описано выше?

package main

import (
    "context"
    "flag"
    "time"

    "github.com/apache/beam/sdks/go/pkg/beam"
    "github.com/apache/beam/sdks/go/pkg/beam/io/textio"
    "github.com/apache/beam/sdks/go/pkg/beam/log"
    "github.com/apache/beam/sdks/go/pkg/beam/x/beamx"
)

// metrics to be monitored
var (
    input         = flag.String("input", "", "Input file (required).")
    numberOfLines = beam.NewCounter("extract", "numberOfLines")
    lineLen       = beam.NewDistribution("extract", "lineLenDistro")
)

func countLines(ctx context.Context, line string) string {
    lineLen.Update(ctx, int64(len(line)))
    numberOfLines.Inc(ctx, 1)

    return line
}

func processLines(ctx context.Context, line string) {
    time.Sleep(10 * time.Second)
}

func CountLines(s beam.Scope, lines beam.PCollection) beam.PCollection 
{
    s = s.Scope("Count Lines")

    return beam.ParDo(s, countLines, lines)
}

func ProcessLines(s beam.Scope, lines beam.PCollection) {
    s = s.Scope("Process Lines")

    beam.ParDo0(s, processLines, lines)
}

func main() {
    // If beamx or Go flags are used, flags must be parsed first.
    flag.Parse()
    // beam.Init() is an initialization hook that must be called on startup. On
    // distributed runners, it is used to intercept control.
    beam.Init()

    // Input validation is done as usual. Note that it must be after Init().
    if *input == "" {
        log.Fatal(context.Background(), "No input file provided")
    }

    p := beam.NewPipeline()
    s := p.Root()

    l := textio.Read(s, *input)
    lines := CountLines(s, l)
    ProcessLines(s, lines)

    // Concept #1: The beamx.Run convenience wrapper allows a number of
    // pre-defined runners to be used via the --runner flag.
    if err := beamx.Run(context.Background(), p); err != nil {
        log.Fatalf(context.Background(), "Failed to execute job: %v", err.Error())
    }
}

EDIT:

После того, как я получил ответ о проблеме, которая может быть вызвана слиянием, я изменил соответствующую часть кода, но он снова не работал.

Теперь первый и второй шаг работают параллельно, однако третий шаг ProcessLines не работает параллельно. Я только сделал следующие изменения. Может кто-нибудь сказать мне, в чем проблема?

func AddRandomKey(s beam.Scope, col beam.PCollection) beam.PCollection {
    return beam.ParDo(s, addRandomKeyFn, col)
}

func addRandomKeyFn(elm beam.T) (int, beam.T) {
    return rand.Int(), elm
}

func countLines(ctx context.Context, _ int, lines func(*string) bool, emit func(string)) {
    var line string
    for lines(&line) {
        lineLen.Update(ctx, int64(len(line)))
        numberOfLines.Inc(ctx, 1)
        emit(line)
    }
}
func processLines(ctx context.Context, _ int, lines func(*string) bool) {
    var line string
    for lines(&line) {
        time.Sleep(10 * time.Second)
        numberOfLinesProcess.Inc(ctx, 1)
    }
}

func CountLines(s beam.Scope, lines beam.PCollection) beam.PCollection {
    s = s.Scope("Count Lines")
    keyed := AddRandomKey(s, lines)
    grouped := beam.GroupByKey(s, keyed)

    return beam.ParDo(s, countLines, grouped)
}

func ProcessLines(s beam.Scope, lines beam.PCollection) {
    s = s.Scope("Process Lines")
    keyed := AddRandomKey(s, lines)
    grouped := beam.GroupByKey(s, keyed)

    beam.ParDo0(s, processLines, grouped)
}

1 Ответ

0 голосов
/ 11 марта 2019

Многие продвинутые участники конвейеров типа MapReduce объединяют стадии, которые можно запускать в памяти вместе.Apache Beam и Dataflow не являются исключением.

То, что здесь происходит, состоит в том, что три шага вашего конвейера слиты и происходят на одной машине.Кроме того, Go SDK в настоящее время не поддерживает разбиение преобразования Read, к сожалению.

Для достижения параллелизма в третьем преобразовании вы можете разбить слияние между Read и ProcessLines.Вы можете сделать это, добавив случайный ключ к своим строкам и преобразовав GroupByKey.

В Python это будет:

(p | beam.ReadFromText(...)
   | CountLines()
   | beam.Map(lambda x: (random.randint(0, 1000), x))
   | beam.GroupByKey()
   | beam.FlatMap(lambda k, v: v)  # Discard the key, and return the values
   | ProcessLines())

Это позволит вам распараллелить ProcessLines.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...