Erlang tcp 服务器投票踢
Erlang tcp server vote kicking
我正在尝试编写一个简单的投票选项以添加到我现有的服务器中。
我有一个使用 gen_server 的消息路由器,它使用 init([]) -> {ok, dict:new()}
存储所有连接的客户端。路由器与 tcp 服务器是分开的,它也是一个 gen_server 但处理 tcp 请求,然后转发到路由器。
由于 "things" 被 handled/stored 分开,我想存储要被踢的用户、选民列表以及是否成功。因此,这需要在某种程度上至少在路由器内是全局的,并且最好与客户端命令分开。
对 best/ideal 方法有什么想法吗?
我相信我可以创建另一个 gen_server "router" 来存储人们踢入记录结构 {kick, {Votes, Passed}} 但我不知道这是否理想。
你想要"a user ID, a list of voters, and the outcome"。让我们看看它看起来像一个元组:
{User :: user_id(), [Voters :: user_id()], Outcome :: boolean()}
没那么难。这些元组的列表——也不难。这些的字典有点复杂,因为你最终得到(本质上,如果这是一个 proplist,使用上面的类型):
{User, {[Voters], Outcome}}
处理起来有点不愉快。例如,您可能想按结果过滤,但现在它被埋在一个元组中的一个元组中,而平面元组列表允许简单过滤(要么是一个真正的过滤器,要么只是保护列表理解,或其他)或使用lists:keysearch/3
。这会使复杂的搜索变得更加尴尬,尤其是当您想知道特定选民投票踢了某人多少次时。
使用 ETS 肯定可以使基本问题变得更容易,因为它具有以比使用 keysearch/3
的元组列表更快的方式完全处理元组的工具(而且,我假设你有数千和数以千计的情况,所以元组列表是不够的——但一如既往,先尝试一下,你可能发现元组列表是完全足够的!)。另一方面,如果搜索案例变得更加复杂,或者案例需要持久存储并仍然受益于内存操作,或者您需要多个索引等,那么您真的应该转向 Mnesia。
除此之外...您正在处理嵌套数据,虽然这实际上 很好 90% 的时间,但另外 10% 的时间您会结束要么重新发明关系数据(但更糟的是,实际效用更少,而且速度更慢),要么只是通过一堆巴洛克式的程序代码来实现一种效果,只需使用像 Postgres 这样的东西(我碰巧非常熟悉这个数据库,所以对我来说它是 "lightweight solution" -- 我知道不是每个人都这样)。
作为参考,您的数据在分解后如下所示(假设用户 ID 是电子邮件地址):
table user
attributes
id EmailAddress
conditions
pk id
table kick
attributes
id UUID
user EmailAddress
outcome Boolean
conditions
pk id
fk user
table vote
attributes
kick UUID
user EmailAddress
conditions
pk (kick user)
fk kick
fk user
虽然上述元组的 User
和 [Voter]
组件显然引用了用户记录,但为什么要拉出一个用户所投的所有选票的列表并不那么明显某些用户使用元组是一个令人讨厌的过程。当我们查看分解后的数据时,我们意识到这是三个表,如果您有此需求,我们真的希望我们可以 运行 单独查询 vote
一个。但是,如果您没有那个要求,那么不用担心,只需在 ETS 或 Mnesia 中使用元组即可! :-) 这种数据在 Erlang 中非常小。
我正在尝试编写一个简单的投票选项以添加到我现有的服务器中。
我有一个使用 gen_server 的消息路由器,它使用 init([]) -> {ok, dict:new()}
存储所有连接的客户端。路由器与 tcp 服务器是分开的,它也是一个 gen_server 但处理 tcp 请求,然后转发到路由器。
由于 "things" 被 handled/stored 分开,我想存储要被踢的用户、选民列表以及是否成功。因此,这需要在某种程度上至少在路由器内是全局的,并且最好与客户端命令分开。
对 best/ideal 方法有什么想法吗?
我相信我可以创建另一个 gen_server "router" 来存储人们踢入记录结构 {kick, {Votes, Passed}} 但我不知道这是否理想。
你想要"a user ID, a list of voters, and the outcome"。让我们看看它看起来像一个元组:
{User :: user_id(), [Voters :: user_id()], Outcome :: boolean()}
没那么难。这些元组的列表——也不难。这些的字典有点复杂,因为你最终得到(本质上,如果这是一个 proplist,使用上面的类型):
{User, {[Voters], Outcome}}
处理起来有点不愉快。例如,您可能想按结果过滤,但现在它被埋在一个元组中的一个元组中,而平面元组列表允许简单过滤(要么是一个真正的过滤器,要么只是保护列表理解,或其他)或使用lists:keysearch/3
。这会使复杂的搜索变得更加尴尬,尤其是当您想知道特定选民投票踢了某人多少次时。
使用 ETS 肯定可以使基本问题变得更容易,因为它具有以比使用 keysearch/3
的元组列表更快的方式完全处理元组的工具(而且,我假设你有数千和数以千计的情况,所以元组列表是不够的——但一如既往,先尝试一下,你可能发现元组列表是完全足够的!)。另一方面,如果搜索案例变得更加复杂,或者案例需要持久存储并仍然受益于内存操作,或者您需要多个索引等,那么您真的应该转向 Mnesia。
除此之外...您正在处理嵌套数据,虽然这实际上 很好 90% 的时间,但另外 10% 的时间您会结束要么重新发明关系数据(但更糟的是,实际效用更少,而且速度更慢),要么只是通过一堆巴洛克式的程序代码来实现一种效果,只需使用像 Postgres 这样的东西(我碰巧非常熟悉这个数据库,所以对我来说它是 "lightweight solution" -- 我知道不是每个人都这样)。
作为参考,您的数据在分解后如下所示(假设用户 ID 是电子邮件地址):
table user
attributes
id EmailAddress
conditions
pk id
table kick
attributes
id UUID
user EmailAddress
outcome Boolean
conditions
pk id
fk user
table vote
attributes
kick UUID
user EmailAddress
conditions
pk (kick user)
fk kick
fk user
虽然上述元组的 User
和 [Voter]
组件显然引用了用户记录,但为什么要拉出一个用户所投的所有选票的列表并不那么明显某些用户使用元组是一个令人讨厌的过程。当我们查看分解后的数据时,我们意识到这是三个表,如果您有此需求,我们真的希望我们可以 运行 单独查询 vote
一个。但是,如果您没有那个要求,那么不用担心,只需在 ETS 或 Mnesia 中使用元组即可! :-) 这种数据在 Erlang 中非常小。