it-swarm.com.de

Paralleles Lesen mehrerer Dateien aus S3 (Spark, Java)

Ich habe einige Diskussionen zu diesem Thema gesehen, konnte aber die richtige Lösung nicht ganz verstehen: Ich möchte ein paar hundert Dateien von S3 in ein RDD laden. So mache ich es jetzt:

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));

Der ReadFromS3Function führt das eigentliche Lesen mit dem AmazonS3-Client durch:

    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();

Ich habe das irgendwie aus den Antworten "übersetzt", die ich für dieselbe Frage in Scala sah. Ich denke, es ist auch möglich, die gesamte Liste der Pfade an sc.textFile(...) zu übergeben, aber ich bin nicht sicher, welcher Weg der beste Weg ist.

9
Nira

das zugrunde liegende Problem ist, dass das Auflisten von Objekten in S3 wirklich langsam ist und die Art und Weise, wie ein Verzeichnisbaum aussieht, die Leistung abbricht, wenn etwas einen Treewalk ausführt (wie das Wildcard Pattern Maching Path). 

Der Code in dem Beitrag enthält eine Auflistung aller Kinder, die eine wesentlich bessere Leistung liefert. Im Wesentlichen wird dies mit Hadoop 2.8 und s3a listFiles (Pfad, rekursiv) geliefert (siehe HADOOP-13208 ).

Nachdem Sie diese Auflistung erhalten haben, verfügen Sie über Strings zu Objektpfaden, die Sie dann den s3a/s3n-Pfaden zuordnen können, die Spark als Textdatei-Eingaben behandelt, und auf die Sie dann die Arbeit anwenden können

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

Und wie gewünscht, hier ist der verwendete Java-Code.

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))

Beachten Sie, dass ich von s3n zu s3a gewechselt habe, denn sofern Sie über die JARs hadoop-aws und Amazon-sdk auf Ihrem CP verfügen, sollten Sie den s3a-Connector verwenden. Es ist besser, und es ist das, das von Leuten (mir) auf Funken-Workloads gewartet und getestet wird. Siehe Die Geschichte der Hadoop-S3-Anschlüsse .

6
Steve Loughran

Sie können sc.textFile verwenden, um mehrere Dateien zu lesen.

Sie können multiple file url mit als Argument übergeben.

Sie können ganze directories angeben, wildcards und sogar CSV von Verzeichnissen und Platzhaltern verwenden.

Ex: 

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

Referenz von diesem ans

2
bob

Ich denke, wenn Sie versuchen, beim Lesen von aws zu parallelisieren, wird der Executor verwendet und die Leistung definitiv verbessert

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 }
0
Nitin