184 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: Node-Pinus
date: 2023-08-01 17:23:58
excerpt:
tags: TypeScript
rating: ⭐
---
# 前言
- Node-Pinus游戏服务器框架:https://github.com/node-pinus/pinus
- 案例:https://github.com/node-pinus/pinus/tree/master/examples/simple-example
- Pomelo的wiki:https://github.com/NetEase/pomelo/wiki/Home-in-Chinese
# ReadME
## 启动方法
1. 执行npm-install.bat或npm-install.sh
2. 编译游戏服
```
cd game-server
npm run build
```
## 启动游戏服
```bash
cd dist
node app
```
显示“all servers startup in xxx ms”即表示启动成功
## 启动网页服务器
```bash
cd web-server
node app
```
显示“Please log on http://127.0.0.1:3001/index.html”即表示启动成功
## 进入客户端网页
浏览器输入
http://127.0.0.1:3001/index.html
点击“Test Game Server”如返回“game server is ok.”即表示连接游戏服务器返回成功
## 调试游戏服务器的方法
1. 安装vscode
2. 在game-server目录启动vscode
3. 按照正常流程启动游戏服
4. 在“调试”界面选择Attach To Connector或Attach To Master
5. 按F5把调试器挂上去然后就可以断点调试了。
# Pomelo相关资料
## Pomelo工具和库介绍
pomelo 提供了一系列的工具和库供开发者使用,这些工具和库能够协助开发者更好地完成应用开发、调试以及部署等工作。这些工具和库涵盖全面,有管理控制工具,有用来做压力测试的工具,也有一些比较通用的库。
- 命令行工具pomelopomelo框架提供的一个较简单的工具该工具可以帮助开发者更便捷、更有效地进行应用开发包括创建项目、启动应用、停止应用、关闭应用等等请参考pomelo命令行工具使用。
- pomelo-clipomelo-cli是一个pomelo服务器群的管理客户端通过连接注册到master服务器可以对服务器群进行较为高级的管理如运行时动态的添加关闭服务器查看服务器的状态等等。请参考pomelo-cli更详细的文档。
- pomelo-robotpomelo-robot是一个用来对pomelo游戏框架进行性能测试的工具可以帮助开发者做一些压力测试请参考pomelo-robot更详细的文档
- pomelo-daemonpomelo-daemon 提供了一个 daemon 服务可以用这个服务来进行分布式部署以及日志收集。请参考pomelo-daemon的使用。
- pomelo-admin-webpomelo-admin-web 是 pomelo 框架中基于pomelo-admin开发的web端监控的模块可以通过 web 端的方式来对游戏服务器集群的运行状态性能日志等进行实时的监控。请参考pomelo-admin-web工具的使用。
- pomelo-syncpomelo-sync 模块是用来管理游戏进程中需要持久化的数据在内存与存储系统之间同步的。请参考pomelo sync 使用文档
- pomelo-protobufpomelo-protobuf 是对google protobuf的一个实现借助javascript的语言特性实现了类.proto文件的运行时解析并用在pomelo框架中完成对要传输消息的压缩。protobuf不仅可以用在服务端也同样可以用于web客户端。具体请参考pomelo-protobuf。
## Chat源码
这个是很简单的应用,其代码结构如下图:
![源码结构图](https://github.com/NetEase/pomelo/wiki/images/source.png)
#### [](https://github.com/NetEase/pomelo/wiki/chat%E6%BA%90%E7%A0%81%E4%B8%8B%E8%BD%BD%E4%B8%8E%E5%AE%89%E8%A3%85#game-server)game-server
game-server目录放的是所有游戏服务器的逻辑以文件app.js作为入口运行游戏的所有逻辑和功能。从图上可以看出其servers里面有三个目录分别是gateconnectorchat。在pomelo中使用路径来区分服务器类型因此三个目录代表了三种不同类型的服务器每一个目录下面可以定义handler,remote,定义了handler和remote就决定了这个服务器的行为。
- 对于gate服务器其逻辑实现代码在其gateHandler.js中它接受客户端查询connector的请求返回给客户端一个可以连接的connector的(ip,port);
- connector服务器其逻辑代码在entryHandler.js中它主要完成接受客户端的请求维护与客户端的连接路由客户端的请求到chat服务器;
- chat服务器其既有handler代码也有remote代码 handler中处理用户的send请求而remote是当有用户加入或者退出的时候由connector来发起远程调用时调用的。在remote里由于涉及到用户的加入和退出所以会有对channel的操作。
game-server 的子目录config下面是游戏服务器所用到的配置文件存放的地方配置信息使用JSON格式包含有日志master服务器和其他服务器的配置信息。除了这个pomelo所需的配置信息外一般情况下也将游戏逻辑所需要的配置信息放到这个目录下例如数据库的配置信息地图信息等。
logs子目录下存放游戏服务器产生的所有的日志信息。
#### [](https://github.com/NetEase/pomelo/wiki/chat%E6%BA%90%E7%A0%81%E4%B8%8B%E8%BD%BD%E4%B8%8E%E5%AE%89%E8%A3%85#web-server)web-server
由于我们这个聊天应用的客户端是web所以需要一个web服务器。在这个目录下主要是客户端的jscss和静态资源等等。在本例子中里面有用户登录聊天的逻辑的js文件等等。我们在这个例子教程中更多地关注的是服务器端的逻辑以及功能对于客户端我们几乎不需要怎么修改其代码直接使用默认就好。
### chat分析
我们要搭建的pomelo聊天室具有如下的运行架构
![](https://github.com/NetEase/pomelo/wiki/images/multi-chat.png)
在这个架构里前端服务器也就是connector专门负责承载连接 后端的聊天服务器则是处理具体逻辑的地方。 这样扩展的运行架构具有如下优势:
- 负载分离:这种架构将承载连接的逻辑与后端的业务处理逻辑完全分离,这样做是非常必要的, 尤其是广播密集型应用(例如游戏和聊天)。密集的广播与网络通讯会占掉大量的资源,经过分离后业务逻辑的处理能力就不再受广播的影响。
- 切换简便因为有了前、后端两层的架构用户可以任意切换频道或房间都不需要重连前端的websocket。
- 扩展性好用户数的扩展可以通过增加connector进程的数量来支撑。频道的扩展可以通过哈希分区等算法负载均衡到多台聊天服务器上。理论上这个架构可以实现频道和用户的无限扩展。
#### 客户端
聊天室的逻辑包括以下几个部分:
- 用户进入聊天室这部分逻辑负责把用户信息注册到session并让用户加入聊天室的channel。
- 用户发起聊天: 这部分包括了用户从客户端发起请求,服务端接收请求等功能。
- 广播用户的聊天: 所有在同一个聊天室的客户端收到请求并显示聊天内容。
- 用户退出: 这部分需要做一些清理工作包括session和channel的清理。
客户端首先要给gate服务器查询一个connector服务器gate给其回复一个connector的地址及端口号这里没有列出完整的代码具体的代码在路径web-server/public/js/client.js中,详细代码略去见client.js:
```javascript
function queryEntry(uid, callback) {
var route = 'gate.gateHandler.queryEntry';
// ...
}
$("#login").click(function() {
username = $("#loginUser").attr("value");
rid = $('#channelList').val();
// ...
//query entry of connection
queryEntry(username, function(host, port) {
pomelo.init({
host: host,
port: port,
log: true
}, function() {
// ...
});
});
});
```
客户端在查询到connector后需要发请求给connector服务器 第一次请求要给connector进程因为首次进入时需要绑定对应的uid信息这里略去详细代码:
```javascript
pomelo.request('connector.entryHandler.enter', {username: username, rid: rid}, function(){
// ...
});
```
当用户发起聊天的时候会请求服务chat.chatHandler.send大致代码如下:
```javascript
pomelo.request('chat.chatHandler.send', {content:msg, from: username, target: msg.target}, function(data) {
// ...
});
```
当有用户加入、离开以及发起聊天时,同房间的人将会收到服务端推送来的相应消息,这些在客户端是以回调的方式进行添加的,大致代码如下:
```javascript
pomelo.on('onAdd', function(data) {
// ...
});
pomelo.on('onLeave', function(data) {
// ...
});
pomelo.on('onChat', function(data) {
// ...
});
```
客户端的详细代码都在目录web-server/public/js/client.js文件中这里客户端的js是使用component进行管理的,详细请参阅component的参考文档。
#### 服务端
我们知道在pomelo中只要定义了一个服务器的handler和remote那么就定义了这个服务器的行为就决定了这个服务器的类型。在本例子中有三种服务器gateconnectorchat,它们完成的具体逻辑如下:
gate完成客户端对connector的查询在其handler里有其实现的代码由于在这里本例中仅仅配置了一台connector服务器因此直接返回其信息给客户端即可然后客户端就可以连接到connector了。
```javascript
handler.queryEntry = function(msg, session, next) {
var uid = msg.uid;
// ...
};
```
connector接受用户的连接完成用户的注册及绑定维护客户端session信息处理客户端的断开连接其逻辑代码在connector/handler/entryHandler.js中。大致如下
```javascript
handler.enter = function(msg, session, next) {
var self = this;
var rid = msg.rid;
var uid = msg.username + '*' + rid
var sessionService = self.app.get('sessionService');
// .....
};
```
chat服务器是执行聊天逻辑的地方它维护channel信息一个房间就是一个channel一个channel里有多个用户当有用户发起聊天的时候就会将其内容广播到整个channel。chat服务器还会接受connector的远程调用完成channel维护中的用户的加入以及离开因此chat服务器不仅定义了handler还定义了remote。当有客户端连接到connector上后connector会向chat发起远程过程调用chat会将登录的用户加到对应的channel中其大致代码为
```javascript
// chatHandler.js
handler.send = function(msg, session, next) {
var rid = session.get('rid');
var username = session.uid.split('*')[0];
// .....
};
// chatRemote.js
ChatRemote.prototype.add = function(uid, sid, name, flag, cb) {
var channel = this.channelService.getChannel(name, flag);
};
ChatRemote.prototype.kick = function(uid, sid, name) {
var channel = this.channelService.getChannel(name, false);
// ...
};
```
注意 在实现具体的Handler的时候最后需要调用next其中next的签名为 next(err, resp).如果没有出现错误那么err为空即可如果不是request请求而是notify的话则一样需要调用next此时resp参数是不需要的一般情况下如果没有错误的话就直接使用next(null)即可。
服务器配置信息在config目录下现在我们只关注servers.json, master.json。master.json配置是master服务器的配置信息包括地址端口号servers.json配置具体的应用服务器信息。在配置文件中分为development和production两种环境表示开发环境和产品环境我们在pomelo start后面可以通过-e可以指定使用哪个环境更多帮助参见pomelo start --help。