今天凌晨,用long-polling做聊天功能,服务器端直接用TChan广播:
get "/recv" $ do
msg <- liftIO $ atomically $
c <- dupTChan chan
readTChan c
json (msg::Message)
我想当然的在一个atomically
里用dupTChan
复制一个全局的TChan
,然后用readTChan
读数据,最后把结果转成json返回给客户端。因为readTChan
在TChan
没数据的时候会阻塞直到有数据,所以这里就实现了long-polling。
但是,测试的时候发现即使有向全局的chan写入数据,请求/recv
还是会一直阻塞,检查一遍JS之后没发现问题,再看TChan
的文档才发现问题所在。
dupTChan
复制得到的TChan
是空的,所以后面紧跟的readTChan
一定会通过retry
阻塞。retry
的时候会重新执行事务,dupTChan
又得到一个空TChan
,然后就死循环了。Haskell的retry
又挺厉害,不会真的在这里死循环,CPU使用也没太大变化,所以很难发现。
所以这里要分成两个事务:
get "/recv" $ do
c <- liftIO $ atomically $ dupTChan chan
msg <- liftIO $ atomically $ readTChan c
json (msg::Message)