我有多个依次执行的任务。我想在不中断处理的情况下处理错误。我可以接受跳过错误的结果,只要我以后可以查看错误,甚至在进行一些数据校正后最好将它们推回处理中即可。
我尝试使用dask并能够创建一个执行过程,但是我不得不使用dask.submit,它需要函数名。我尝试使用streamz,然后能够获得以下解决方案。
import json
from streamz import Stream
from dask.distributed import Client
def save_error(data):
with open("data/error.json", "a+") as f:
f.write(json.dumps(data)+"\n")
def step1(data):
if isinstance(data["x"], int):
data["x"] *= 10
return data
logger.emit({"error":"invalid input","step":"step1", "data":data})
return None
def step2(data):
try:
data["x"] /= data["y"]
return data
except Exception as e:
logger.emit({"error":e.args,"step":"step2", "data":data})
return None
if __name__ == "__main__":
client = Client()
main = Stream()
logger = Stream(asynchronous=True)
data = [
{"x":1, "y": 4},
{"x":2, "y": 3},
{"x":"x", "y": 2},
{"x":"y", "y": 6},
{"x":7, "y": 0},
{"x":8, "y": 2}
]
logger.sink(save_error)
main.map(step1) \
.filter(lambda x: x != None) \
.map(step2) \
.filter(lambda x: x != None) \
.sink(print)
for item in data:
main.emit(item)
client.close()
在原始示例https://streamz.readthedocs.io/en/latest/dask.html中,使用了一个简单的数字列表,只需添加分散,缓冲和收集,就可以从本地执行切换到dask
分布式执行。
但是,如果我使用dict
而不是数字,则使用散点图和聚集会得出错误的结果。
没有分散,缓冲和收集
def inc(data):
data["x"] = data["x"] + 1
return data
source = Stream()
x = (source.map(inc)
.sink(print))
for i in range(5):
source.emit({"x": i*4, "y": i % 5})
输出:
{'x': 1, 'y': 0}
{'x': 5, 'y': 1}
{'x': 9, 'y': 2}
{'x': 13, 'y': 3}
{'x': 17, 'y': 4}
分散,缓冲和收集
def inc(data):
data["x"] = data["x"] + 1
return data
client = Client()
source = Stream()
x = (source.scatter()
.map(inc)
.buffer(8)
.gather()
.sink(print))
for i in range(5):
source.emit({"x": i*4, "y": i % 5})
输出:
{'x': 1, 'y': 0}
{'x': 1, 'y': 0}
{'x': 1, 'y': 0}
{'x': 1, 'y': 0}
{'x': 1, 'y': 0}
我可以像上面的代码那样使用单独的异步流来推送错误日志,但是不能与分散和收集一起使用。
我不明白为什么json的分散/聚集结果会有所不同,而json与数字完美结合。
答案 0 :(得分:0)
Streamz默认情况下不使用Dask。您可能要看这里:https://streamz.readthedocs.io/en/latest/dask.html
如果您不想使用streamz,可能还想看看Dask Futures和as_completed
答案 1 :(得分:0)
我发现prefect允许我跳过有问题的行/项目,还允许我使用dask
进行并行执行。
可能使自定义LogHandler异步。
由于此代码可与dask executer
一起使用,因此应该有可能跳过错误加处理错误,同时也不会影响普通dask
的主处理管道。
import logging
import requests
import prefect
from prefect import task, Flow, engine
from time import sleep
from prefect.utilities.logging import get_logger
import json
class LogHandler(logging.StreamHandler):
def emit(self, record):
requests.post("http://0.0.0.0:3000/", json=record.msg)
@task
def inc(data):
logger = prefect.context.get("logger")
if (data["x"] %7 != 0):
data["x"] += 1
return data
else:
logger.error({"error": "simulated error", "data":data, "step":"inc"})
raise engine.signals.SKIP("Simulated error")
@task
def dec(data):
logger = prefect.context.get("logger")
if (data != None):
data["y"] -= 1
logger.info({"data":data, "step":"dec"})
return data
@task
def add(x, y):
logger = prefect.context.get("logger")
res = x["x"] + y["y"]
logger.info({"res": res})
return res
@task(name="show", skip_on_upstream_skip=False)
def show_result(data, res):
logger = prefect.context.get("logger")
success= [item for item in res if not isinstance(item, engine.result.NoResultType)]
logger.info({"data":data, "result": success})
def main():
data = [{"x": x, "y": x % 5, "z": x % 3} for x in range(10)]
with Flow("dask-example") as flow:
incs = inc.map(data)
decs = dec.map(incs)
adds = add.map(x=incs, y=decs)
show_result(data, decs)
task_logger = get_logger("Task")
task_logger.addHandler(LogHandler())
flow.run() # runs this flow on its schedule
if __name__ == "__main__":
main()