如何使Apache Spark mapPartition正常工作?

时间:2015-10-30 15:35:07

标签: python apache-spark pyspark

我正在尝试基于每个分区做一些工作,我想返回与输入相同的数据:

from urllib3 import HTTPConnectionPool

rdd = sc.parallelize(["peter", "john", "harris"])
def sendPartition(iterator):
    pool = HTTPConnectionPool('ajax.googleapis.com', maxsize=10)

    for record in iterator:
        r = pool.request('GET', '/ajax/services/search/web', fields={'q': 'urllib3', 'v': '1.0'})

    return iterator


rdd.mapPartitions(sendPartition).count()

我收到此错误:

  

TypeError:' NoneType'对象不可迭代

PS:这只是我想要实现的简化。我想对每个元素执行复杂的geosearch请求到ElasticSearch(因此我不能使用Spark Elasticsearch连接器)。在这个地图分区之前,我有一个巨大的过滤器,地图等管道。

PPS:我重新点燃了火花,现在我得到了#34; 0"作为输出,它比错误更好,但我预计它会是" 3"。

1 个答案:

答案 0 :(得分:0)

关于类型错误,它看起来不像是可以使用问题中包含的代码进行复制。我的猜测在某个时刻None值已传递给RDD构造函数或从sendPartition返回。

将空RDD作为输出的问题是您使用分区迭代器的方式的结果。 PySpark使用itertools.chain将数据传递给mapPartition,其行为与Scala Iterator的行为大致相同。

import itertools

iter = itertools.chain(range(10))
iter.next()
## 0

完成for循环后

for x in iter:
    x

你最终得到一个空的chain

type(iter)
## itertools.chain

iter.nex()
## Traceback (most recent call last)
##     ...
## StopIteration:

虽然StopIteration作为普通迭代逻辑的一部分处理,但没有数据要返回。

有几种方法可以解决这个问题,其中最干净的是提取函数并使用列表理解

def make_request(record, pool):
    r = pool.request('GET', '/ajax/services/search/web',
        fields={'q': 'urllib3', 'v': '1.0'})
    return r.read() # Or any other data you need.

def sendPartition(iterator):
    pool = HTTPConnectionPool('ajax.googleapis.com', maxsize=10)
    return [make_request(record, pool) for record in iterator]

请注意,如果要使用连接池,则必须在退出mapPartitions之前读取数据。这意味着没有懒惰的评估(如生成器)。就个人而言,我会考虑异步请求(例如在3.5中使用async/await,在其他地方使用RxPy)并在退出之前进行评估。