-
Notifications
You must be signed in to change notification settings - Fork 7
/
architecture.html
203 lines (182 loc) · 20.4 KB
/
architecture.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
<!DOCTYPE html>
<html lang="zh-cn">
<head prefix="og: http://ogp.me/ns#">
<meta charset="utf-8">
<title>服务器架构总览 | Screeps 中文文档</title>
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Canonical links -->
<link rel="canonical" href="http://screeps-cn.github.io/architecture.html">
<!-- Alternative links -->
<link rel="alternative" hreflang="en" href="http://screeps-cn.github.io/architecture.html">
<link rel="alternative" hreflang="zh-tw" href="http://screeps-cn.github.io/zh-tw/architecture.html">
<link rel="alternative" hreflang="zh-cn" href="http://screeps-cn.github.io/zh-cn/architecture.html">
<link rel="alternative" hreflang="ru" href="http://screeps-cn.github.io/ru/architecture.html">
<link rel="alternative" hreflang="ko" href="http://screeps-cn.github.io/ko/architecture.html">
<!-- Icon -->
<link rel="apple-touch-icon" sizes="57x57" href="/icon/apple-touch-icon-57x57.png">
<link rel="apple-touch-icon" sizes="114x114" href="/icon/apple-touch-icon-114x114.png">
<link rel="apple-touch-icon" sizes="72x72" href="/icon/apple-touch-icon-72x72.png">
<link rel="apple-touch-icon" sizes="144x144" href="/icon/apple-touch-icon-144x144.png">
<link rel="apple-touch-icon" sizes="60x60" href="/icon/apple-touch-icon-60x60.png">
<link rel="apple-touch-icon" sizes="120x120" href="/icon/apple-touch-icon-120x120.png">
<link rel="apple-touch-icon" sizes="76x76" href="/icon/apple-touch-icon-76x76.png">
<link rel="apple-touch-icon" sizes="152x152" href="/icon/apple-touch-icon-152x152.png">
<link rel="icon" type="image/png" href="/icon/favicon-196x196.png" sizes="196x196">
<link rel="icon" type="image/png" href="/icon/favicon-160x160.png" sizes="160x160">
<link rel="icon" type="image/png" href="/icon/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="/icon/favicon-16x16.png" sizes="16x16">
<link rel="icon" type="image/png" href="/icon/favicon-32x32.png" sizes="32x32">
<meta name="msapplication-TileColor" content="#2f83cd">
<meta name="msapplication-TileImage" content="/icon/mstile-144x144.png">
<!-- CSS -->
<!-- build:css build/css/navy.css -->
<link rel="stylesheet" href="/css/navy.css?1">
<link rel="stylesheet" href="/css/prism.css">
<!-- endbuild -->
<link href="https://fonts.googleapis.com/css?family=Lato:300,400,700" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/docsearch.js/1/docsearch.min.css">
<!-- RSS -->
<link rel="alternate" href="/atom.xml" title="Screeps 中文文档">
<!-- Open Graph -->
<meta name="description" content="既然Screeps是一个为程序员设计的游戏, 你可能会对它服务器端怎么工作感兴趣. 对于我们来说, 我们也想把这个项目的一些高级架构透露出来.
重中之重
服务器端使用的软件有 Node.js 8.9.3, MongoDB 3, Redis 3.
我们在服务器端写了 20k 行的 JS 代码
运行时计算在 ovh 上的 40 个四核专用服务器上并行完成,使用 160 个 Intel Xeon CPU">
<meta property="og:type" content="website">
<meta property="og:title" content="服务器架构总览">
<meta property="og:url" content="http://screeps-cn.github.io/architecture.html">
<meta property="og:site_name" content="Screeps 中文文档">
<meta property="og:description" content="既然Screeps是一个为程序员设计的游戏, 你可能会对它服务器端怎么工作感兴趣. 对于我们来说, 我们也想把这个项目的一些高级架构透露出来.
重中之重
服务器端使用的软件有 Node.js 8.9.3, MongoDB 3, Redis 3.
我们在服务器端写了 20k 行的 JS 代码
运行时计算在 ovh 上的 40 个四核专用服务器上并行完成,使用 160 个 Intel Xeon CPU">
<meta property="og:image" content="http://screeps-cn.github.io/img/architecture_stage1.png">
<meta property="og:image" content="http://screeps-cn.github.io/img/architecture_stage2.png">
<meta property="og:image" content="http://screeps-cn.github.io/img/architecture_run.png">
<meta property="og:updated_time" content="2024-09-20T13:38:58.407Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="服务器架构总览">
<meta name="twitter:description" content="既然Screeps是一个为程序员设计的游戏, 你可能会对它服务器端怎么工作感兴趣. 对于我们来说, 我们也想把这个项目的一些高级架构透露出来.
重中之重
服务器端使用的软件有 Node.js 8.9.3, MongoDB 3, Redis 3.
我们在服务器端写了 20k 行的 JS 代码
运行时计算在 ovh 上的 40 个四核专用服务器上并行完成,使用 160 个 Intel Xeon CPU">
<meta name="twitter:image" content="http://screeps-cn.github.io/img/architecture_stage1.png">
<!-- Google Analytics -->
</head>
<body>
<div id="container">
<header id="header" class="wrapper">
<div id="header-inner" class="inner">
<h1 id="logo-wrap">
<a href="https://screeps.com" id="logo">Screeps</a>
<a href="/index.html" id="logo-docs">docs</a>
</h1>
<a id="mobile-nav-toggle">
<span class="mobile-nav-toggle-bar"></span>
<span class="mobile-nav-toggle-bar"></span>
<span class="mobile-nav-toggle-bar"></span>
</a>
<div id="header-main"></div>
</div>
</header>
<div id="content-wrap">
<div id="content" class="wrapper">
<div id="content-inner">
<article class="article-container" itemscope itemtype="http://schema.org/Article">
<div class="article-inner">
<div class="article">
<div class="inner">
<header class="article-header">
<h1 class="article-title" itemprop="name" id="top">服务器架构总览</h1>
<a href="https://github.com/screeps-cn/docs/edit/master/source/architecture.md" class="article-edit-link" title="改进本文"><i class="fa fa-pencil"></i></a>
</header>
<div class="article-content" itemprop="articleBody">
<p>既然Screeps是一个为程序员设计的游戏, 你可能会对它服务器端怎么工作感兴趣. 对于我们来说, 我们也想把这个项目的一些高级架构透露出来.</p>
<h2 id="重中之重" class="article-heading"><a href="#重中之重" class="headerlink" title="重中之重"></a>重中之重<a class="article-anchor" href="#重中之重" aria-hidden="true"></a></h2><ul>
<li>服务器端使用的软件有 <a href="https://nodejs.org/en/" target="_blank" rel="external">Node.js</a> 8.9.3, <a href="http://mongodb.org" target="_blank" rel="external">MongoDB</a> 3, <a href="http://redis.io/" target="_blank" rel="external">Redis</a> 3.</li>
<li>我们在服务器端写了 20k 行的 JS 代码</li>
<li>运行时计算在 ovh 上的 40 个四核专用服务器上并行完成,使用 160 个 Intel Xeon CPU E3-1231 v3 处理器内核(具有相应数量的节点实例)。</li>
<li>每个 shard 的 MongoDB 数据库都在一台有 24 核心,128GRAM 的机器上运行,每秒处理 30k 的请求。</li>
<li>玩家的运行时代码是在内存中工作,不进行任何硬盘和数据库请求</li>
</ul>
<h2 id="架构总览" class="article-heading"><a href="#架构总览" class="headerlink" title="架构总览"></a>架构总览<a class="article-anchor" href="#架构总览" aria-hidden="true"></a></h2><p>所有的游戏数据都是储存在 MongoDB 里. 每一个游戏对象都是分散的数据库文档(表). 它解释了由数据库分配的<code>id</code>属性对象的特定视图。</p>
<p>每一个游戏中的 tick 都是被 Redis 上的特殊同步代码控制的. 一个 tick 有两个阶段:</p>
<ol>
<li><strong>阶段1:解析代码</strong></li>
<li><strong>阶段2:执行代码</strong></li>
</ol>
<p>下面是一张阶段处理流程图:</p>
<p><img src="img/architecture_stage1.png" alt=""></p>
<p><img src="img/architecture_stage2.png" alt=""></p>
<p>每一个阶段都会创建一个任务队列。第一阶段的任务是执行所有活跃玩家的脚本,而第二阶段是处理游戏世界的房间。队列存储为一个 redis 列表,每个任务都由单独的计算机单独处理。</p>
<p>每个 tick 开始时会生成一个列表,其中列出了所有活跃的玩家,这些玩家被放入队列中以处理他们的游戏脚本。所有服务器从队列接收任务,请求玩家需要的数据库数据,并启动他或她的游戏脚本的运算处理,收集各种游戏对象的命令。当队列净空后,第二阶段开始。所有活动的命令都被放入队列,运行时服务器开始处理每个房间中对象的命令。(通俗点讲,第一阶段只会把所有检测到的活跃玩家的代码解析出来,看看哪些需要在第二阶段执行,第二阶段去执行这些代码)</p>
<p>尽管第二阶段的不同房间和第一阶段的不同玩家被分别并行处理,但并行处理的数量严格和 cpu 的数量对应。一个房间和一个玩家由一个核心同步处理,排除了各种其他条件。</p>
<p>两个阶段完成后,就形成了一定数量的更改数据库中游戏对象的请求。 这些要求在第二阶段结束后<a href="https://docs.mongodb.org/manual/core/bulk-write-operations/" target="_blank" rel="external">在bulk里得到了执行</a>。 MongoDB 3 使用了新的储存引擎<a href="http://www.wiredtiger.com/" target="_blank" rel="external">WiredTiger</a> 根据文档级的并发性,它允许利用数据库服务器上多个并行线程的优势。 当整个数据库操作完成后, 整个系统开始执行下一个tick的操作.</p>
<p>数据库对象更新是唯一需要硬盘访问的操作。在数据库服务器上,磁盘刷新每分钟只执行一次,不会影响根本不使用磁盘的运行时服务器(它们上没有磁盘)。运行时服务器接收游戏对象和<code>内存</code>对象的就绪数据,这些数据甚至在任务启动之前就加载到RAM中。所有有用的工作都是由运行时服务器的cpu核心来完成的,这些cpu核心在tick的第一阶段即计算阶段被玩家“租用”。</p>
<h2 id="服务器扩展" class="article-heading"><a href="#服务器扩展" class="headerlink" title="服务器扩展"></a>服务器扩展<a class="article-anchor" href="#服务器扩展" aria-hidden="true"></a></h2><p>该系统的设计允许在两个级别上轻松扩展:</p>
<ul>
<li>当数据库上的负载增加时 (例如: 玩家们在 shard 上更加活跃), 我们可以选择增加 WiredTiger 的 CPU 数或增加更多的 shard (每一个 shard 都有自己的数据库).</li>
<li>当玩家计算所产生的 cpu 总负载的增加时,我们可以添加更多的运行时服务器来执行这些计算。在它们启动后的一分钟内,它们就可以从 redis 队列接收和处理任务。</li>
</ul>
<h2 id="玩家脚本运行环境" class="article-heading"><a href="#玩家脚本运行环境" class="headerlink" title="玩家脚本运行环境"></a>玩家脚本运行环境<a class="article-anchor" href="#玩家脚本运行环境" aria-hidden="true"></a></h2><p>Node.JS 的 <a href="https://nodejs.org/api/vm.html" target="_blank" rel="external"><code>vm</code></a> 库在计算游戏脚本阶段执行任务时使用. 每个节点实例进程都启动一个单独的 fork,该 fork 不能访问其父进程。在启动后,fork 会立即向数据库发出一个预先请求,以获取计算所需的数据。 然后它为用户创建一个 context 并执行 <a href="https://nodejs.org/api/vm.html#vm_vm_runincontext_code_contextifiedsandbox_options" target="_blank" rel="external"><code>vm.runInContext</code></a>. context 会保存在 fork 中以备将来使用,这允许您在脚本中重复使用 <code>global</code> 对象和 <code>require</code> 缓存。 而且,编译<a href="http://v8project.blogspot.com.by/2015/07/code-caching.html" target="_blank" rel="external">缓存脚本代码</a>可以加快之后的代码编译速度。</p>
<p><img src="img/architecture_run.png" alt=""></p>
<p>尽管 <code>runincontext</code> 是特定于每个玩家的代码执行超时才会被调用的,但它并不总是能够在某些工作负载类型下优雅地完成脚本执行。如果出现这种情况,则在超时时终止整个 fork 而不是 vm。这个过程中的所有玩家 context 都会消失,然后从头开始重新创建。</p>
<p>未来,我们计划开源所有服务器端系统的代码,让您在自己的机器上可以启动 Screeps 进行模拟并研究。</p>
</div>
<footer class="article-footer">
<time class="article-footer-updated" datetime="2024-09-20T13:38:58.407Z" itemprop="dateModified">上次更新:9月 20, 2024</time>
<a href="/cpu-limit.html" class="article-footer-prev"><i class="fa fa-chevron-left"></i><span>CPU 限制</span></a><a href="/ptr.html" class="article-footer-next"><span>公开测试区域 (PTR)</span><i class="fa fa-chevron-right"></i></a>
</footer>
</div>
</div>
<aside id="article-toc" role="navigation">
<div id="article-toc-inner">
<strong class="sidebar-title">目录</strong>
<ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#重中之重"><span class="toc-text">重中之重</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#架构总览"><span class="toc-text">架构总览</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#服务器扩展"><span class="toc-text">服务器扩展</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#玩家脚本运行环境"><span class="toc-text">玩家脚本运行环境</span></a></li></ol>
<a href="#" id="article-toc-top">回到顶部</a>
</div>
</aside>
</div>
</article>
<aside id="sidebar" role="navigation">
<div class="inner"><a href="/api/" class=api-link><span>API Reference</span><img src="/img/link-external.svg"></a><a href="/index.html" class="sidebar-link">总览</a><strong class="sidebar-title">游戏环境</strong><a href="/introduction.html" class="sidebar-link">简介</a><a href="/creeps.html" class="sidebar-link">Creeps</a><a href="/control.html" class="sidebar-link">控制</a><a href="/defense.html" class="sidebar-link">防御</a><a href="/respawn.html" class="sidebar-link">重生</a><a href="/start-areas.html" class="sidebar-link">初始区域</a><a href="/resources.html" class="sidebar-link">资源</a><a href="/market.html" class="sidebar-link">市场</a><a href="/invaders.html" class="sidebar-link">NPC 入侵者</a><a href="/power.html" class="sidebar-link">超能</a><strong class="sidebar-title">脚本</strong><a href="/scripting-basics.html" class="sidebar-link">脚本基础</a><a href="/global-objects.html" class="sidebar-link">全局对象</a><a href="/modules.html" class="sidebar-link">模块</a><a href="/debugging.html" class="sidebar-link">调试</a><a href="/game-loop.html" class="sidebar-link">游戏循环</a><a href="/commit.html" class="sidebar-link">外部提交</a><a href="/simultaneous-actions.html" class="sidebar-link">同步操作</a><a href="/cpu-limit.html" class="sidebar-link">CPU 限制</a><strong class="sidebar-title">其他</strong><a href="/architecture.html" class="sidebar-link current">服务器架构</a><a href="/ptr.html" class="sidebar-link">公开测试区域 (PTR)</a><a href="/third-party.html" class="sidebar-link">第三方工具</a><a href="/auth-tokens.html" class="sidebar-link">验证令牌</a><a href="/community-servers.html" class="sidebar-link">社区服务器</a><a href="/tos.html" class="sidebar-link">服务条款</a><a href="/privacy-policy.html" class="sidebar-link">隐私政策</a><strong class="sidebar-title">资源</strong><a href="http://blog.screeps.com" class="sidebar-link">博客</a><a href="http://blog.screeps.com/categories/Changelogs/" class="sidebar-link">修改日志</a><a href="http://chat.screeps.com" class="sidebar-link">聊天室</a><a href="https://screeps.com/forum/" class="sidebar-link">论坛</a><strong class="sidebar-title">贡献文章</strong><a href="/contributed/rules.html" class="sidebar-link">贡献规则</a><a href="/contributed/advanced_grunt.html" class="sidebar-link">高级 Grunt 使用</a><a href="/contributed/modifying-prototypes.html" class="sidebar-link">修改原型</a><a href="/contributed/caching-overview.html" class="sidebar-link">缓存概述</a><a href="/contributed/ps_ubuntu.html" class="sidebar-link">私有服务器 MongoDB</a></div>
</aside>
</div>
</div>
</div>
<footer id="footer" class="wrapper">
<div class="inner">
<div id="footer-copyright">
© 2024 <a href="https://screeps.com/" target="_blank">Screeps</a><br>
Documentation licensed under <a href="http://creativecommons.org/licenses/by/4.0/" target="_blank">CC BY 4.0</a>.
</div>
<div id="footer-links">
<a href="https://github.com/screeps-cn/docs" class="footer-link" target="_blank"><i class="fa fa-github-alt"></i></a>
</div>
</div>
</footer>
</div>
<div id="mobile-nav-dimmer"></div>
<nav id="mobile-nav">
<div id="mobile-nav-inner">
<a href="/api/" class=api-link><span>API Reference</span><img src="/img/link-external.svg"></a><a href="/index.html" class="mobile-nav-link">总览</a><strong class="mobile-nav-title">游戏环境</strong><a href="/introduction.html" class="mobile-nav-link">简介</a><a href="/creeps.html" class="mobile-nav-link">Creeps</a><a href="/control.html" class="mobile-nav-link">控制</a><a href="/defense.html" class="mobile-nav-link">防御</a><a href="/respawn.html" class="mobile-nav-link">重生</a><a href="/start-areas.html" class="mobile-nav-link">初始区域</a><a href="/resources.html" class="mobile-nav-link">资源</a><a href="/market.html" class="mobile-nav-link">市场</a><a href="/invaders.html" class="mobile-nav-link">NPC 入侵者</a><a href="/power.html" class="mobile-nav-link">超能</a><strong class="mobile-nav-title">脚本</strong><a href="/scripting-basics.html" class="mobile-nav-link">脚本基础</a><a href="/global-objects.html" class="mobile-nav-link">全局对象</a><a href="/modules.html" class="mobile-nav-link">模块</a><a href="/debugging.html" class="mobile-nav-link">调试</a><a href="/game-loop.html" class="mobile-nav-link">游戏循环</a><a href="/commit.html" class="mobile-nav-link">外部提交</a><a href="/simultaneous-actions.html" class="mobile-nav-link">同步操作</a><a href="/cpu-limit.html" class="mobile-nav-link">CPU 限制</a><strong class="mobile-nav-title">其他</strong><a href="/architecture.html" class="mobile-nav-link current">服务器架构</a><a href="/ptr.html" class="mobile-nav-link">公开测试区域 (PTR)</a><a href="/third-party.html" class="mobile-nav-link">第三方工具</a><a href="/auth-tokens.html" class="mobile-nav-link">验证令牌</a><a href="/community-servers.html" class="mobile-nav-link">社区服务器</a><a href="/tos.html" class="mobile-nav-link">服务条款</a><a href="/privacy-policy.html" class="mobile-nav-link">隐私政策</a><strong class="mobile-nav-title">资源</strong><a href="http://blog.screeps.com" class="mobile-nav-link">博客</a><a href="http://blog.screeps.com/categories/Changelogs/" class="mobile-nav-link">修改日志</a><a href="http://chat.screeps.com" class="mobile-nav-link">聊天室</a><a href="https://screeps.com/forum/" class="mobile-nav-link">论坛</a><strong class="mobile-nav-title">贡献文章</strong><a href="/contributed/rules.html" class="mobile-nav-link">贡献规则</a><a href="/contributed/advanced_grunt.html" class="mobile-nav-link">高级 Grunt 使用</a><a href="/contributed/modifying-prototypes.html" class="mobile-nav-link">修改原型</a><a href="/contributed/caching-overview.html" class="mobile-nav-link">缓存概述</a><a href="/contributed/ps_ubuntu.html" class="mobile-nav-link">私有服务器 MongoDB</a>
</div>
</nav>
<!-- Scripts -->
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<!-- build:js build/js/main.js -->
<script src="/js/lang_select.js"></script>
<script src="/js/scrollingelement.js"></script>
<script src="/js/toc.js"></script>
<script src="/js/mobile_nav.js"></script>
<script src="/js/custom.js"></script>
<!-- endbuild -->
<script src="https://cdn.jsdelivr.net/retinajs/1.3.0/retina.min.js" async></script>
<!-- Algolia -->
</body>
</html>