如何最有效地使用Groovy / GPars处理CSV文件的行?

时间:2011-10-13 14:47:07

标签: groovy

问题很简单,我很惊讶它在搜索时没有立即弹出。

我有一个需要处理的CSV文件,可能非常大。应将每一行传递给处理器,直到处理完所有行。为了读取CSV文件,我将使用OpenCSV,它实质上提供了一个readNext()方法,它给了我下一行。如果没有更多行可用,则所有处理器都应终止。

为此,我创建了一个非常简单的groovy脚本,定义了一个同步的readNext()方法(因为读取下一行并不是非常耗时),然后创建了几个线程来读取下一行并处理它。它工作正常,但是......

不应该有我可以使用的内置解决方案吗?这不是gpars集合处理,因为它总是假设内存中存在一个现有集合。相反,我无法将其全部读入内存并进行处理,这将导致内存异常。

所以....有一个很好的模板,可以使用几个工作线程“逐行”处理CSV文件吗?

3 个答案:

答案 0 :(得分:6)

同时访问文件可能不是一个好主意,GPars的fork / join-processing仅适用于内存数据(集合)。我的意思是按顺序将文件读​​入列表。当列表达到一定大小时,使用GPars同时处理列表中的条目,清除列表,然后继续阅读行。

答案 1 :(得分:5)

对于演员来说,这可能是一个很好的问题。同步读者actor可以将CSV行移交给并行处理器actor。例如:

@Grab(group='org.codehaus.gpars', module='gpars', version='0.12')

import groovyx.gpars.actor.DefaultActor
import groovyx.gpars.actor.Actor

class CsvReader extends DefaultActor {
    void act() {
        loop {
            react {
                reply readCsv()
            }
        }
    }
}

class CsvProcessor extends DefaultActor {
    Actor reader
    void act() {
        loop {
            reader.send(null)
            react {
                if (it == null)
                    terminate()
                else
                    processCsv(it)
            }
        }
    }
}

def N_PROCESSORS = 10
def reader = new CsvReader().start()
(0..<N_PROCESSORS).collect { new CsvProcessor(reader: reader).start() }*.join()

答案 2 :(得分:2)

我只是在Grails中完成一个问题的实现(你没有指定你是使用grails,普通的hibernate,普通的JDBC还是其他东西)。

我知道没有任何开箱即用的东西。你可以看看与Spring Batch的集成,但是最后一次看到它时,它对我来说感觉非常沉重(而且不是非常时髦)。

如果您正在使用普通JDBC,那么做Christoph建议的可能是最容易的事情(读取N行并使用GPars同时旋转这些行)。

如果您正在使用grails或hibernate,并希望您的工作线程可以访问spring上下文以进行依赖注入,那么事情会变得复杂一些。

我解决它的方法是使用Grails Redis插件(免责声明:我是作者)和Jesque plugin,这是Resque的java实现。

Jesque插件允许您创建“作业”类,这些类具有“进程”方法,该方法具有用于处理在Jesque队列中排队的工作的任意参数。你可以根据需要增加工人数量。

我有一个文件上传,管理员用户可以将文件发布到,它将文件保存到磁盘并为我创建的ProducerJob排队作业。 ProducerJob旋转文件,对于每一行,它将消息队列一条消息,以便ConsumerJob接收。该消息只是从CSV文件中读取的值的映射。

ConsumerJob获取这些值并为其行创建适当的域对象并将其保存到数据库中。

我们已经在生产中使用Redis,因此使用它作为排队机制是有道理的。我们有一个旧的同步加载,连续运行文件加载。我目前正在使用一个生产者工人和4个消费者工作者,并且以这种方式加载东西的速度比旧负载快100倍(对最终用户有更好的进度反馈)。

我同意最初的问题,即这样的东西可能会被打包,因为这是一个相对常见的事情。

更新:我提出a blog post with a simple example doing imports with Redis + Jesque