共计 4373 个字符,预计需要花费 11 分钟才能阅读完成。
用 Go 语言写游戏服务器也有一个多月了,也可能显著的感触到两者的区别。这篇文章就是想具体的聊聊其中的区别。当然,在理解区别之间,咱们先简略的理解一下 Go 语言自身。
PS:这里仅拿 SLG 手游举例子
1. Go 语言的特点
Go 语言跟其余的语言例如 Java 比起来,算得上一门很年老的语言。Go 语言是由 Robert Griesemer、Rob Pike 和 Ken Thompson 于 2007 年在 Google 开发。并于 2009 年正式公布。
Go 语言的设计理念围绕着简洁这两个字,认为少即是多。如果你相熟 Java,用 Java 那一套语法命名跟 Go 做比照,能够很显著的领会到这种感觉。
Go 的特点能够简略的概括成以下几个点。
1.1 动态类型和编译型
首先 Go 是动态类型,动态类型就是编译时就晓得每一个变量的类型,得益于此,在编译的阶段就可能发现很多问题。而如果是动静语言,例如 JavaScript,有些问题直到运行时能力发现。
Go 是编译型语言,看到编译型大家脑子里可能会想到另外一个词解释型。两者的区别从字面上来了解其实曾经能够看进去,我用一个简略的例子来类比一下。
- 编译型 去餐馆吃饭,点了菜之后,饭店会等所有的菜做好了再上
- 解释型 去餐馆吃饭,点了菜之后,陆陆续续的边吃边上
1.2 跨平台
顾名思义,你写的 Go 源码在所有的零碎都可能运行。
这点其实很好了解,例如 Java 的口号是 ”Write once, run anywhere”。咱们都晓得 Java 是编译型的语言,然而 Java 在编译的时候生成的是字节码,这个字节码与以后的操作系统无关,与 CPU 也无关。
这种字节码必须依赖 Java 虚拟机能力运行,而虚构机会将操作系统和 CPU 之间的差别与用户屏蔽。对于编程的人来说这个过程其实无感知的。而对 Java 来说,语言自身的跨平台并不能代表代码能够跨平台。
Go 的跨平台从某种方面来说,与 Java 类型,咱们须要装置与以后操作系统绝对应版本的 Go。编译进去的可执行文件会依据操作系统的不同而有所不同。
1.3 主动垃圾回收
与 JVM 一样,Go 在运行时的内存治理(GC)由 Go 语言自身来治理,不须要程序员的参加,然而咱们能够干涉。
1.4 原生的并发编程
何为原生?咱们都晓得,在 Java 中如果要实现并发,须要内部的类库反对(Thread),而 Go 不须要从内部再引入任何依赖。反对应用关键字 go
即可。而且 Java 中是通过共享内存进行通信的,相熟 Go 的应该都看过一句话“不要通过共享内存来通信,而应该通过通信来共享内存”
1.5 欠缺的构建工具
从获取、编译、测试、装置、运行和剖析等一系列流程都有本人的内置工具。例如获取能够应用 go get
命令来下载更新指定的代码包,并且对它们进行编译和装置,能够应用 go build
对源码进行编译,用go run
命令来运行 Go 的程序,用 go fmt
来疾速格式化代码,对立代码格调。
1.6 多范式编程
目前支流的编程范式有命令式编程、函数式编程和咱们最相熟的面向对象编程。在编写 Go 的代码的时候,咱们能够抉择应用面向对象的办法,也能够应用函数式编程的思维,互相联合,相辅相成。
例如,在 Go 外面也能够用接口来形容行为,也能够应用纯函数来避免出现副作用。因而,多范式编程就是指这个语言反对多种编程范式的。
1.7 代码格调强对立
应用 Go 的内置工具 go fmt
即可疾速的将代码格式化成官网对立的规范,以此来达到代码格调对立的目标。甚至能够用 golangci-lint 来检测你的语法跟内置的规范语法是否有抵触,齐全能够将这个检测工具挂在 git 的钩子上,以此来达到强制的代码格调对立的目标。
1.8 沉闷的社区
还有一个很重要的特点是,国内的 Go 的社区非常的沉闷,这对于 Go 在国内的遍及起到了很大的作用。
2. 用 Go 的劣势
先说一下我对 Go 语言的认识,我认为 Go 在服务器这块是十分有劣势的。当前如果有高并发的利用场景,那么大概率这个服务就是用 Go 写的。不晓得大家有没有发现,摩尔定律正在生效。近十年内,硬件的原始解决能力都没有太大的晋升。显然,一味的减少晶体管的数量曾经不是解决问题最好的办法。
NASA 前不久公布到官网而后又迅速删掉的文章走漏了,Google 可能曾经实现了量子霸权,艰深一点说就是领有超过所有传统计算机的计算能力。而搁置更多的晶体管的代价也越来越高,所以当初厂商都在向处理器中增加更多的内核来晋升性能。
就像大家相熟的 Java,尽管 Java 自身反对多线程,然而在 Java 上应用多线程编程代码算是比拟低廉的。在 Java 中创立一个新的线程就会耗费靠近 1M 左右的内存。如果你真的须要反对运行上千个线程,那么服务很可能运行着就 OOM 了。除了内存耗费外,还会存在因为反对多线程带来的并发和死锁等问题。
而 Go 中,应用协程来代替线程。而且一个协程所耗费的内存比线程少了很多倍。同样的物理设施限度,你可能只能启动最多几千个线程,而协程可能启动上百万个。而且不同的 Goroutine 能够通过信 channel 进行平安的通信。
3. 游戏服务器和 Web 服务器的区别
有些对游戏服务器的介绍可能会说,游戏服务器是一个须要长期运行的程序,而后怎么怎么样。我集体认为 Web 服务器一样的须要长期运行,也须要响应不定点不定时来自用户的申请。两者从宏观上来看其实没有实质的区别。同时 Web 服务器也会对于稳定性和性能有要求,游戏服个别分为大小服,咱们这里都依照小服举例子。
3.1 状态
首先要提到的就是状态。可能你会据说过一个概念,游戏服务器是有状态的,而 Web 服务器是无状态的。什么意思呢?Web 服务器的数据流大多间接会到数据库中。而游戏服务器的数据流首先会到内存中,而后定期的写入数据库(落地)。
换句话说,游戏服务器自身的数据与数据库中的数据在运行期间会存在一个数据不统一的窗口。如果此时游戏服务器宕机了,那么就会造成数据首先到的内存数据与数据库存的数据不统一。
而 Web 服务器则不会有这样的问题,Web 所有的数据状态都会落地,而且能够针对操作加上事务,不必放心因为操作失败而引入脏数据。正因为有了状态的束缚,游戏服务器就会很谨慎的应用内存、CPU。以求在资源无限的状况下,最大化的进步的承载量,并且升高服务提早。当然,Web 服务器会为了升高某个接口的响应工夫而去做对应的优化。
3.2 扩容
在 Web 服务器中,如果你不能评估一个服务所面临的压力,又不想因为刹时的热点拜访导致服务间接不可用的话,齐全能够设置成主动扩容,因为每个服务只是单纯的接管申请,而后解决申请、返回后果,不会将数据保留在服务器的内存中。要有数据存到内存,那也是在 Redis 中。而 Redis 数据失落对数据的一致性根本没有影响。
然而在游戏服务器这边很难做到像 Web 那样灵便。首先,数据的流向不是数据库,而是内存。
举个很简略的例子,玩家的主城被攻打着火了,如果有了主动扩容,很有可能在落地的窗口内,玩家再申请一次,申请到了另一个实例。主城又没有着火了。因为数据都会先存在内存中。
再举一个例子,玩家氪金买了一个礼包。而后退出游戏,落地窗口内再次上线没了。这就不是单纯的数据问题了,玩家这是花了真金白银买的道具,忽然就没了,一两个还好解决,如果多个玩家都呈现这样的问题,那这就属于重大的线上事变了。修复数据的工作量非常的大。
所以,对于一个游戏服务器,所能应用的内存和 CPU 的资源是十分无限的,不像 Web 服务器能够不必花很大的代价做到横向扩大。这也就是为什么游戏服务器会非常非常的重视代码的性能以及稳定性。
3.3 稳固
就像下面说的例子,如果游戏服务器运行中出了 BUG,导致服务间接不可用,或者说通过这个 BUG 刷到了大量的道具,将是一个十分重大的线上事变。
而对于 Web 服务器来说,如果是管理系统之类的,有可能会有脏数据值得一提的是,脏数据对于 Web 来说,排查起来也是一件很头疼的事件。如果没有脏数据,只是服务暂且不可用,而且如果用的是微服务架构,重启服务的代价是相对来说比拟小的,只有正在重启的服务的业务是不可用的,其余的局部则能够失常的拜访。
而对于游戏服务器来说,服务器重启影响的是全服的玩家。玩家在停服期间,甚至连游戏都进不了,特地的影响玩家体验。而且,如果停服之前服务器的数据落地呈现了问题,服务重启之后会将数据从数据库 load 到内存中,此时同样会造成数据不统一的问题。
3.4 性能
从我的教训来看,在做 Web 服务器的时候,没有为了缩小 GC 的压力,为了少占用内存去做过多的优化。当然这是因为我的项目自身的体量不大,如果 QPS 很高的话,Web 服务器同样很须要重视性能,只不过游戏服务器须要始终特地留神这个方面。
不过在 Web,如果访问量很大的话导致单个服务不能扛住压力,大部分人首先想到的解决方案应该就是搞多个实例,毕竟能够做到很轻松的横向扩大。
在游戏服务器里,会把服务器的资源看的相当的贵重。例如,能不落地的字段就相对不要落地,某个字段的值能够通过已知的条件算进去的,就尽量不要定义在代码里。不过这也要看具体情况衡量运算量和调用的频率。因为上线之后,如果遇到了数据不统一,保护的数据越少,修复数据的难度就越小。
3.5 谨严
这一点上来说,我认为是两者都很关注的一个重点。只不过,在游戏服务器的某些状况中,如果服务器抛出异样或者 panic。其造成的结果会被游戏非凡的环境放大。
例如,召回你的在外部队失败了,那么部队就会始终在里面且不可用。这跟在浏览器中点一个按钮没有反馈比起来,影响绝对较小。而且应用微服务架构,在修复问题之后能够以很低的老本来重启对应的服务,而游戏服务器中还要修复一次数据。
再举一个很极其的例子,点击商店,玩家要筹备氪金了。然而却发现进不了商店,也可能不能获取商品列表。这些会间接影响到游戏的体验,甚至支出。
而对于 Web 来说,服务器的稳定性同样很重要。不然依据业务的不同,造成结果的严重性也有可能不同。影响了用户体验,就会间接影响到产品的口碑。
3.6 数据传输格局
相熟 Web 的都晓得,数据传输格局是 JSON。而在游戏服务器中是 Protobuf,是由 Google 开发的数据传输格局,与 JSON 相似。Protobuf 是二进制的,二进制数据量会比 JSON 更小一点。而且,如果传输的字段是空值,就不会被传输。而 JSON 如果是空值,一样的也会被传输。
无论是在什么样的环境中,举个例子,Node.js 和 Java 中,Protobuf 的性能体现都比 JSON 好。在 Java 中,Protobuf 甚至要比 JSON 快了靠近 80%。如果 Java 的服务之间通信有了性能瓶颈,能够思考服务之间应用 RPC 来通信。
然而凡事都具备两面性。Protobuf 的毛病依然存在:
- 文档较少
- 社区与 JSON 的比照起来
- 可读性没有 JSON 好
4 总结
以上就是这两个月以来,总结的两者的区别。只是从大体上做了一个比照,并没有具体深刻细节。细节的话有可能会在当前独自的来介绍。