web-dev-qa-db-fra.com

Lecture de plusieurs fichiers de S3 en parallèle (Spark, Java)

J'ai assisté à quelques discussions à ce sujet mais je ne comprenais pas vraiment la bonne solution: Je veux charger quelques centaines de fichiers de S3 dans un RDD. Voici comment je le fais maintenant:

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

Le ReadFromS3Function effectue la lecture réelle à l'aide du client 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();

J'ai en quelque sorte "traduit" cela à partir des réponses que j'ai vues pour la même question à Scala. Je pense qu'il est également possible de passer toute la liste des chemins à sc.textFile(...), mais je ne suis pas sûr de savoir quelle est la meilleure méthode.

9
Nira

le problème sous-jacent est que la liste des objets dans s3 est très lente et que sa structure ressemble à une arborescence de répertoires tue les performances chaque fois que quelque chose se passe dans une promenade dans les arbres (comme le fait le traitement générique des chemins). 

Le code de l'article contient la liste de tous les enfants qui offre de bien meilleures performances. Il s'agit essentiellement de ce qui est livré avec Hadoop 2.8 et s3a listFiles (chemin, récursif) voir HADOOP-13208 .

Après avoir obtenu cette liste, vous avez des chaînes dans les chemins des objets que vous pouvez ensuite mapper vers les chemins s3a/s3n et que spark doit gérer en tant qu’entrées de fichier texte, et auxquels vous pouvez ensuite appliquer un travail.

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

Et comme demandé, voici le code Java utilisé.

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

Notez que je suis passé de s3n à s3a car, à condition que vous disposiez des fichiers JAR hadoop-aws et Amazon-sdk sur votre CP, le connecteur s3a est celui que vous devriez utiliser. C’est mieux, et c’est celui qui est entretenu et mis à l’essai par des personnes (moi) contre les charges d’étincelles. Voir L'histoire des connecteurs S3 de Hadoop .

6
Steve Loughran

Vous pouvez utiliser sc.textFile pour lire plusieurs fichiers.

Vous pouvez passer multiple file url avec comme argument.

Vous pouvez spécifier directories entier, utiliser wildcards et même CSV de répertoires et de caractères génériques.

Ex: 

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

Référence de cette ans

2
bob

Je suppose que si vous essayez de mettre en parallèle tout en lisant les aws utiliseront l'exécuteur et amélioreront définitivement les performances

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