2

我正在开发一个 Yesod 应用程序,其中许多应用程序请求将导致从 3rd-party API 获取数据。获取的数据只会在后续请求期间使用——也就是说,触发 API 调用的请求可以在不等待调用完成的情况下完成。

一条给定的数据xyz不要被获取和存储两次,这一点很重要,但应用程序的本质是多个客户通常会xyz同时对这些数据感兴趣。如果我让每个应用程序线程查询数据库以查看是否xyz已获取,即使规模不大,我也会开始看到与并发相关的问题。即使我要编写一堆代码来处理与并发相关的完整性问题——不好玩,而且很难确定我已经涵盖了所有情况——以这种方式滥用昂贵的数据库查询也是一种不好的做法。

在我看来,一个不错的选择是让所有 App 线程将请求(“确保已获取 xyz 数据”)发布到 AMQP 队列,一个或多个“后台工作”类型的进程将订阅该队列。

这个线程中,Greg Weber 建议使用双包布局,其中两个包都具有“我的持久层”作为依赖项。他提到我可以使用符号链接或hs-source-dirs 来避免维护“持久层”代码的两个副本。

在高层次上,Greg 所描述的内容对我来说非常有意义,但我对 Haskell 比较陌生,我担心我需要一段时间才能弄清楚细节。有人可以更详细地为我安排吗?

  • 包目录应该如何布局?(究竟是哪些文件构成了我的持久层?)
  • .cabal 文件应该是什么样子?
  • 一旦它们以这种方式布局,我是否需要更改我的(脚手架站点)源文件导入我的持久模型的方式?

另一部分是:您会如何推荐编写 BackgroundJobs 流程?除了 Yesod 的脚手架之外,我从未编写过生产型 Haskell 代码。我大致了解它——我将只写一个main,在其中我将订阅消息队列,并在每条消息上进行我的标注/处理/存储——但我是否需要担心例如手动分叉确保在等待调用完成时进程不会阻塞?

非常感谢。

4

2 回答 2

2

我有一个应用程序,它由一个 webapp 和一个单独的守护进程组成,该进程收集数据并将其插入到它与 webapp 共享的数据库中。

我基本上将所有代码都放在同一个源代码树中,并且有两个定义的文件main :: IO (),一个被称为webapp.hs,另一个被称为daemon.hs. 然后我让 cabal 文件定义并构建两个单独的可执行文件。

不幸的是,我无法将代码作为我日常工作中正在做的内部项目来分享。

于 2013-06-21T00:26:21.457 回答
1

我已经开始使用 Erik 建议的一般方法——我刚刚executable在我的脚手架项目的 .cabal 文件中添加了第二个块。这是相应的源文件(daemon.hsErik 公式中的“”):

{-# 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

我基于此 Yesod wiki 条目上的第一个示例进行了修改,因此可执行文件需要一个环境标志。

于 2013-06-22T00:42:00.150 回答