ZeroMQ: Messaging for Many Applications comments on REQ -> ROUTER communication:
If we rewrote our “Hello World” server using
ROUTER, we’d be able to process any number of “Hello” requests in parallel.
So I typed out hwclient.hs and have slightly modified the hwserver.hs from the ZeroMQ Guide:
Hello World Client
{-# LANGUAGE OverloadedStrings #-}
-- Hello World client
module Main where
import Control.Monad
import System.ZMQ4.Monadic
main :: IO ()
main = runZMQ $ do
liftIO $ putStrLn "Connecting to hello world server…"
requester <- socket Req
connect requester "tcp://localhost:5555"
forM_ [1..10] $ \i -> do
liftIO . putStrLn $ "Sending Hello " ++ show i ++ "…"
send requester [] "Hello"
_ <- receive requester
liftIO . putStrLn $ "Received World " ++ show i
And the Router:
Hello World Router
{-# LANGUAGE OverloadedStrings #-}
-- Hello World server
module Main where
import Control.Concurrent
import Control.Monad
import System.ZMQ4.Monadic
main :: IO ()
main = runZMQ $ do
-- Socket to talk to clients
responder <- socket Router
bind responder "tcp://*:5555"
forever $ do
buffer <- receive responder
liftIO $ do
putStrLn "Received Hello"
threadDelay 1000000 -- Do some 'work'
send responder [] "World"
But, when I run it, I see the client send a message, but never receive a response.
$stack exec -- client-exe
Connecting to hello world server…
Sending Hello 1…
In my other window, I see the server receives 3 messages and nothing more:
$stack exec -- server-exe
Received Hello
Received Hello
Received Hello
Q1: Why does the client never receive a reply?Q2: And why does the server (ROUTER) receive 3 messages rather than 1?
EDIT
I updated hwserver.hs to print out the messages that it receives rather than simply "Hello".
Posting the diff:
printf "received request: %s\n" . unpack $ buffer
replaced
putStrLn "Received Hello"
And the server (Router) showed:
$stack exec -- server-exe
received request: A§
received request:
received request: Hello
Lastly, here's the send's function signature:
λ: :t send
send
:: Sender t =>
Socket z t
-> [Flag] -> Data.ByteString.Internal.ByteString -> ZMQ z ()
REQ/ROUTERbehaviour automatically? (cit.: "When aZMQ_REQsocket is connected to aZMQ_ROUTERsocket, in addition to the identity of the originating peer each message received shall contain an empty delimiter message part. Hence, the entire structure of each received message as seen by the application becomes: one or more identity parts, delimiter part, one or more body parts. When sending replies to aZMQ_REQsocket the application must include the delimiter part." ) - user3666197workerhas to save the envelope ( which is all the parts up to and including the empty message frame ) and then it can do what’ s needed with the data part. Note that aREPsocket would do this automatically, but we’ re using theREQ-ROUTERpattern ... " ) - user3666197ROUTERin two distinct ways: • As a proxy that switches messages between frontend and backend sockets. • As an application that reads the message and acts on it. In the first case, theROUTERsimply reads all frames, including the artificial identity frame, and passes them on blindly. In the second case theROUTERmust know the format of the reply envelope it’ s being sent. As the other peer is aREQsocket, theROUTERgets the identity frame, an empty frame, and then the data frame." - user3666197