并行读取S3中的多个文件(Spark,Java)

时间:2016-12-09 14:33:27

标签: java apache-spark amazon-s3

我看到了一些关于此问题的讨论,但无法理解正确的解决方案: 我想从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(...),但我不确定哪种方法是最好的方法。

3 个答案:

答案 0 :(得分:6)

潜在的问题是,在s3中列出对象的速度非常慢,并且只要某些内容出现在树状结构中(就像路径的通配符模式一样),它看起来像目录树的方式会导致性能下降。

帖子中的代码正在进行全子项列表,提供更好的性能,它基本上与Hadoop 2.8和s3a listFiles(路径,递归)一起提供HADOOP-13208

获取该列表后,您将获得对象路径的字符串,然后您可以将其映射到s3a / s3n路径,以便将spark作为文本文件输入处理,然后您可以将其应用于

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-awsamazon-sdk JAR,则s3a连接器是您应该使用的连接器。它更好,它是一个由人(我)维护和测试火花工作负载的那个。请参阅The history of Hadoop's S3 connectors

答案 1 :(得分:2)

您可以使用sc.textFile来阅读多个文件。

您可以将multiple file url作为参数传递。

您可以指定整个directories,使用wildcards甚至CSV目录和通配符。

例如:

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

Reference from this ans

答案 2 :(得分:0)

我想如果你尝试并行化,而阅读aws将会使用执行器,并且肯定会提高性能

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 }