理解游戏服务器架构-部署架构

目录

前言

我所理解的服务器架构

什么是否部署架构

部署架构的职责

进程业务职责

网络链接及通讯方式

   与客户端的连接方式

   服务器之间连接关系

数据落地以及一致性 

数据库的选择

数据访问三级缓存

数据分片

 读写分离

分布式数据处理

负载均衡

热更新

配置更新

合服

合服的痛点

解决合服数据重叠问题

总结:


前言

       本人做游戏多年,当时从开发棋牌游戏开始阴差阳错进入游戏行业,服务器架构做过很多套,我曾经的小伙伴拿着我设计的架构带到新的公司,都有一个不错的表现。但是发现一个现象,后面设计的架构永远比前面设计的更为优秀或良好,也许随着时间的推移,个人技术以及新技术与理念的引入、新的业务场景等多种原因,让我们不得不对架构做一定的调整。

        我经常问我的求职者一个问题,你怎么理解服务器架构,架构两个词从字面上理解很好理解,如果对于一个系统工程了说,它可以理解为系统工程的顶层设计,如果是一个简单的单一功能,那它是单一个功能总体设计思路。   但是我们今天所讲的架构是一个承载百万人同时在线的架构设计,注定它不是一个简单问题。它包含着技术开发人员所需要掌握的技术需要的广阔性和深度,并拥有强力的设计理念。

我所理解的服务器架构

服务器架构我的理解为三个方面,它包含部署架构和逻辑底层架构、业务架构。

                

什么是否部署架构

部署架构是在考虑业务职责和各个场景的节点隶属关系、连接关系以及业务分割的服务器的概括性架构,它确定了服务器进程之间的部署关系。

部署架构的职责

     在这一层,我们应该抛开编程语言的限制,考虑进程业务职责,网络连接形式、数据落地以及一致性问题、扩展性问题、负载均衡问题、稳定性问题、热更新问题等

                        

进程业务职责

进程业务职责我们应该考虑,根据业务进行功能拆分,并确定进程应该做什么和它主要功能是什么,是我们赋予各个进程具体职责。

以游戏服务器为例,它应该需要为用户做登录认证的登录服;职责为统一网络连接和安全的网关服;为任务和养成系统等玩家具体玩法需要的游戏服;担负着全服排行榜和ID分配等全局功能功能的世界服;需要好友、聊天 等功能的社交服;需要保存数据的数据库服等

网络链接及通讯方式

   与客户端的连接方式

        当我们确定了业务职责时,那么用什么样的通讯方式就是我们具体要考虑的问题。首先我们

        需要思考一个问题,我们的业务那些进程需要与玩家进行连接呢?在游戏中一般,登陆服和

        网关服是需要与玩家进行连接。

        登陆服往往通过http的协议进行验证登陆,让然也可以选择TCP方式,以及后期发展的

        websocket的方式。websocket我们可以看着是http的变种,其实它最核心的还是http。

        当然根据实际业务场景有可能选择udp。帧同步的战斗服就是可确认的rudp协议方式。

             

   服务器之间连接关系

        在服务器部署架构中,我们要确认的一项重要内容之一就是多个服务器进程之间能够直接通

        讯。某个业务进程是被动连接还是主动连接?是一个连接还是多个连接?是一对多的关系还

        是一对一的关系?

      在连接关系中,我们同时要考虑服务器进程之间用何种方式进行通讯。我最近设计的一套服务 

      器架构,通过zookeeper进行服务器配置更新,每个服务器有自己的一个服务器节点ID,服务

      器节点ID通过zookeeper获得自己配置信息,同时将本节点注册到zookeeper上,其他节点监听

      到某个节点注册信息,zookeeper按照配置规则进行节点自动连接,一切做到自动化。

     在数据库的设计方式,我将数据功能设计成SDK的方式,数据库节点是作为server端,而使用

     数据库的为Client端,任何节点想要数据库的操作直接使用数据库SDK就可以,非常方便。

          

数据落地以及一致性 

        数据库的选择

对于业务,我们应该充分分析它的属性以及业务当量,根据业务属性和当量我们选择合适我们自己需要的数据库类型,通用的数据库有mysql、orcale、mongoDB

             

但们也有各自的缺点,MySQL、Oracle和MongoDB各自的缺点如下:

  1. MySQL:功能相对较少,并发控制能力有限,数据安全性较低。
  2. Oracle:劣势在于成本较高,不是开源数据库,需要购买许可证才能使用。
  3. MongoDB:不适合存储关系型数据,数据一致性相对较弱,且没有类似SQL的查询语言,查询功能相对较弱

对于游戏开发,游戏本身的数据库我可能选择mongodb,因为完美的将player玩家对象整个丢进我们的数据库中,简单并且高效实现。当然在window系统下,你也可以选择sqlServer,但是发它只能在window操作系统下运行。

数据访问三级缓存

在早期的数据访问和存储是进程直接操作数据库的。由于数据库数据往往保存在磁盘中,存储和访问效率就往往是非常低,尤其是访问量比较大时容易造成拥堵。

所以后来为了提高访问效率,在用户个人数据方面,为了提高访问数据的效率可能我们需要用到三级缓存机制,即:memory、redis/memcache、db数据落地三层设计。

                

对于热点数据,首先我们从memory中查找,没有在查找redis或memcache,最后才查找database。

数据分片

如果一个业务逻辑都在某个进程中,那么数据一致性相对就简单了许多。那么更多的考虑的是因为单点会不会出现执行效率和性能的问题以及如何提高承载上线,可是问题并没有那么简单。

例如:游戏服的社交往往处理好友、聊天、帮会等通用数据,如果在拥有天量用户的游戏中,我们不可能一个服务器节点保存所有的这些数据。可能进行数据的在进程的分片,数据如何分片就需要我们做一定的考量。同样我们的角色数据不可能只有一个数据库,可能需要架设数据库服务器集群来做处理,同样表格数据也需要分片等等,让访问数据更为高效。

分片算法:

            

 读写分离

当然我们不可能所有数据都进行分片,比如拍卖行数据以及处理。拍卖行数据这个系统的特性是用户基数达到一定量及的时候,读数据的压力比改数据的压力会大很多,在设计初期我们可能需要考虑读写分离。

读写分离适用于程序使用数据较多,而更新较少、查询较多的情况。此时,设计主从主从同步,可以减少数据库压力,提高性能。

拍卖行或者商行,更新相对较小,访问量较大,当我们在全局服更新数据时,同步更新到其他服,玩家访问拍卖行或者商行时,访问的是本服的即可,这样减少了全局服的压力。

数据库数据的IO(分库分表)

在服务器性能瓶颈中往往数据库的IO会拖累整个优秀的程序设计。

MySQL为例:数据库中的表超过1000000条记录时,效率会受到多种因素的影响。以下是一些可能影响数据库效率的关键因素:

硬件性能:

  • 磁盘I/O:SSD比传统的HDD更快。
  • 内存:足够的RAM可以确保更多的数据和索引被缓存在内存中。
  • CPU:强大的CPU可以更快地处理查询。

数据库设计:

  • 表结构:正确的数据类型、适当的索引和分区可以提高查询性能。
  • 索引:为经常用于查询条件的列创建索引。
  • 规范化:避免不必要的数据重复。

查询优化:

  • 查询语句:避免使用SELECT *,只选择需要的列。
  • 避免全表扫描:使用WHERE子句和索引来限制查询的数据量。
  • 连接优化:尽量减少表之间的连接,特别是当连接条件没有索引时。

数据库配置:

  • 缓冲池大小:例如,InnoDB的缓冲池大小应足够大,以容纳大部分的数据和索引。
  • 日志和二进制日志设置:这些设置可以影响写入的性能。

并发性:

  • 高并发读写可能会导致性能下降。
  • 使用连接池可以减少创建和关闭连接的开销。

数据分布:

  • 如果数据分布不均,某些查询可能会更慢。

网络延迟:

  • 如果应用程序和数据库不在同一台机器上,网络延迟也可能成为问题。

了解了更多的数据库引起瓶颈问题,除了设计修改硬件和配置属性时,我们应该避免大的表格的出现;同时,尽可能避免只有一个全局数据库的可能性。

所以应当对于大表格数据进行分表,对于一个数据无法承载的应当通过集群和分库的方式解决数据IO的瓶颈。

分布式数据处理

服务器数据的落地不应该仅仅考虑当前问题,而要考虑后期扩容或随着时间的变化服务器减少问题。不应该只考虑本线程问题而考虑跨线程问题;只考虑本进程而不考虑跨进程问题。

在实际运用中,有可能有多个进程进行同样的逻辑处理,只是处理不同的数据而已。例如:社交服处理的都是社交相关的逻辑的数据,但是一个玩家的涉及需要修改的数据只能在单个社交服处理,不能两个社交服都可以修改一个玩家的同一份数据。

一句话总结,对于数据落地,我们尽可能让数据安全地保存,同一份数据的修改同时有且只有一个地方进行修改,并用一切手段提高访问和落地的效率。

负载均衡

负载均衡是决定了游戏稳定和承载上线的总要环节,作为服务器后台开发,那一定要知道我们的瓶颈在哪里,要知道单个进程的承载和单个服的承载,要对服务器前期要有个预设值,通过这个这个预设值来进行总体的实际。服务器的瓶颈通用的有网络IO,数据库IO、内存、CPU性能等问题  

         

我们不能实现一个服务器只有单一节点的运用。如果有,那么它的体量也大不到哪里去。所以我们要知道如何进行负载均衡,如何对服务器进行横向分布,如果在游戏上线前期没有做充分的准备,出现问题都有可能是对项目致命的问题。我们不仅仅考虑正确的情况下,同时我们要对外部危险攻击做好相应的准备。

在这里分享两个例子:

第一个案例:

有一天我曾经下面的一个小伙伴去其他公司负责整个地方棋牌项目。棋牌项目的特点就是被ddos攻击的重灾区,同样类型的游戏,市场就那么大,你抢别人的蛋糕,不有点硬技术可不行。当时小伙伴打电话给我,问怎么解决?最终我发现它的登录和网关是没有做负载均衡的。对方一直打他们的login服务器和网关,登录和网关它们没有做负载均衡,这种情况只能先上高防,后期把负载均衡加上,出现类似的情况,将新玩家导入到新的服务器,让ddos攻击有高防的机器,这样影响就小了很多。当然还有很多技术细节,这里就不一一列举了。由于小伙伴在这块考虑不足,负载均衡没有加上,最后这个项目刚开始一周就胎死腹中。

第二个案例:

我面试过应聘我们后端小主程的一个小朋友,他们公司做了一个大IP项目,腾讯发行他们家的游戏,社交、工会、跨服战等功能都在单个全局服,最后发现就是这个单个全局服成了整个游戏服务器的败笔。腾讯当天导量300多万,直接将服务器整崩溃了。腾讯推的量一般是短时间巨量,当导入巨量项目组接不住时,腾讯不会再给你导入多少量了,这个项目实在可惜。

以两个失败的案例告诉我们,一个差的架构可以间接杀死一个好的项目,服务器架构的最大体现往往在游戏上线的那刻尤为重要。负载均衡没有做好,可能造成项目不可挽回的损失,给我们巨大的流量和推广也无法接住,着实可惜。

为了实现负载均衡,我们可以通过nginx、动态服务器列表、设计上扩容组合实现。在登录时,一定要设计排队功能,当有大当量用户突然访问时,最起码有一个保底机制不至于出现登录挤兑情况影响。

     

Nginx(engine x)是基于 C 语言实现的一个高性能、轻量级的 HTTP 和反向代理 Web 服务器,同时也提供 IMAP/POP3/SMT服务。

Nginx 既可用作静态服务器,提供图片、视频服务,也可用作反向代理或负载均衡服务器。Nginx 作为反向代理,当代理后端应用集群时,需要进行负载均衡。

Nginx 提供了对上游服务器(真实业务逻辑访问的服务器)的负载均衡、故障转移、失败重试、容错、健康检查等功能,以一种廉价有效透明的方法扩展了网络设备和服务器的带宽、增加吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性。
Nginx 具有高并发连接、低内存消耗、低成本、配置简单灵活、支持热部署、稳定性高、可扩展性好等优点,这些优点都得益于其优秀的架构设计(模块化、多进程和多路I/O复用模型)。

                                     

热更新

服务器热更新是指在不停机或者不关服的前提下,对服务器上的应用程序或系统进行更新。它的好处在于能够保证系统的高可用性,避免了系统在更新过程中的不可用现象,并且将系统升级的周期缩短到了最小

实践证明,每次重启服务器,都有可能一定概率造成玩家的流失,现如今获取一个付费用户是多么的不容易。更新可以分为,配置更新、代码更新、合服等

        

配置更新

配置更新是最频繁的更新,极限情况一天可能都要更新好几次,如果每次都要重新启服来进行更新,损失太大了。频繁的启服对用户的体验很不友好,同时让人不敢在游戏中消费。

所以配置的更新一定是热更新实现。当配置需要更新的时候,可以通过GM命令通知各个服务器,进行配置的重新装载。

代码热更新

对于代码,并不是所有代码都支持热更新的,对于Java它所支持的热更程度为代码的函数和成员变量都不能改变,但是函数内部的实现能够变化。如果是代码C++和Lua的结合,能够比较友好的实现代码热更新。所以代码的更新也取决与所选择的逻辑编程语言。

对于语言的限制技术上往往是有无力感的,因为对于技术的我们没有办法做什么。但是我有一种策略是可以将影响做到最小,当我们需要更新代码的时候,可以将现有玩家快速切换到更新好了的服务器,同时将当前服务器关闭进行更新,等当前服务器更新成功后再让新玩家进入当前服务器。当然这种情况玩家并不会无感,但起码影响小了很多。

合服

合服的痛点

肯定奇怪,合服为什么属于热更新范围内。其实合服也是更新的一部分。

合服通用做法是将需要合服的服务器关闭,同时进行数据库合并。但是并没有那么简单,因为1服和2服的数据往往不相通的,那么某些ID作为数据Index(索引)就有可能重叠问题。

比如:玩家数据库表格以playerId作为index(索引),采用自增的方式,1服如此,2服如此,自然就会有重叠的地方。而且之中情况是连锁反应的,因为玩家的道具、任务表格都是以playerId作为关键index(索引),这就一一去重,非常麻烦,即使我们客户通过工具来实现,实际起来非常的费劲,总有特例需要处理。

解决合服数据重叠问题

能够有一个长效机制让我们合服不怎么麻烦呢?答案肯定是有的,ID的唯一性其实给了启发。如果有一个机制能够让ID在不同进程之间产生不同无法重叠的ID,那么是否不是就可以解决ID重用问题?

是的,我们可以参考雪花算法。雪花算法生成的ID为64位整数,具体的格式如下:

0 | 0000000000 0000000000 0000000000 000000000 | 00000 | 00000 | 000000000000

其中,第1位为符号位,固定为0;接下来的41位为时间戳(毫秒级),记录了生成ID的时间;然后是10位的机器ID,5位数据中心ID和5位工作机器ID,用于标识不同的机器;最后是12位的序列号,用于表示在同一毫秒内生成的多个ID的顺序。

实际使用时,每台机器需要配置一个唯一的机器ID,以保证生成的ID不与其他机器生成的ID重复。同时,需要注意时钟回拨的问题,即当本地时钟发生回拨时,可能会导致生成的ID出现重复或者乱序的情况。

当然这只是抛砖引玉,还有其它类似算法,这里就不一一详举了。

我们在给角色命名或修改名字的时候,同样会出现类似的重叠问题。这以问题的解决就有待于读者去思考了。

总结:

在部署架构阶段,我们考虑的是我们架构设计的愿景,或者说是我们向往架构的设计的蓝图。告诉我们将要做什么,确定我们最重要的目标和方向,划分职责。犹如宪法在法典中的意义一样重要,它是指导思想。接下了的逻辑顶层架构设计以及业务架构设计都要依照部署架构的设计而进行落地。

我们应该抛开语言而考虑每个进程需要做什么,它们之间的连接关系,数据落地和分割,瓶颈问题的解决,这样从全局上考虑问题,思路就非常清晰了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/502694.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

express实现用户登录和注册接口

目录 1 创建数据库2 连接数据库3 集成ORM库4 创建业务逻辑5 创建路由7 测试接口总结 我们在编写后端接口的时候操作数据库是一种常见的功能需求,express本身并不提供直接操作数据库的能力,需要借助第三方库来操作数据库,本篇讲解一下软件开发…

【Java】LinkedList模拟实现

目录 整体框架IMyLinkedList接口IndexNotLegalException异常类MyLinkedList类成员变量(节点信息)addFirst(头插)addLast(尾插)在指定位置插入数据判断是否存在移除第一个相等的节点移除所有相等的节点链表的长度打印链表释放回收链表 整体框架 IMyLinkedList接口 这个接口用来…

2006-2023年2月各地级市城投债详细数据

2006-2023.2各地级市城投债详细数据 1、时间:2006-2023.2 2、来源:深圳证券交易所和上海证券交易所官网、人民银行、证券监督管理委员会等金融监管机构等官网 3、指标:省份、城市、证券代码、证券简称、债券简称、证券全称、债券初始面值单…

C语言使用STM32开发板手搓高端家居洗衣机

目录 概要 成品效果 背景概述 1.开发环境 2.主要传感器。 技术细节 1. 用户如何知道选择了何种功能 2.启动后如何进行洗衣 3.如何将洗衣机状态上传至服务器并通过APP查看 4.洗衣过程、可燃气检测、OLED屏显示、服务器通信如何并发进行 小结 概要 本文章主要是讲解如…

使用pdf表单域填充pdf内容

需要引用如下包 <dependency><groupId>com.itextpdf</groupId><artifactId>itext-core</artifactId><version>8.0.3</version><type>pom</type></dependency>1、预先准备一个pdf模板&#xff0c;并在指定位置添加…

创建一个vue3 + ts + vite 项目

vite 官网&#xff1a; https://cn.vitejs.dev/guide/ 兼容性注意 Vite 需要 Node.js 版本 18&#xff0c;20。然而&#xff0c;有些模板需要依赖更高的 Node 版本才能正常运行&#xff0c;当你的包管理器发出警告时&#xff0c;请注意升级你的 Node 版本。 安装项目 1. 使用n…

政安晨:【Keras机器学习实践要点】(五)—— 通过子类化创建新层和模型

目录 介绍 安装 层级&#xff1a;状态&#xff08;权重&#xff09;与某些计算的组合 层可以有不可训练的重量 最佳实践&#xff1a;推迟权重的创建&#xff0c;直到输入的形状已知。 层可以递归组合 后端不可知层和特定后端层 add_loss()方法 可以选择在您的层上启用…

【DETR系列目标检测算法代码精讲】01 DETR算法01 DETR算法框架和网络结构介绍

为什么要有DETR 总所周知&#xff0c;传统的目标检测算法非常依赖于anchor和nms等手工设计操作&#xff0c;非常费时费力&#xff0c;自然而然的就产生了取消这些操作的想法。但是我们首先需要思考的是&#xff0c;为什么我们需要anchor和nms&#xff1f; 因为我们是没有指定…

3D汽车模型线上三维互动展示提供视觉盛宴

VR全景虚拟看车软件正在引领汽车展览行业迈向一个全新的时代&#xff0c;它不仅颠覆了传统展览的局限&#xff0c;还为参展者提供了前所未有的高效、便捷和互动体验。借助于尖端的vr虚拟现实技术、逼真的web3d开发、先进的云计算能力以及强大的大数据处理&#xff0c;这一在线展…

Docker Swarm安装部署应用

一、Docker Swarm核心概念 1、什么是Docker Swarm GitHub地址 Docker Swarm 是 Docker 官方推出的容器集群管理工具&#xff0c;基于 Go 语言实现。使用它可以将多个 Docker 主机封装为单个大型的虚拟 Docker 主机&#xff0c;快速打造一套容器云平台。 Docker Swarm 是生产…

Java线程池工作原理浅析

为什么要用线程池&#xff1f; 1、线程属于稀缺资源&#xff0c;它的创建会消耗大量系统资源 2、线程频繁地销毁&#xff0c;会频繁地触发GC机制&#xff0c;使系统性能降低 3、多线程并发执行缺乏统一的管理与监控 线程池的使用 线程池的创建使用可通过Executors类来完成…

【网站项目】泉文化管理系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

Vue2(十二):Vuex环境搭建、Vuex工作原理、几个配置项、多组件共享数据、Vuex模块化

一、Vuex 1.概念 专门在Vue中实现集中式状态&#xff08;数据&#xff09;管理的一个Vue插件&#xff08;use引入&#xff09;&#xff0c;对vue应用中多个组件的共享状态进行集中式的管理&#xff08;读&#xff0f;写&#xff09;&#xff0c;也是一种组件间通信的方式&…

String,StringBuffer,StringBuilder 的区别【大白话Java面试题】

String&#xff0c;StringBuffer&#xff0c;StringBuilder 的区别【大白话Java面试题】 大白话回答 1、可变/不可变类 String是不可变类。他被被final修饰&#xff0c;所以每一次的创建修改删除都要重新分配内存创建新的对象。 StringBuilder和StringBuffer是可变类&#xff…

Linux部署Sonarqube+Gogs+Jenkins(一)

Linux部署SonarqubeGogsJenkins 一、1.Linux安装JDK11环境1. 本地进行上传2. 进入到/usr/java目录&#xff0c;并且进行解压3. 配置文件/etc/profile&#xff0c;配置环境变量4.让对应的配置文件生效5. 验证 二、Linux安装Python环境三、Linux安装Jenkins环境1、/usr目录下创建…

ssm框架笔记-maven

html是骨头 css使皮肤 js是你能做的动作 MAVEN 依赖管理&#xff1a;1.声明dependenciys标签 2.maven search3。 版本号提取 3.$引用 3.2依赖传递和冲突 依赖传递指的是当一个模块或库 A 依赖于另一个模块或库 B&#xff0c;而 B 又依赖于模块或库 C&#xff0c;那么 A 会间…

ADT 创建表,并用ABAP往里面插数据

参考&#xff1a;Create Table Persistence and Generate Data | SAP Tutorials 4、Replace your code with following: CLASS zcl_generate_travel_data_xxx DEFINITIONPUBLICFINALCREATE PUBLIC .PUBLIC SECTION.INTERFACES if_oo_adt_classrun.PROTECTED SECTION.PRIVATE S…

基于SSM医院病历管理系统

基于SSM医院病历管理系统的设计与实现 摘要 病历管理系统是医院管理系统的重要组成,在计算机技术快速发展之前&#xff0c;病人或者医生如果想记录并查看自己的健康信息是非常麻烦的&#xff0c;因为在以往病人的健康信息通常只保存在自己的病历卡或者就诊报告中&#xff0c;…

【C++】vector的介绍及使用说明(类模版的实现方式,顺序存储与动态数组,迭代器iterator的运用,vector的增删查改)

目录 00.引言 01.vector的介绍 类模版 动态分配内存 顺序存储 02.vector的使用 构造函数 迭代器iterator 1.分类&#xff1a; 2.运用&#xff1a; 扩容 1.resize() 2.reverse() 增删查改 1.增加 2.删除 3.查找 4.修改 00.引言 以前我们讲过string类&#xff0…

如何系统的自学python?

系统地自学Python是一个循序渐进的过程&#xff0c;以下是一份详细的指南&#xff0c;帮助你从零开始逐步掌握这门语言&#xff1a; 1、了解Python及其应用场景&#xff1a; 阅读关于Python的简介&#xff0c;理解它为何流行&#xff0c;以及在哪些领域&#xff08;如Web开发…