【问题标题】:How does hgearman worker work?hgearman工人如何工作?
【发布时间】:2017-04-01 10:47:50
【问题描述】:

三周前我问了一个问题how does hgearman-client work?。在一些帮助下,我编写了一个简单的客户端应用程序,现在我正在工作端工作。下面的 worker 实现编译良好,运行无任何异常。唯一的麻烦是W.runWorker gc (return g) 不会被执行。如果我理解正确,这是 Haskell 懒惰和 return t Monad 包装的结果。但我一点也不知道如何摆脱这个问题。有人可以帮忙吗?

import qualified Control.Monad.State as S
import qualified Data.ByteString.Char8 as B
import qualified Network.Gearman.Client as C
import qualified Network.Gearman.Worker as W
import Network.Gearman.Internal (Function, Port)
import Network.Socket (HostName)

main :: IO ()
main = do
  c <- connect
  case c of
    Left e -> error $ B.unpack e
    Right gc -> do
      (res, _) <- flip S.runStateT gc $ do
        g <- (W.registerWorker name func)
        let t = W.runWorker gc (return g)
        return t >> return ()

      return res
  where
    connect = C.connectGearman (B.pack "i") host port
    host = "localhost"::HostName
    port =  4730::Port
    name = (B.pack "foo")::Function
    func _ = B.pack "bar"

不幸的是,绑定t &lt;- W.runWorker 的尝试以编译器异常结束。如果我以这种方式更改代码:

Right gc -> do
  (res, _) <- flip S.runStateT gc $ do
    g <- (W.registerWorker name func)
    t <- W.runWorker gc (return ())
    return t >> return  ()

  return res

编译失败并出现异常:

Couldn't match expected type `S.StateT
                                Network.Gearman.Internal.GearmanClient IO a0'
                with actual type `IO GHC.Conc.Sync.ThreadId'
    In a stmt of a 'do' block: t <- W.runWorker gc (return ())
    In the second argument of `($)', namely
      `do { g <- (W.registerWorker name func);
            t <- W.runWorker gc (return ());
            return t >> return () }'

IO GHC.Conc.Sync.ThreadIdrunWorker 的结果。

【问题讨论】:

    标签: haskell gearman


    【解决方案1】:

    对于某些a,类型为Gearman a 的值是一个动作,是做某事的秘诀。您可以将这些配方绑定在一起以制作更大的配方,直到您构建了 main 配方,它是可以运行的配方。

    实际上,这意味着如果您正在运行一个如下所示的 do-block:

    do ...
       foo
       ...
    

    然后foo 将运行。如果你有一个如下所示的 do-block:

    do ...
       ret <- foo
       ...
    

    然后foo将被运行,运行foo的结果将存储在ret中。这两种语法是绑定。但是,如果您正在运行如下所示的 do-block:

    do ...
       let ret = foo
       ...
    

    那么foo 将不会运行——相反,您只是要求变量retfoo 的简写,因此fooret 之后可以互换。

    所以现在你可以看到,在:

    do g <- W.registerWorker name func
       let t = W.runWorker gc (return g)
       return t >> return ()
    

    第二行实际上并没有运行 worker,它只是让t 成为运行 worker 的简写。返回一个动作也不会绑定它。你需要绑定:

       t <- W.runWorker gc (return g)
    

    顺便说一句,我一直在查看文档,看起来registerWorker 返回了一个Gearman (),这意味着运行该操作的结果是(),或者“没什么意思”。所以g没什么意思,你可以去掉它说

    do W.registerWorker name func
       t <- W.runWorker gc (return ())
       return t >> return ()
    

    大概是为了代替第二行中的return (),您将放置一个您想在工作人员中运行的操作。喜欢:

       t <- W.runWorker gc $ do
           ... the things you want the worker to do ...
       return t >> return ()
    

    最后一行:return t &gt;&gt; return (),也写成

    do return t
       return ()
    

    return () 完全相同。 return x 构造一个没有副作用的动作,它只用于结果。然后,当您使用&gt;&gt;(或不将结果绑定到do 块中)时,您仅针对其副作用运行操作并丢弃其结果。所以第一个 return 什么都不做。

    【讨论】:

    • 非常感谢您的详细回答。我也试过t &lt;- W.runWorker gc (return g)。不幸的是,在这种情况下,编译器抱怨Couldn't match expected type。这就是我转向 let + return 的原因;我将在我的问题中添加代码 sn-p 和异常。
    【解决方案2】:

    最后我在 Haskell 中实现了一个 gearman worker。

    {-# LANGUAGE DeriveDataTypeable #-}
    import Control.Exception (Exception, IOException, catch, throwIO)
    import qualified Data.ByteString.Char8 as B
    import Control.Monad.State
    import Data.Typeable     (Typeable)
    import qualified Network.Gearman.Client as C
    import qualified Network.Gearman.Worker as W
    import Network.Gearman.Internal (Function, GearmanClient, Port)
    import Network.Socket (HostName)
    import           Control.Concurrent
    import qualified Control.Monad.State as S
    
    data ConnectException = ConnectException HostName Port IOException
        deriving (Show, Typeable)
    instance Exception ConnectException
    
    main :: IO ()
    main = do
      c <- connect
      gc <- either (error . B.unpack) return c
      work gc
      return ()
      where
        connect = C.connectGearman (B.pack "worker-id") host port `catch` \e -> throwIO (ConnectException host port e)
        host = "localhost"::HostName
        port =  4730::Port
    
    work :: GearmanClient -> IO ()
    work gc = do
          (res, _) <- flip S.runStateT gc $ do
              W.registerWorker (B.pack "reverse"::Function)  B.reverse
              S.get >>= (\env -> forever $ S.liftIO (W.runWorker env (return ()) >> threadDelay (1000*1000)))
              return ()
          return res
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-02-09
      • 1970-01-01
      • 2021-10-13
      • 2012-04-10
      • 2011-11-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多