如何动态地将Source添加到现有Graph?

时间:2016-06-21 12:14:54

标签: scala akka akka-stream

什么可以替代动态更改运行图?这是我的情况。我有图表将文章摄入DB。文章来自3种不同格式的插件。因此,我有几个流程

val converterFlow1: Flow[ImpArticle, Article, NotUsed]
val converterFlow2: Flow[NewsArticle, Article, NotUsed]
val sinkDB: Sink[Article, Future[Done]]

// These are being created every time I poll plugins    
val sourceContentProvider : Source[ImpArticle, NotUsed]
val sourceNews : Source[NewsArticle, NotUsed]
val sourceCit : Source[Article, NotUsed]

val merged = Source.combine(
    sourceContentProvider.via(converterFlow1),
    sourceNews.via(converterFlow2),
    sourceCit)(Merge(_))

val res = merged
  .buffer(10, OverflowStrategy.backpressure)
  .toMat(sinkDB)(Keep.both)
  .run()

问题是我每24小时从内容提供商处获取一次数据,每2小时一次从新闻获取数据,最后一个来源可能随时来自,因为它来自人类。

我意识到图表是不可变的,但我如何定期将Source的新实例附加到我的图表中,以便我对摄取过程进行单点限制?

更新:您可以说我的数据是Source - s的流,在我的情况下有三个来源。但我不能改变它,因为我从外部类(所谓的插件)获取Source的实例。这些插件独立于我的摄取类。我不能将它们组合成一个巨大的类,以便有单Source

3 个答案:

答案 0 :(得分:3)

好的,一般来说,正确的方法是将来源流加入单一来源,即从Source[Source[T, _], Whatever]转到Source[T, Whatever]。这可以使用flatMapConcatflatMapMerge来完成。因此,如果您可以获得Source[Source[Article, NotUsed], NotUsed],则可以使用flatMap*个变种之一并获得最终Source[Article, NotUsed]。为你的每个来源做这件事(没有双关语),然后你的原始方法应该有用。

答案 1 :(得分:2)

我已根据Vladimir Matveev给出的答案实施了代码,并希望与其他人分享,因为它看起来像是我的常见用例。

我知道Viktor Klang提到的rep(1:4, c(1, 2, 3, 4)) [1] 1 2 2 3 3 3 4 4 4 4 ,但我不知道 #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/highgui/highgui.hpp> #include <iostream> int main() { cv::Mat input = cv::imread("pallet.jpg"); // convert to grayscale (you could load as grayscale instead) cv::Mat gray; cv::cvtColor(input,gray, CV_BGR2GRAY); // compute mask (you could use a simple threshold if the image is always as good as the one you provided) cv::Mat mask; cv::threshold(gray, mask, 0, 255, CV_THRESH_BINARY_INV | CV_THRESH_OTSU); // find contours (if always so easy to segment as your image, you could just add the black/rect pixels to a vector) std::vector<std::vector<cv::Point> > contours; std::vector<cv::Vec4i> hierarchy; cv::findContours(mask,contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); /// Draw contours and find biggest contour (if there are other contours in the image, we assume the biggest one is the desired rect) // drawing here is only for demonstration! int biggestContourIdx = -1; float biggestContourArea = 0; cv::Mat drawing = cv::Mat::zeros( mask.size(), CV_8UC3 ); for( int i = 0; i< contours.size(); i++ ) { cv::Scalar color = cv::Scalar(0, 100, 0); drawContours( drawing, contours, i, color, 1, 8, hierarchy, 0, cv::Point() ); float ctArea= cv::contourArea(contours[i]); if(ctArea > biggestContourArea) { biggestContourArea = ctArea; biggestContourIdx = i; } } // if no contour found if(biggestContourIdx < 0) { std::cout << "no contour found" << std::endl; return 1; } // compute the rotated bounding rect of the biggest contour! (this is the part that does what you want/need) cv::RotatedRect boundingBox = cv::minAreaRect(contours[biggestContourIdx]); // one thing to remark: this will compute the OUTER boundary box, so maybe you have to erode/dilate if you want something between the ragged lines // draw the rotated rect cv::Point2f corners[4]; boundingBox.points(corners); cv::line(drawing, corners[0], corners[1], cv::Scalar(255,255,255)); cv::line(drawing, corners[1], corners[2], cv::Scalar(255,255,255)); cv::line(drawing, corners[2], corners[3], cv::Scalar(255,255,255)); cv::line(drawing, corners[3], corners[0], cv::Scalar(255,255,255)); // display cv::imshow("input", input); cv::imshow("drawing", drawing); cv::waitKey(0); cv::imwrite("rotatedRect.png",drawing); return 0; } 。这真是太棒了。

Source.queue

我仍然会检查flatMapConcat返回的implicit val system = ActorSystem("root") implicit val executor = system.dispatcher implicit val materializer = ActorMaterializer() case class ImpArticle(text: String) case class NewsArticle(text: String) case class Article(text: String) val converterFlow1: Flow[ImpArticle, Article, NotUsed] = Flow[ImpArticle].map(a => Article("a:" + a.text)) val converterFlow2: Flow[NewsArticle, Article, NotUsed] = Flow[NewsArticle].map(a => Article("a:" + a.text)) val sinkDB: Sink[Article, Future[Done]] = Sink.foreach { a => Thread.sleep(1000) println(a) } // These are being created every time I poll plugins val sourceContentProvider: Source[ImpArticle, NotUsed] = Source(List(ImpArticle("cp1"), ImpArticle("cp2"))) val sourceNews: Source[NewsArticle, NotUsed] = Source(List(NewsArticle("news1"), NewsArticle("news2"))) val sourceCit: Source[Article, NotUsed] = Source(List(Article("a1"), Article("a2"))) val (queue, completionFut) = Source .queue[Source[Article, NotUsed]](10, backpressure) .flatMapConcat(identity) .buffer(2, OverflowStrategy.backpressure) .toMat(sinkDB)(Keep.both) .run() queue.offer(sourceContentProvider.via(converterFlow1)) queue.offer(sourceNews.via(converterFlow2)) queue.offer(sourceCit) queue.complete() completionFut.onComplete { case Success(res) => println(res) system.terminate() case Failure(ex) => ex.printStackTrace() system.terminate() } Await.result(system.whenTerminated, Duration.Inf) 是否成功,但在我的情况下,这些来电很少见。

答案 2 :(得分:1)

如果您无法将其建模为Source[Source[_,_],_],那么请考虑使用Source.queue[Source[T,_]](queueSize, overflowStrategy)here

如果提交失败,那么你必须要小心的是。