《架构之美》阅读笔记3

Posted on 2021-01-31 21:27  九天龙凤  阅读(49)  评论(0编辑  收藏  举报

问题、品质需求

1. 系统的伸缩性需求。如大型在线游戏,需要满足大量用户。在线用户数量短时间内可能有很大的变化。 
这其中隐含的需求是: 
多用户 
并行 
分布式系统,系统运行在多台机器上 
高可扩展性(用于加入新的故事情节,意味着新的代码) 
高稳定性、可靠性(一个用户崩溃,不影响其他用户) 
数据一致性(多个用户看到同一个东西的状态应该是一样的) 
2. 架构设计目标 
即另外一个需求,对其他开发者部署出一个简单的编程模型,程序员可以将系统视为一个单机开发环境。 
隐藏分布式和并发是一件困难的事。需要一种严格限制的编程模型。 
典型的游戏服务器开发模型:反应式 
客户端(游戏机)(生成事件) - 服务端的事件监听器(监听事件,并生成任务) - 此任务可与多个客户端进行交互 
或者是服务端自己周期性生成任务。 
这是一种典型的胖客户端机制,适用于游戏和虚拟世界,也适用于J2EE和Web服务的应用。 
区别另外一种经典的企业级架构: 
瘦客户端 - 胖客户端 - 更胖的数据库服务器。服务器保存客户端的绝大部分信息,绝大多数真正的工作在服务器上完成。 
在游戏的软件架构中。不被修改的数据都被放在客户端完成,只有共享的数据才放在服务器,服务器尽量保持简单,减少计算。保持共享事实的最终来源,防止玩家作弊。客户端只访问少量的状态数据,但访问的数据大部分会被改写。 
而另外一种架构是90%的数据都是只读的,大多数任务会读取大量数据,再修改少量数据。 
3. 延迟的需求 
游戏架构要求用户体验好,大的延迟不被接受,甚至牺牲吞吐量换取少的延迟。 
而企业环境的架构重在吞吐量,管理业务。有一点延迟可以接受。 
一般情况下,处理拥塞的解决方案: 
1. 基于地理位置来实现。游戏设计包含不同的游戏区域,每个虚拟区域运行一台服务器,每个区域拥有自我限制功能,当人数过多时,服务拥塞,游戏变慢,趣味性下降,用户就转向更有趣的区域,响应时间就会得到改进。(对于棋牌类游戏,每个房间或区域有人数限制,满的房间可以限制进入) 
这种开发方法的问题:游戏设计时,需要决定哪些区域放在一台服务器上,而添加新的区域时比较容易,若改动原来的区域,可能需要改动代码,这些都是开发的工作量。 
2. 分区sharding。一个分区是一个区域的副本,运行在自己的服务器上,独立于其他分区,不同的玩家进入同一个区域的不同副本(分区)。这样的缺点时,不允许不同副本的玩家彼此进行交互。 
3. Darkstar架构就是克服以上缺点,支持随时伸缩,同时又不要求游戏逻辑受到伸缩影响。支持动态响应负载,而不是放在游戏设计中完成。 

Darkstar的架构

DarkStar是由一组服务组成。每个服务定义为一个小的编程接口。这些接口很像经典操作系统的服务,支持对服务端的访问持久存储、调度并执行任务、与游戏的客户端进行通信。 
这些服务的程序不会受低层实现变更的影响,因为每个服务由一个接口来描述。当接口不变时,一个服务的变更,不会影响其他服务的实现。这是一个"分治"的过程。 
另外,将基础设施设计为一组服务,可以将这些服务在不同场景下进行不同的组合,更加灵活,复用性强。一组服务可以组成一个Darkstar栈,Darkstar栈中具体包含哪些服务可以由一个配置文件来设置。 

从宏观结构上来看

(分层、模块化、通信机制)

每个Darkstar栈运行在一个服务器上,Darkstar栈就是服务的副本和游戏逻辑的副本。客户端连接到其中一个服务器,与该世界的抽象表示进行交互。

每个副本可以与客户端独立的交互,不需要处理相同事件。复制主要是用于支持伸缩性。游戏逻辑也不知道其他服务器上的副本。而不同服务器上的副本协作是由Darkstar项目的基础设施完成的。 
游戏客户端与服务端的通信机制包含两种,一种是直接通信,另外一种是"发布-订阅"模式。 
Darkstar栈由一组元服务来协调,这是一组网络访问服务,对游戏程序员是不可见的。这些服务支持在线的副本相互协作,共同运营整个游戏。而且副本间相互独立,某副本失效,会发起失效恢复操作。 

此外,这些Darkstar元服务会跟踪各副本的负载,在需要的时间重新分配负载,或随时添加服务器,增加总体容量。

 

基本的组件/服务

对于游戏程序员而言,可见的架构就是栈中包含了一组服务,而4个基本服务是必须的。数据服务、通道服务、客户端会话服务、任务服务。

 

数据服务 
数据存储、数据读取、数据操作。 
特征:大部分都是持久化数据、数据间静态关系少、很少有复杂查询、需要对延迟进行优化、需要修改的数据多 
选型:与标准数据库的使用方式有区别、数据库简单的命名策略,编程语言上对对象的引用 
任务服务 

用于调度、执行任务。

任务来源:响应某个事件、或游戏内部逻辑触发的。常见任务的操作包括:从数据服务中读取、修改一些数据,可能会有一些通信、或者生成其他的任务。

时间限制:执行任务的时间必须很短。配置中可以设置,默认值是100ms。 
底层特征:Darkstar的底层在尽量的调度最多的任务。但对游戏程序员,因事件只响应一个任务。 
并行性:客户端产生的任务、服务端响应逻辑的任务是并行执行的。 
数据竞争:由并行性带来的数据竞争,同步、事务性操作(一个操作要么全部成功,要么回退) 
通信机制:若任务包含事务操作,通信机制也必须支持事务 
通信服务、会话服务: 
登录、认证后建立会话,监听客户端消息,并响应生成任务。会话也负责维持消息的顺序,前一消息未处理完,后一消息就先不提交。这样也让"任务服务"得到了简化。因为任务服务假定其任何时候触发的任务都是并发的。 
通道服务: 
一对多的通信机制。客户端之间可以交互,这种方式不能采取让客户端直接通信的方式,虽然这样会减轻服务端的负载,但容易产生作弊行为。所有通道信息都必须经过服务器。 
这些抽象层不会暴露客户端或服务端的真实端点,这样Darkstar系统能将服务端的通信端点从一个服务器上移到另外一个机器上,同时又不改变客户对通信的感觉。底层的基础设施可以根据需要进行动态调整。 

任务的可移动性

要实现负载的动态均衡能力,就应该实现任务的可移动性,从一台机器移到另外一台机器上。这是系统伸缩性的要求和目标。 
技术要求: 
1. 任务是由java写成,这样物理机器上运行JVM,任务就可以运行 
2. 任务数据来源。任务的数据都来自数据服务。而数据服务是所有游戏实例和Darkstar栈所共享的。 
3. 通信由"会话服务"或"通道"来实现中介。其抽象能力,使得特定的会话由一个服务器移向另外一个服务器。 
4. 负载的监控。Darkstar元服务的工作,这是网络层面的服务,对程序员不可见,但对Darkstar栈中的服务是可见的。不会影响游戏的逻辑性,而能实现系统的伸缩性。 
5. 利用伸缩机制来实现系统的高容错能力。由于任务和通信机制是与具体机器无关的,且任务本身也是持久对象,保存在"数据服务"中。若任务中断,等同于事务中断,系统会重新调度不同的机器运行。这样的后果是延迟有点长,但系统的正确性不变。 

游戏架构的思考、改进

游戏与虚拟架构的特点: 
1. 由于此行业的保密性,专用性, 其性能测试,数据指标比较难获取,另外很多偶然性的因素,使得无法对不同的基础设施做比较,对通用基础设施的测试更加困难。 
2. 很多服务器的架构都是定制的,很少有可复用的基础设施假设,不注重这方面的积累。 
Darkstar架构是围绕服务器性能做出了很多关键决定。 

1. Darkstar拒绝在服务器中存放任何重要信息。

所有生存周期超过一个任务的数据都需要放入"数据服务"中统一管理。这是Darkstar的核心。为什么?因为它能检测数据的并发问题,对游戏程序员隐藏这些细节,让服务器能利用多核架构,并实现整体是伸缩性。

2. 延迟的分析。

以上对并行性的处理,会引发一些延迟。将数据放入内存,才能将延迟最小化,是主流观点。

而采用"数据服务"的方式会影响性能,访问数据会引入一定延迟。但比其他方法会更有竞争力:1)持久化存储可以利用数据库缓存和一致性,尽量减少数据访问延迟。2)将相关联的玩家放到一个服务器上去,也可以利用数据库的标准缓存技术,减少访问和保存持久数据的延迟。(与基于地理位置的技术不同,这部分工作无需放入游戏开发工作中,而是根据运行时进行优化,(类似于编译优化与运行优化))

3. 可靠性更高。

利用高并发来弥补持久化存储的延迟损失,方案总体上是更有优势,也更符合未来芯片基础架构发展的方向。另外,持久化"数据服务"将服务器失效而导致的数据丢失减少到了最小。

4. 简化游戏程序员工作。 
当同时要求支持伸缩性和减少延迟的开发目标,那么开发者需要编写自己的分布式和多线程基础架构和代码,这对开发者的要求更高,且工作量更大。而Darkstar将所有任务封装到事务中,并在"数据服务"中检测数据冲突,开发者就能享受多线程的好处,又不必在他们的代码中引入锁协议、同步和信号量。 Darkstar提供透明的负载平衡。 

但是开发者还是需要了解Darkstar的底层并发和分布式实质,需要遵循一定编程模型,尽量利用数据服务的并发性,提升游戏的整体性能。