我在Haskell中编写了一个systemd socket激活服务。想法是当消息发送到其套接字时应该自动启动服务,服务应该处理在套接字上等待的所有消息,然后退出。
注意:处理所有等待消息后服务应该关闭的原因(而不是永远运行)是套接字激活之间应该有几个小时或几天。
部署-trigger.socket:
[Socket]
ListenStream=/var/run/deploy-trigger.socket
[Install]
WantedBy=sockets.target
部署-trigger.service:
[Service]
ExecStart=/home/user4301448/.local/bin/deploy-trigger-exe
StartLimitInterval=0
Main.hs
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Control.Monad (forever)
import qualified Data.ByteString.Char8 as BS (putStrLn)
import Data.Foldable (foldl')
import Network.Socket (withSocketsDo, accept, close, SockAddr(SockAddrUnix), Socket)
import Network.Socket.ByteString (recv)
import Network.Socket.Activation (getActivatedSockets)
import System.Exit (exitWith, ExitCode(..))
main :: IO ()
main = withSocketsDo $ forever $ getActivatedSockets >>= processSocks
processSocks :: Maybe [Socket] -> IO ()
processSocks (Just socks) = do
putStrLn "Got socket(s)."
traverse_ (\sock -> accept sock >>= printMsgFromSock) socks
putStrLn "Finished processing socket(s)."
processSocks Nothing = do
putStrLn "Received no socket(s)."
exitWith ExitSuccess
printMsgFromSock :: (Socket, SockAddr) -> IO ()
printMsgFromSock (sock, sockaddr) = do
msg <- recv sock 2048
case sockaddr of
SockAddrUnix s -> putStrLn ("Printing message from socket: " ++ s)
_ -> putStrLn "Printing message from something that is not a UNIX socket."
BS.putStrLn msg
close sock
编译时(并使用stack install
安装),然后使用以下命令通过向套接字发送一些文本来激活:
printf 'Hello world\r\n' | nc -U /var/run/deploy-trigger.socket
以下内容将打印到systemd日志(我正在使用journalctl -f
来查看日志):
systemd[1]: Starting deploy-trigger.service...
没有印刷任何其他内容;该过程永远运行并最大化所有计算机的CPU核心。为什么会发生这种情况,是否有办法将行为改变为第一段中描述的行为?
将main
更改为以下内容:
main = withSocketsDo $ getActivatedSockets >>= processSocks
因此再次删除forever
,stack install
并向套接字发送一些文本将以下内容打印到日志中:
systemd[1]: Starting deploy-trigger.service...
deploy-trigger-exe[14800]: Got socket(s).
deploy-trigger-exe[14800]: Printing message from socket:
deploy-trigger-exe[14800]: Hello world
deploy-trigger-exe[14800]: Finished processing socket(s).
systemd[1]: Started deploy-trigger.service.
deploy-trigger-exe
然后干净利落地退出。这样做的缺点是,对于发送到套接字的每条消息,二进制文件似乎由systemd运行,这是不可取的。
注意:我怀疑这个问题源于我对UNIX套接字的无能。任何有关我误解的信息,纠正我的duff术语的答案都将是一个奖励。
答案 0 :(得分:3)
似乎因为stdout
没有连接到终端,putStrLn
的小输出仍然是缓冲的,因此不会出现在日志中。这可以通过定期调用hFlush
来解决,例如:
main = withSocketsDo $ forever $
getActivatedSockets >>= processSocks >>= \_ -> hFlush stdout
答案 1 :(得分:2)
好的,首先关于丢失的输出,正如<deadlock>
<victim-list>
<victimProcess id="process28fc21868" />
</victim-list>
<process-list>
<process id="process28fc21868" taskpriority="0" logused="2504" waitresource="KEY: 5:72057594562412544 (40fd182c0dd9)" waittime="5008" ownerId="299034576" transactionname="user_transaction" lasttranstarted="2018-02-01T12:22:55.580" XDES="0x140b2cc70" lockMode="X" schedulerid="1" kpid="3600" status="suspended" spid="87" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2018-02-01T12:22:55.580" lastbatchcompleted="2018-02-01T12:22:55.580" lastattention="2018-02-01T12:22:52.480" clientapp="EUROSTOP e-i Service" hostname="SRVAZBRWSQL01" hostpid="1328" loginname="sa" isolationlevel="read committed (2)" xactid="299034576" currentdb="5" lockTimeout="4294967295" clientoption1="673316896" clientoption2="128056">
<executionStack>
<frame procname="ALL.dbo.trgi_u_Constants" line="705" stmtstart="37030" stmtend="37270" sqlhandle="0x03000500651fce5aad8fd3002aa5000000000000000000000000000000000000000000000000000000000000">
update myconstants
set value = (select last_c_no from inserted)
where batch = 'last_c_no' </frame>
<frame procname="IF_TEST.myschema.SendTestData_Customers_SToALL_Customers" line="87" stmtstart="11420" stmtend="11650" sqlhandle="0x030007007f19ab663ac3e5005ca7000001000000000000000000000000000000000000000000000000000000">
update myschema.ALL_Constants
set last_c_no = @nLastCNumber + @NumberOfInsertedCs </frame>
<frame procname="IF_TEST.myschema.SendTestData_Customers" line="22" stmtstart="962" stmtend="1196" sqlhandle="0x030007009c954b7757b4d00054a7000001000000000000000000000000000000000000000000000000000000">
exec myschema.SendTestData_Customers_SToALL_Customers @MessageCode, @RejectAllOnValidationError </frame>
<frame procname="IF_TEST.myschema.SendSubmittedFData" line="13" stmtstart="876" stmtend="1114" sqlhandle="0x030007009b4fb66e2db4d00054a7000001000000000000000000000000000000000000000000000000000000">
exec [myschema].[SendTestData_Customers] @MessageCode, @RejectAllOnValidationError
-- Customer Orders </frame>
<frame procname="adhoc" line="1" stmtstart="104" sqlhandle="0x010007008547740e50dc4dd90700000000000000000000000000000000000000000000000000000000000000">
Exec myschema.SendSubmittedFData @0, @1, @2, @3 </frame>
<frame procname="unknown" line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
unknown </frame>
</executionStack>
<inputbuf>
(@0 nvarchar(4000),@1 int,@2 nvarchar(4000),@3 int);Exec myschema.SendSubmittedFData @0, @1, @2, @3 </inputbuf>
</process>
<process id="process2b6e94cf8" taskpriority="-6" logused="43652" waitresource="KEY: 5:72057594562412544 (d08358b1108f)" waittime="5009" ownerId="299033786" transactionname="user_transaction" lasttranstarted="2018-02-01T12:22:53.810" XDES="0x28262f130" lockMode="X" schedulerid="2" kpid="13408" status="suspended" spid="122" sbid="0" ecid="0" priority="-5" trancount="3" lastbatchstarted="2018-02-01T12:15:00.580" lastbatchcompleted="2018-02-01T12:15:00.580" lastattention="1900-01-01T00:00:00.580" clientapp="SQLAgent - TSQL JobStep (Job 0x24121E41ABD80643985B522FE6C248A7 : Step 1)" hostname="SRVAZBRWSQL01" hostpid="2252" loginname="SRVAZBRWSQL01\ALLSYSTEM" isolationlevel="read committed (2)" xactid="299033786" currentdb="5" lockTimeout="4294967295" clientoption1="673316896" clientoption2="128056">
<executionStack>
<frame procname="ALL.dbo.trgi_u_Constants" line="726" stmtstart="38142" stmtend="38386" sqlhandle="0x03000500651fce5aad8fd3002aa5000000000000000000000000000000000000000000000000000000000000">
update myconstants
set value = (select last_gs_rec_no from inserted)
where batch = 'last_gs_rec_no' </frame>
<frame procname="ALL.dbo.trgi_i_i_I_deliveries" line="1005" stmtstart="69046" stmtend="69220" sqlhandle="0x030005006cd104389a850d00a1a6000000000000000000000000000000000000000000000000000000000000">
update dbo.Constants set last_gs_rec_no = last_gs_rec_no + @nmyDeliveriesCreatedCount </frame>
<frame procname="C_HELP.dbo.Client_myDeliveries_I_Std" line="86" stmtstart="7306" stmtend="8324" sqlhandle="0x030006000d09a1438efcba0074a8000001000000000000000000000000000000000000000000000000000000">
insert into [ALL].dbo.i_I_deliveries
--[columns]
select --[columns]
from #Client_deliveries_PO_stg stg </frame>
<frame procname="C_HELP.dbo.Client_myDeliveries_I_S" line="446" stmtstart="32832" stmtend="33032" sqlhandle="0x0300060028323b37f7a70101eca7000001000000000000000000000000000000000000000000000000000000">
exec dbo.Client_myDeliveries_I_Std @week_selector, @username, @factory_bin_location, @Parameter </frame>
<frame procname="adhoc" line="6" stmtstart="216" sqlhandle="0x02000000e49e3111676b7e3aec714d06946692f70e3a8a880000000000000000000000000000000000000000">
exec Client_myDeliveries_I_S </frame>
</executionStack>
<inputbuf>
DECLARE @deadlock_var NCHAR(3);
SET @deadlock_var = N'LOW';
SET DEADLOCK_PRIORITY @deadlock_var;
exec Client_myDeliveries_I_S </inputbuf>
</process>
</process-list>
<resource-list>
<keylock hobtid="72057594562412544" dbid="5" objectname="ALL.dbo.myconstants" indexname="pk_myconstants" id="lockd5ad7b00" mode="RangeS-U" associatedObjectId="72057594562412544">
<owner-list>
<owner id="process2b6e94cf8" mode="RangeS-S" />
</owner-list>
<waiter-list>
<waiter id="process28fc21868" mode="X" requestType="convert" />
</waiter-list>
</keylock>
<keylock hobtid="72057594562412544" dbid="5" objectname="ALL.dbo.myconstants" indexname="pk_myconstants" id="lock9466df00" mode="RangeS-U" associatedObjectId="72057594562412544">
<owner-list>
<owner id="process28fc21868" mode="RangeS-S" />
</owner-list>
<waiter-list>
<waiter id="process2b6e94cf8" mode="X" requestType="convert" />
</waiter-list>
</keylock>
</resource-list>
</deadlock>
所说的那样,如果写入管道,Linux输出会被块缓冲。
将Li-yao Xia
更改为
main
您将在main = do
hSetBuffering stdout LineBuffering
withSocketsDo $ forever $ getActivatedSockets >>= processSocks
中看到:
journalctl -f
在此(预期)输出之后,您的程序将挂起。
如何找出它的挂起位置?当然还有systemd[1]: Started deploy-trigger.service.
deploy-trigger-exe[14197]: Got socket(s).
deploy-trigger-exe[14197]: Printing message from socket:
deploy-trigger-exe[14197]: Hello world
deploy-trigger-exe[14197]: Finished processing socket(s).
deploy-trigger-exe[14197]: Got socket(s).
(有传言说95%的计算机问题可以通过strace
来解决。)
strace
正如我们所看到的,该程序现已在% sudo strace -fp $(pidof deploy-trigger-exe)
strace: Process 14197 attached
中被阻止。这是有道理的(因为另一方已断开连接)。
您可能会感到困惑的另一件事是它第二次打印accept sock
。
我认为你对Got socket(s)
的工作方式存在误解。我总结你写getActivatedSockets
。这告诉我你第二次打电话给forever $ getActivatedSockets >>= ...
时,它会返回别的东西而不是第一次(特别是,我怀疑你认为它会在#34}之后返回getActivatedSockets
;以某种方式处理&#34;套接字。)
但是看code of getActivatedSockets
,它总会返回相同的结果(因为它只是读取一些环境变量的内容)。因此,将它包装在Nothing
中似乎没有意义。
你写了
服务应处理在套接字上等待的所有消息,然后退出
为实现这一目标,我认为您应该删除forever
:
forever
(尝试更改此代码时,请不要忘记先杀死仍在运行的main = do
hSetBuffering stdout LineBuffering
withSocketsDo $ getActivatedSockets >>= processSocks
putStrLn "End of main, exiting"
。
你会得到:
deploy-trigger-exe
我认为这就是你要找的东西。
另一个提示:请注意,如果您向套接字发送大型消息,则必须循环systemd[1]: Started deploy-trigger.service.
deploy-trigger-exe[15881]: Got socket(s).
deploy-trigger-exe[15881]: Printing message from socket:
deploy-trigger-exe[15881]: Hello world
deploy-trigger-exe[15881]: Finished processing socket(s).
deploy-trigger-exe[15881]: End of main, exiting
以接收所有数据。
(快速插件:我正在帮助解决像这样的问题。)