如何在dask / streamz任务中合并异步队列?

时间:2019-08-18 02:51:17

标签: python dask

我有多个依次执行的任务。我想在不中断处理的情况下处理错误。我可以接受跳过错误的结果,只要我以后可以查看错误,甚至在进行一些数据校正后最好将它们推回处理中即可。

我尝试使用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与数字完美结合。

2 个答案:

答案 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()