我正在使用Erlang编写的IRC bot应用程序。它有两个关键模块:irc_bot
和irc_proto
,每个模块作为gen_server
进程运行(每个进程实例有一个)。 irc_bot
使用gen_tcp
连接到IRC服务器,接收TCP消息并进行相应的响应。 irc_proto
的工作是公开函数,这些函数反过来根据IRC协议格式化响应。 (例如,有一个函数irc_proto:say(To, Msg)
可将Msg
转换为格式正确的PRIVMSG
响应。)
目前,此应用程序不符合 OTP标准。当irc_bot
启动时,它会打开TCP连接,然后将TCP套接字(由gen_tcp:connect()
返回)传递给irc_proto:start_link()
。 irc_proto
将其作为其状态的一部分存储,并使用该套接字发回响应。
最终(在不久的将来!),此应用程序将符合OTP标准,并且将拥有某种主管(可能更多,但显然至少是顶级主管)。其中存在一个问题:如果主管负责启动irc_bot
和irc_proto
,那么主管将没有TCP套接字(由gen_tcp:connect()
返回),因此无法将其传递给irc_proto
。
我不确定这里最好的方法是什么。我需要几层主管(或类似的东西),所以我可以(不知何故)将TCP套接字传递给irc_proto
?或者分享那个插座只是一个糟糕的设计? irc_bot
是否应该实际暴露某些功能,例如irc_bot:send()
,irc_proto
实际上用于在格式化响应后发回响应? OTP是否就如何构建此应用程序提供任何指导或建议?
答案 0 :(得分:4)
有两种方法:
gen_tcp:send/2
没有所有权限制)并让它们直接发送给它。从单个套接字中获得两个进程 receive 可能会比这更容易发生,但确实有可能。但是,当拥有它的控制进程关闭时,套接字将关闭,并且套接字只能有一个所有者。为了确保你没有运行半破案例,你需要跨共享套接字的进程的链接/监视器,最简单的情况是链接,所以它们都会立刻死掉......那时你实际上是对的回到只有一个进程代表几个底层进程处理套接字但代码可读性较差的情况。
<强>无论其强>
我更喜欢在概念上隔离事物,让我让每个服务最多说两个协议 - 一个&#34; up&#34;服务链和一个&#34; down&#34;服务链。在这种情况下&#34; up&#34;可能意味着知道如何说IRC,&#34; down&#34;可能意味着知道如何与渠道处理者交谈。
如果我以这种方式思考机器人的问题&#34;谁拥有套接字?&#34;因为答案显而易见而消失了:知道如何说IRC的人和其他人。
走向这个方向我会写一个gen_server模块,它知道如何说IRC(或者,更可能是gen_fsm - 我必须看一下协议来决定),一个gen_server模块,表现为机器人-channel(一个通道处理程序),以及一个可以执行所需字符串/二进制处理的函数模块。
每个服务器一个IRC说话者,并且在你加入的每个频道的一个频道处理流程之下。在这种情况下,字符串处理不是进程,除非您选择为每个要处理的消息生成进程(这完全可能,但可能不是IRC通道所需的方法)。
到目前为止,
这样的潜在模块irc_talker.erl
chan_hand.erl
message_munger.erl
现在这是三个模块,但是......哦,我的!至于把它变成一个OTP应用程序......你可能会结束一个&#34; supersup&#34;代表整个应用程序并管理服务器连接主管。服务器连接监督器是产生和管理IRC谈话者(每个谈话者代表服务器连接)和频道监督者的监督者。主通道支持将管理所有通道连接,或者每个服务器的通道支持将被生成 - 在任何一种情况下,通道处理程序都可以自由地拥有自己的非共享状态,因此如果有任何疯狂的事情发生在崩溃的通道上其中一个没有受到影响。
对于看起来更像的模块:
ircbot.erl
irc_sup.erl
irc_talker.erl
chan_sup.erl
chan_hand.erl
message_munger.erl
希望在散文中能够正确地看到这一点。这是一个可怕的非图表。
Supersup
ServerSups
IRCTalkers ChannelSups
ChannelHandlers
想象一下,你可以看到他们之间的沟通渠道以及他们需要说些什么来完成你的工作。确保你说出那些东西,并且事情会以一种使恢复变得明显和可重复的方式爆炸。
具体的优点是只在message-munging模块中编写纯函数。这个(可能永远不会实现)的优点是你可以用任何其他协议替换IRC说话者(只要新的说话者符合应用程序的向下&#34;内部协议)并使用相同的机器人代码与您想要的任何其他文字聊天服务进行对话。