web-dev-qa-db-ja.com

S3から複数のファイルを並行して読み取る(Spark、Java)

私はこれについていくつかの議論を見ましたが、正しい解決策を完全に理解することができませんでした:S3からRDDに数百のファイルをロードしたいと思います。これが私が今それをやっている方法です:

_ObjectListing objectListing = s3.listObjects(new ListObjectsRequest().
                withBucketName(...).
                withPrefix(...));
List<String> keys = new LinkedList<>();
objectListing.getObjectSummaries().forEach(summery -> keys.add(summery.getKey())); // repeat while objectListing.isTruncated()

JavaRDD<String> events = sc.parallelize(keys).flatMap(new ReadFromS3Function(clusterProps));
_

_ReadFromS3Function_は、_AmazonS3_クライアントを使用して実際の読み取りを行います。

_    public Iterator<String> call(String s) throws Exception {
        AmazonS3 s3Client = getAmazonS3Client(properties);
        S3Object object = s3Client.getObject(new GetObjectRequest(...));
        InputStream is = object.getObjectContent();
        List<String> lines = new LinkedList<>();
        String str;
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            if (is != null) {
                while ((str = reader.readLine()) != null) {
                    lines.add(str);
                }
            } else {
                ...
            }
        } finally {
            ...
        }
        return lines.iterator();
_

私は、Scalaで同じ質問に対して見た回答から、これを「翻訳」しました。パスのリスト全体をsc.textFile(...)に渡すことも可能だと思いますが、どちらが最善の方法であるかはわかりません。

10
Nira

根本的な問題は、s3でオブジェクトを一覧表示するのが非常に遅く、ディレクトリツリーのように見えるように作成されているため、何かがツリーウォークを実行するたびにパフォーマンスが失われることです(パスのワイルドカードパターンの加工と同様)。

投稿のコードは、より優れたパフォーマンスを提供するすべての子のリストを実行しています。これは、本質的にHadoop 2.8およびs3a listFiles(path、recursive)に同梱されているものです HADOOP-13208 を参照してください。

そのリストを取得した後、sparkのs3a/s3nパスにマップして、テキストファイル入力として処理できるオブジェクトパスへの文字列を取得できます。

val files = keys.map(key -> s"s3a://$bucket/$key").mkString(",")
sc.textFile(files).map(...)

そして、要求されたとおり、ここにJava使用されるコードがあります。

String prefix = "s3a://" + properties.get("s3.source.bucket") + "/";
objectListing.getObjectSummaries().forEach(summary -> keys.add(prefix+summary.getKey())); 
// repeat while objectListing truncated 
JavaRDD<String> events = sc.textFile(String.join(",", keys))

S3nをs3aに切り替えたことに注意してください。CPにhadoop-awsおよびAmazon-sdk JARがある場合、s3aコネクタが使用する必要があるためです。それはより良いものであり、spark人によるワークロード(私)に対して保守およびテストされるものです。 HadoopのS3コネクタの履歴 を参照してください。

8
Steve Loughran

sc.textFileを使用して複数のファイルを読み取ることができます。

引数としてmultiple file urlを渡すことができます。

directories全体を指定したり、wildcardsを使用したり、ディレクトリやワイルドカードのCSVを使用したりすることもできます。

例:

sc.textFile("/my/dir1,/my/paths/part-00[0-5]*,/another/dir,/a/specific/file")

このansからの参照

2
bob

あなたがawsを読んでいるときに並列化しようとすると、executorが利用され、パフォーマンスが確実に向上すると思います

val bucketName=xxx
val keyname=xxx
val df=sc.parallelize(new AmazonS3Client(new BasicAWSCredentials("awsccessKeyId", "SecretKey")).listObjects(request).getObjectSummaries.map(_.getKey).toList)
        .flatMap { key => Source.fromInputStream(s3.getObject(bucketName, keyname).getObjectContent: InputStream).getLines }
1
sun007