web-dev-qa-db-ja.com

spark=ストリーミングでブロードキャスト変数を更新するにはどうすればよいですか?

spark=ストリーミング:

いくつかの参照データに基づいてフィルタリングしたいオブジェクトのストリームがあります

最初は、これをBroadcast Variableを使用して達成するのは非常に簡単なことだと思っていました。

public void startSparkEngine {
    Broadcast<ReferenceData> refdataBroadcast
      = sparkContext.broadcast(getRefData());

    final JavaDStream<MyObject> filteredStream = objectStream.filter(obj -> {
        final ReferenceData refData = refdataBroadcast.getValue();
        return obj.getField().equals(refData.getField());
    }

    filteredStream.foreachRDD(rdd -> {
        rdd.foreach(obj -> {
            // Final processing of filtered objects
        });
        return null;
    });
}

ただし、まれではありますが、参照データは定期的に変更されます

私は、ドライバーで変数を変更してre-broadcastでき、各ワーカーに伝播されるという印象を受けましたが、BroadcastオブジェクトはSerializableではなく、finalである必要があります。

どのような選択肢がありますか?私が考えることができる3つの解決策は次のとおりです。

  1. 参照データルックアップをforEachPartitionまたはforEachRddに移動して、完全にワーカー上に存在するようにします。ただし、参照データはREST APIの内側にあるため、何らかの方法でタイマー/カウンターを保存して、ストリーム内のすべての要素に対してアクセスされるリモートを停止する必要があります。

  2. 新しいブロードキャスト変数を使用して、refdataが変更されるたびにSparkコンテキストを再起動します。

  3. 参照データを[〜#〜] rdd [〜#〜]に変換してから、次のような方法でjoinストリームストリーミング中ですPair<MyObject, RefData>、ただしこれはすべてのオブジェクトに参照データを送信します。

28
Andrew Stubbs

@Rohan Alettyによる回答の拡張。以下は、ttlに基づいてブロードキャスト変数を更新するBroadcastWrapperのサンプルコードです。

public class BroadcastWrapper {

    private Broadcast<ReferenceData> broadcastVar;
    private Date lastUpdatedAt = Calendar.getInstance().getTime();

    private static BroadcastWrapper obj = new BroadcastWrapper();

    private BroadcastWrapper(){}

    public static BroadcastWrapper getInstance() {
        return obj;
    }

    public JavaSparkContext getSparkContext(SparkContext sc) {
       JavaSparkContext jsc = JavaSparkContext.fromSparkContext(sc);
       return jsc;
    }

    public Broadcast<ReferenceData> updateAndGet(SparkContext sparkContext){
        Date currentDate = Calendar.getInstance().getTime();
        long diff = currentDate.getTime()-lastUpdatedAt.getTime();
        if (var == null || diff > 60000) { //Lets say we want to refresh every 1 min = 60000 ms
            if (var != null)
               var.unpersist();
            lastUpdatedAt = new Date(System.currentTimeMillis());

            //Your logic to refresh
            ReferenceData data = getRefData();

            var = getSparkContext(sparkContext).broadcast(data);
       }
       return var;
   }
}

コードは次のようになります。

public void startSparkEngine() {

    final JavaDStream<MyObject> filteredStream = objectStream.transform(stream -> {
        Broadcast<ReferenceData> refdataBroadcast = BroadcastWrapper.getInstance().updateAndGet(stream.context());

        stream.filter(obj -> obj.getField().equals(refdataBroadcast.getValue().getField()));
    });

    filteredStream.foreachRDD(rdd -> {
        rdd.foreach(obj -> {
        // Final processing of filtered objects
        });
        return null;
    });
}

これはマルチクラスターでも機能しました。お役に立てれば

23
Aastha

ストリーミングアプリケーションを扱っているほぼすべての人が、参照データ(DB、ファイルなど)をストリーミングデータに織り込む(フィルター、ルックアップなど)方法が必要です。 2つの部分全体の部分的な解決策があります

  1. ストリーミング操作で使用される参照参照データ

    • 目的のキャッシュTTLでCacheLookupオブジェクトを作成します
    • ブロードキャストでラップ
    • ストリーミングロジックの一部としてCacheLookupを使用します

次の場合を除き、ほとんどの場合、これは正常に機能します。

  1. 参照データを更新する

    これらのスレッドの提案にもかかわらず、これを達成する決定的な方法はありません。つまり、前のブロードキャスト変数を削除して新しい変数を作成します。これらの操作の間に予想されるもののような複数の未知数。

これは非常に一般的なニーズであるため、更新を通知するブロードキャスト変数に情報を送信する方法があれば役立ちます。それにより、「CacheLookup」のローカルキャッシュを無効にすることができます。

問題の2番目の部分はまだ解決されていません。これに対する実行可能なアプローチがある場合、私は興味があります

6
Ravi Reddy

すでにこれを試したかどうかはわかりませんが、SparkContextをシャットダウンせずにブロードキャスト変数の更新を達成できると思います。 unpersist() メソッドを使用すると、ブロードキャスト変数のコピーが各エグゼキューターで削除され、再度アクセスするには変数を再ブロードキャストする必要があります。ユースケースでは、ブロードキャストを更新するときに次のことができます。

  1. エグゼキューターが現在の一連のデータで終了するのを待ちます

  2. ブロードキャスト変数を保持しない

  3. ブロードキャスト変数を更新する

  4. エグゼキュータに新しい参照データを送信するための再ブロードキャスト

この投稿 からかなりの量を集めていますが、最後に返信した人は、ローカルで機能していると主張しています。エグゼキュータが古いデータを確実に削除できるようにするために、おそらくアンパーシストでtrueにブロッキングを設定することに注意することが重要です(そのため、次の反復で古い値が再び読み取られません) 。

3
Rohan Aletty

これに最近直面した問題。 scala users ..

BroadCastWrapperを行うScalaの方法は以下の例のようになります。

import Java.io.{ ObjectInputStream, ObjectOutputStream }
import org.Apache.spark.broadcast.Broadcast
import org.Apache.spark.streaming.StreamingContext
import scala.reflect.ClassTag

/* wrapper lets us update brodcast variables within DStreams' foreachRDD
 without running into serialization issues */
case class BroadcastWrapper[T: ClassTag](
 @transient private val ssc: StreamingContext,
  @transient private val _v: T) {

  @transient private var v = ssc.sparkContext.broadcast(_v)

  def update(newValue: T, blocking: Boolean = false): Unit = {

    v.unpersist(blocking)
    v = ssc.sparkContext.broadcast(newValue)
  }

  def value: T = v.value

  private def writeObject(out: ObjectOutputStream): Unit = {
    out.writeObject(v)
  }

  private def readObject(in: ObjectInputStream): Unit = {
    v = in.readObject().asInstanceOf[Broadcast[T]]
  }
}

更新関数を呼び出して新しいブロードキャスト変数を取得する必要があるたびに。

3
Ram Ghadiyaram