【发布时间】:2011-04-23 14:59:13
【问题描述】:
服务器端会话 [还] 不是 Snap 框架的一部分。有没有办法添加某种服务器端状态?
假设我想为每个 HTTP 请求增加一个计数器。我该怎么做?
【问题讨论】:
标签: haskell session-state haskell-snap-framework
服务器端会话 [还] 不是 Snap 框架的一部分。有没有办法添加某种服务器端状态?
假设我想为每个 HTTP 请求增加一个计数器。我该怎么做?
【问题讨论】:
标签: haskell session-state haskell-snap-framework
我找到了两个会话相关的包:
snap-auth 由 Snap 框架团队制作,或至少由其中一位作者/贡献者 (Ozgun Ataman) 制作。它针对身份验证和会话管理。会话管理是使用 ByteString 到 ByteString 的映射完成的,这意味着您只能存储已经序列化为 ByteString 的数据:
type Session = Map ByteString ByteString
另一方面,mysnapsession 允许您使用任意类型来为会话建模。然而,Map 类型的会话有一些辅助函数。更多详情here。作者 Chris Smith 也是 Snap 框架项目的成员。
【讨论】:
就目前而言,上述答案是正确的,但它并没有解决一些实际问题。
首先是服务器重启。如果您的存储不仅仅是缓存,它还需要在服务器重启时保持持久性。
其次是代码重载。 Snap 的未来版本,从 0.3 开始(可能在 12 月初到期)将在开发使用中动态重新加载代码。就开发速度而言,这是一个巨大的优势,但它使服务器本地状态成为一种有趣的心理锻炼。如果程序员更改了服务器本地状态的类型/初始化/任何内容,则需要重新初始化。那里有一些巨大的工程挑战。
当我为 0.3 编写动态重新加载代码时,我曾为这个问题苦苦挣扎了一段时间。然后我看了看其他平台。 php?在外部存储所有内容(数据库、内存缓存等)。完全没有内存中的交叉请求存储。 Ruby on Rails?一样。
结合第一个问题中固有的挑战,我得出的结论是,除了可能的缓存优化之外,服务器应该是无状态的。将持久性问题留给为其设计的库/外部流程。
所以我设计了生产和开发加载器使用的通用接口(一个使用静态加载,另一个使用动态加载)来获取 3 个函数:一个初始化函数、一个清理函数和一个使用由初始化函数。在生产模式下,编译为在服务器启动时调用初始化,并在服务器关闭时进行清理。在开发模式下,它编译为:对于每个请求,动态加载所有 3 个,然后运行 init、handler、cleanup。显然,没有任何状态能够以这种方式在交叉请求中幸存下来。
然后我的答案就变成了:通过某种具有内置持久性的机制进行交叉请求存储,并让服务器状态成为其接口。如果您想在进程内工作,请使用 happstack-state 或 sqlite 之类的东西,如果您想在本地进程之外工作,请使用数据库或其他一些外部存储。
作为补充说明,由于添加了 MonadSnap 接口,在 Snap 0.3 中管理“全局”资源(如连接池等)也容易得多。
【讨论】:
最简单的方法是将状态放在 mvar 后面:
fooHandler :: MVar Int -> Snap ()
fooHandler mvar = do
x <- liftIO $ modifyMVar mvar $ \y -> let y'=y+1 in (y',y')
writeBS $ S.pack $ "Incremented counter to: " ++ show x
在站点初始化时初始化 mvar。希望这会有所帮助。
【讨论】:
\y -> let y'=y+1 in y' `seq` (y',y')。否则,您可能会在 mvar 中存储未评估的 thunk 而不是值。或者使用 strict-concurrency 包中的严格 mvar。
modifyMVar :: MVar a -> (a -> IO (a, b)) -> IO b,应该是modifyMVar mvar $ \y -> let y' = y+1 in y' ``seq`` return (y',y')吧?
modifyMVar ... 代码是否类型正确?只需手动解析类型。你已经说过modifyMVar :: MVar a -> (a -> IO (a, b)) -> IO b,所以最好是mvar :: MVar a,我们从fooHandler(mvar :: MVar Int,所以a ~ Int和lambda(\y -> let y' ... :: TypeOf y in y' `seq` return (y',y') :: a -> IO (a,b)知道这一点。所以(y',y')强制a和b是一样的。使用hoogle 你应该知道seq :: a -> b -> b b 是return (y',y') :: IO (a,a) 所以这是正确的。a 和Int 统一起来,事情看起来不错。