我正在尝试基于每个分区做一些工作,我想返回与输入相同的数据:
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"。答案 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)并在退出之前进行评估。