2

基本上我想要一个在服务器上运行的 Java、Python 或 C++ 脚本,监听玩家实例:加入、跟注、下注、弃牌、抽牌等,并且在玩家离开或断开连接时也有超时。

基本上我希望这些动作中的每一个都是一个小请求,以便玩家可以是同一台机器上与游戏服务器通信的进程,也可以是跨网络的机器。

消息传递的安全性不是问题,这是为了学习/研究/娱乐。

我的优先事项:

  1. 有一个很好的方案来检测玩家何时断开连接,但也能够在启动/导致失手之前考虑网络延迟等。
  2. 速度。我将尽可能快地玩数百万这样的牌。
  3. 在共享服务器实例上运行(我可能对端口或需要 root 的东西的访问权限有限)

我的问题:

  1. 监听端口或使用套接字或 HTTP 端口 80 apache 监听脚本?(我对这些之间的区别有点模糊)。
  2. 有什么好的框架可以解决吗?
  3. 消息类型?我在考虑 JSON 或协议缓冲区。
  4. 如何让它快速?

谢谢大家 - 只是在寻找一些指示和建议。我认为这是一个很酷的问题,需要学习很多整洁的东西。

4

3 回答 3

3

就框架而言,Ginkgo看起来很有希望用于构建网络服务(这就是您正在做的事情)。Python 非常简单,gevent启用的异步性使您可以执行异步操作,而无需担心回调。gevent 核心还使您可以访问许多构建块

与其让大量服务通过端口进行通信,不如考虑1)一个好的消息队列,如RabbitMQ0mq,或2)一个分布式协调服务器,如Zookeeper

话虽如此,您的目标是困难的,尤其是在您不熟悉基础知识的情况下。学习这些基础知识是值得的。

一开始不要担心速度。让它工作,然后让它规模化。当然,您可以采取一些方向,以便将来更容易扩展。特别是Zookeeper为您提供了易于实现的用于水平扩展的原语(即多个工作人员分担负载)。特别是,请参阅Zookeeper 食谱书及其相应的python 实现(由kazoo提供,一个基于 gevent 的客户端库)。

不要忘记“快速”还意味着优化您自己的开发时间,以加快迭代速度并减少对开发环境的诅咒。所以使用 Python,它可以让你现在快速启动并运行,如果你真的开始绑定 CPU 时间或内存使用,稍后再进行优化。(使用这个特定的应用程序,您更有可能在网络 IO 上进行绑定。)

于 2012-10-18T20:56:07.953 回答
2

还要别的吗?也许一杯咖啡来回答你的问题:-)

从头开始回答您的问题将需要几本书,其主题范围从基本的 TCP/IP 网络到可扩展架构,但我仍会尝试为您提供一些指导。

问题:

  1. 监听端口或使用套接字或 HTTP 端口 80 apache 监听脚本?(我对这些之间的区别有点模糊)。

我敢冒昧地说,如果您不清楚其中每一个的定义,那么设计一个实现“尽可能快地玩数百万手牌”的服务可能有点过头了?但是不要让它阻止你,因为他们说“无知是幸福”。

  1. 有什么好的框架可以解决吗?

我认为您的项目非常适合 Node.js。主要原因是 Node.js 相对可扩展,并且擅长隐藏可扩展性所需的复杂性。Node.js 也有缺点,只需 Google 搜索“Node.js 可扩展性批评”。与使用更通用的框架相比,Node.js 的主要观点是可扩展性很困难,没有办法绕过它,而且 Node.js 如此高级和具体,为解决问题提供的选择较少。另一个缺点是 Node.js 是 Javascript,而不是您喜欢的 Java 或 Phyton。

  1. 消息类型?我在考虑 JSON 或协议缓冲区。

我认为客户端和服务器之间不会有很多流量,所以我选择 JSON 并不重要,因为它更普遍。

  1. 如何让它快速?

真正的问题是如何使其可扩展。运行人类与人类的纸牌游戏并不是计算密集型的,因此您可能会在达到任何计算限制之前耗尽 I/O 容量。通过将负载分散到机器上来克服这些限制。在多人游戏中,常见的做法是拥有一个列表服务器,该列表服务器提供指向相同游戏服务器的链接,每个服务器都有预定义数量的可供玩家使用的插槽。这是 broker-workers 架构的一种变体,broker 机器根据客户端的繁忙程度为客户端分配工作机器。在游戏中,用户希望能够选择他们的服务器,这样他们就可以和他们的朋友一起玩。

有关的:

  1. 有一个很好的方案来检测玩家何时断开连接,但也能够在启动/导致失手之前考虑网络延迟等。

由于这是人类时间尺度(秒而不是毫秒),因此客户端应该每 10 秒发送一次 keepalive,例如 30 秒会话超时。keepalive 将是您的应用程序协议中的 JSON 消息,而不是 HTTP,后者较低级别并由框架处理。框架本身应该为您提供 HTTP 1.1 连接管理/池,它允许多个 http 会话(请求/响应)通过同一连接,但不要求客户端始终连接。这是可靠性和速度之间的良好折衷,对于回合制纸牌游戏应该足够好。

于 2012-10-18T04:01:22.963 回答
1

老实说,我会从经典的 LAMP 开始。拿一个库存的 Apache 服务器和一个 mysql 数据库,并将您的 Python 脚本放在 cgi-bin 目录中。他们发送和接收 JSON 而不是 HTTP 的事实并没有太大区别。

当然,这显然不会是最灵活或可扩展的解决方案,但它会迫使您尽早面对实际问题。

您将遇到的第一个问题是游戏状态。你声称没有共享状态,但这是不对的——牌组中的牌,桌上的赌注,轮到谁了——这都是状态,在多个玩家之间共享,在服务器上管理。这些命令中的任何一个还能如何工作?因此,您需要某种方式在 CGI 脚本的不同实例之间共享状态。经典的解决方案是将状态存储在数据库中。

当然,您还需要首先处理用户会话。细节取决于您选择的会话管理方案,但最大的问题是如何将断开/超时从较低级别传播到应用程序级别。如果有人把 20 美元放在桌子上然后断开连接会发生什么?您必须考虑所有可能的用例。

接下来,您需要考虑可伸缩性。你想要数以百万计的游戏?好吧,如果有一个包含所有游戏状态的数据库,那么您可以在它前面拥有任意数量的 Web 服务器——John Doe 可能在 server1 上,而 Joe Schmoe 在 server2 上,但它们可以在同一个游戏中。另一方面,你可以为每个服务器建立一个单独的数据库,只要你有办法强制同一游戏中的人们在同一服务器上见面。哪一个更有意义?无论哪种方式,您如何在服务器之间进行负载平衡。(你不仅要让他们都忙,还要避免4个玩家都准备好了,但是他们在3个不同的服务器上,所以他们不能互相玩……)。

这个过程的最终结果将是一个以你希望的 1% 的容量运行的服务器的巨大混乱,你不知道如何维护。但是您会更详细地考虑您的问题空间,并且您还将学习服务器开发的基础知识,从长远来看,这两者可能更重要。

如果你有时间,我接下来会把整个事情扔掉,通过设计一个自定义 TCP 协议从头开始重写所有内容,用 Twisted 之类的方式为它实现一个服务器,将游戏状态保存在内存中,并编写一个简单的自定义代理而不是标准负载均衡器。

于 2012-10-18T20:31:32.197 回答