我正在开发一个Yesod应用程序,其中许多应用程序请求将导致从第三方API获取数据。获取的数据仅在后续请求期间使用 - 也就是说,触发API标注的请求可以在不等待标注完成的情况下完成。
重要的是,一段给定的数据xyz
不能被提取和存储两次,但应用程序的性质是多个客户端通常会在同一时刻对xyz
感兴趣。如果我让每个应用程序线程查询数据库以查看是否已经获取xyz
,我将开始看到与并发相关的问题,即使是适度的规模。即使我要编写一堆代码来处理与并发相关的完整性问题 - 这并不好玩,而且很难确定我是否涵盖了所有情况 - 以这种方式滥用昂贵的数据库查询是不好的做法。
在我看来,一个很好的选择是让所有App线程发布请求(“确保已经获取xyz数据”)到AMQP队列,一个或多个“后台工作者”类型进程将订阅该队列。
在this thread中,Greg Weber提出了一种双包布局,其中两个包都将“我的持久层”作为依赖关系。他提到我可以使用符号链接或hs-source-dir
来避免维护“持久层”代码的两个副本。
Greg所描述的高层次对我来说是完全合理的,但我对Haskell相对较新,我担心我需要一段时间来弄清楚细节。有人可以更详细地为我详细说明吗?
另一部分是:您如何建议编写BackgroundJobs流程?除了在Yesod的脚手架中,我从未编写过生产Haskell代码。我在广泛的大纲中得到它 - 我只会写一个main
,我将在其中订阅消息队列,并在每条消息上执行我的标注/处理/存储 - 但我是否需要担心例如手动分叉以确保在等待标注完成时进程不会阻塞?
非常感谢。
答案 0 :(得分:2)
我有一个应用程序,它包含一个webapp和一个单独的守护进程,它收集数据并将其插入与webapp共享的数据库中。
我基本上拥有相同源代码树中的所有代码,并且有两个定义main :: IO ()
的文件,一个名为webapp.hs
,另一个名为daemon.hs
。然后我让cabal文件定义并构建两个独立的可执行文件。
不幸的是,我无法分享代码作为我在日常工作中所做的内部项目。
答案 1 :(得分:1)
我离开了起始块,使用了Erik建议的一般方法 - 我刚刚在我的脚手架项目的.cabal文件中添加了第二个executable
块。这是相应的源文件(Erik的表述中的“daemon.hs
”):
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Import
import Yesod.Default.Config
import qualified Database.Persist
import qualified Database.Persist.Store as DPS (runPool, createPoolConfig, loadConfig, applyEnv)
import Settings
import Model
import Data.Conduit (runResourceT)
import Control.Monad.Logger (runStdoutLoggingT)
import Debug.Trace
import Data.Text as T
runQueries = do
res <- getBy $ UniqueFoo "bar"
trace ("\nresult: " ++ show res ++ "\n\n") $ return ()
main :: IO ()
main = do
conf <- (fromArgs parseExtra)
dbconf <- withYamlEnvironment "config/postgresql.yml" (appEnv conf)
DPS.loadConfig >>= DPS.applyEnv
p <- DPS.createPoolConfig (dbconf :: Settings.PersistConfig)
runStdoutLoggingT $ runResourceT $ DPS.runPool dbconf runQueries p
我基于this Yesod wiki entry上的第一个示例 - 修改了所以可执行文件需要一个环境标志。