git仓库:
- https://github.com/tap2joy/ChatBin.git 可执行文件仓库
- https://github.com/tap2joy/Protocols.git 协议库
- https://github.com/tap2joy/ChatClient.git 客户端
- https://github.com/tap2joy/CenterService.git 中心服
- https://github.com/tap2joy/ChatService.git 聊天服务
- https://github.com/tap2joy/Gateway.git 网关服
- TCP通信,设计了一个简单的包头,第一个4字节表示包长,第二个4字节表示协议ID,后面跟protobuf包体
- 在common里面定义了通用的错误码,消息id和通用错误消息包
- 使用bufio.NewScanner的split处理粘包问题
- 用户名字唯一,用名字作为用户的唯一标识
- 尽量采用分布式,无状态服务,方便水平扩展
整个服务器分为3块:中心服务器(CenterService),聊天服务(ChatService)和网关服(Gateway)
- 中心服务器目前是做成单节点,负责服务注册和服务发现,以及在线玩家的管理。
- 中心服务器3秒内未收到服务发来的心跳,则视为已超时,并定时清理超时的服务。
- 聊天服务是主要的聊天功能所在,可水平扩展,会定时向中心服务器发送心跳包。
- 网关服负责与客户端的长链接,并转发客户端的请求,以及服务器的推送消息。
- 网关服会定时轮询最新可用的聊天服务列表,以实现服务发现功能。网关服是可以水平扩展的。
- 客户端与网关服采用TCP长链接通信,服务器之间采用rpc通信
- 准备数据库。ChatService有用到postgresql数据库。创建好名字为chat_db的数据库,数据库相关参数可在config/app.json中修改
数据库相关操作:
psql -U postgres -W 111111 create database chat_db owner postgres; - 如过你是windows操作系统,直接拉取仓库:https://github.com/tap2joy/ChatBin.git, 进入到根目录下,执行start.bat即可开启服务器,执行stop.bat即可停止所有服务
- 如果你想通过代码构建部署,那么需要先拉取仓库:
- 在对应的目录下执行:
git submodule init git submodule update go build
- 依次启动CenterService, ChatService, Gateway
- CenterService 监听的端口是9100
- ChatService 端口是在config/app.json里面配置
- Gateway grpc的端口配的是9109,针对客户端的端口是9108
- 每个服务代码目录下都有restart.sh,在linux系统下可以直接执行: bash restart.sh 进行重启服务或新开服务
- 支持容器部署,执行docker build命令打镜像,执行docker run启动
- 可以直接使用:https://github.com/tap2joy/ChatBin.git 下面ChatClient的exe启动客户端。 配置默认连的远程云服,如果要连本地,则使用client_local.json.
- 客户端启动后会显示网络连接成功,并自动拉取当前可用的聊天室列表。然后输入要进去的聊天室id和昵称,即可开始聊天
- 输入:/popular n,会显示n 秒内使用频率最高的单词,如果没有会提示 empty,如果时间参数未传,会提示参数错误。
- 输入:/stats [user] 会显示指定用户的本次在线时长,如果用户不存在,会提示 user not exist.
- 输入:/switch [id] 会切换到指定id的聊天室,并自动拉取聊天记录
- 敏感词过滤,采用的TrieTree字典树处理,匹配效率高
- 最近n秒内高频词统计,这是一个相对低频的操作,采用的是实时拉取n秒内的聊天记录,遍历聊天记录后生成一个map[string]int, 同时保留当前最高的频率和最高频率对应的词,这样遍历一次就可以得到频率最高的词。
- 消息推送。在中心服务器上,存储了一份在线玩家数据,包括玩家当前的聊天室id,上线时间,所在gateway的地址。 当ChatService要给指定聊天室发消息时,先到CenterService上面拉取到所有的在线用户列表,从中获取他们所在的gateway, 调用grpc接口PushMessage,将消息发送到gateway,gateway再分发给不同的客户端。
- Gateway可以水平扩展,用户可以连任意一个Gateway。后面可以做负载均衡,应该不会成为瓶颈。
- ChatService可以水平扩展,无状态服务,目前采用随机算法进行负载均衡。但是依赖数据库,所以瓶颈就在数据库。 解决数据库问题的方法有:加缓存,增加从库,数据库分片等方案
- CenterService目前是单一节点,不过所承载的功能简单,但还是可能成为整个系统的瓶颈。 可以考虑采用redis作为数据存储,改成无状态服务,从而可以水平扩展。还可以考虑将服务管理和用户管理拆开。
- protobuf: 通信协议
- grpc:rpc调用
- xorm: 数据库操作
- pq:postgresql驱动
- viper:json文件读取
- 进入test目录,执行go test。测试覆盖了所有的关键算法,如:
- 敏感词过滤
- 高频词处理
- 配置文件加载,配置项读取
- 时间格式化
- TrieTree测试
- 控制台输入测试
- 各个rpc接口测试
- grpc测试,使用的是grpcurl,在test/shell目录下,执行对应的shell
-
CenterService
- UserOnline 用户上线
- 参数:
- Name: 用户名字
- Gate: 所在gateway地址
- Channel: 聊天室id
- 返回:
- OldUser: 如果当前用户已经在其他gate登录了,则返回老的用户信息,否则为空
- 参数:
- UserOffline 用户下线
- 参数:
- Name: 用户名字
- 参数:
- ChangeChannel 切换聊天室
- 参数:
- Name: 用户名字
- Channel: 目标聊天室id
- 参数:
- GetUserOnlineTime 获取用户在线时长
- 参数:
- Name: 用户名字
- 返回:
- Duration: 在线时长,单位秒
- 参数:
- GetOnlineUsers 获取在线用户列表
- 参数:
- Channel: 聊天室id
- 返回:
- Users: 用户列表
- 参数:
- RegisterService 注册服务
- 参数:
- Type:服务类型
- Address: 服务地址
- 参数:
- GetServices 获取可用服务列表
- 参数:
- Type:服务类型
- 返回:
- List:服务列表
- 参数:
- UserOnline 用户上线
-
ChatService
- SendMessage 发送聊天消息
- 参数:
- SenderName:发送者名字
- Channel:聊天室id
- Content:内容
- System:是否是系统消息
- 返回:
- Result:gm命令的返回值
- 参数:
- GetChatLog 获取聊天记录
- 参数:
- Channel:聊天室id
- 返回:
- Logs:聊天记录
- 参数:
- GetChannelList 获取聊天室列表
- 返回:
- List:聊天室列表
- 返回:
- SendMessage 发送聊天消息
-
Gateway
- PushMessage 推送消息
- 参数:
- SenderName:发送者名字
- Content:内容
- UserNames:目标玩家列表
- Timestamp:时间戳
- 参数:
- KickUser 踢出用户
- 参数:
- Name: 用户名字
- Gate: 网关地址
- 参数:
- PushMessage 推送消息

