我的第一个万行项目

大三时期做的游戏平台。主要语言是 Kotlin 和 TypeScript。回头看觉得做得还行,反正当时拿了满分 :)

后端语言 Kotlin,框架是Vert.X。前端语言 TypeScript,框架是 Vue 和 Electron。提供 Java SDK,以及一个样例小游戏。

面向用户,SUSTeam 提供一个网页前端,主要用于浏览与购买。一个客户端,在网页基础上增加了本地游戏库、下载和更新的功能。面向开发者,SUSTeam 提供一个管理平台,以及接入平台服务的 SDK。

# 用 Kotlin 开发是怎样的体验

技术选型会上,后端用 Kotlin 的决定是组里最强的萌妹做出的。类似 Kotlin 这种年轻灵活、蓬勃生长的现代化语言,之前只接触过 Swift,组里剩下三个人见都没见过,只好硬着头皮学。

现代的语言自有它的好处。比如单例或者伴生对象,Kotlin 一个关键词就搞定了。又比如支持静态分发的函数扩展,省去了大量的继承或装饰器。灵活的闭包(上下文)也让代码可读性变得更强。这些特性都是老派语言没有的。

下决心用 Kotlin 开发的主要原因是看中了 Vert.X 这个框架。现在大家都知道协程性能优秀了,正好 Vert.X 的 Event Loop 和 Kotlin 的 suspend 结合得天衣无缝。~~写事务的不必给自己找那么多麻烦。~~后期 Vert.X 果然不负众望。最后两周临时改需求,加入前后端即时通信,我们选择的 STOMP 协议也在 Vert.X 里有很好的实现。连测试都直接 mock 了,真的省下很多时间(让我们做剩下的另外 5 个项目)。

# 又一次用 Vue 是什么体验

大二数据库项目的前端选择了 Vue,当时我们还是课程中少数用了前端框架的组。答辩的时候见识了不少硬上 HTML 和 JS 的辣眼睛小组;见到了一个令人瞠目结舌的 Unity 前端小组(做火车订票系统);还有更多直接没做前端,就一个 CLI 敷衍过去。不过这次强制要求写前端,而且美观程度也列入了评分细则,Vue 成了大多数组的选择。

其实我自己是想用 React 的。因为不喜欢写很多 v-ifv-bind,把控制流写到 template 外面,搞得耳边全是鼠标滚轮的声音(在后面写一点,在前面写一点,自动跳转又经常不 work)。分拆组件的插件也不是很智能,所以写 prop 的活又重了一点。无奈组员们大多没接触过前端框架,我只好妥协,选用更容易上手的 Vue。

不过这里踩了一个坑,那就是用 TypeScript。这个选择搞得大家都很狼狈:我们非常喜欢用 Annotations 的方式把组件写成类的形式,也非常喜欢定义 interface 使得 API 调用更规范,总之严谨的语言很讨喜。然而,不少开源库对 TypeScript 的支持很不好,大量时间被用在修复兼容性上。TypeScript 社区任重而道远吧。

# 项目核心工作

# 选题与工作量

OOAD 给出的选项特别多,包括数据库 OJ、课程作业管理平台、软工工具部署平台、游戏平台、塔防游戏、斗地主游戏,一共七个选项。我们觉得不能很好的处理数据库 OJ 的性能问题,而软工工具部署平台和课程作业管理平台过于简单。最后选择做游戏平台。

游戏平台不仅要求展示、交易,还对 DLC、折扣、公告、评价、增量更新、好友系统、成就有一系列要求。在平台的基础之上,还要求给开发者提供 SDK,方便接入服务。仔细分析下来,前端后端 SDK,工作量还是不小的。

# 有趣的工作

其实每天都在解决不同的问题,这部分只能胡扯一点映象深刻的。

# 单元测试

对于主要功能,都有单元测试。这可能在学生开发的项目中不常见。单元测试里 mock 了 DAO 层,可以在不影响数据库的情况下做测试。

关于 Kotlin 单元测试这个库,又是一堆大坑。在五个人的本地环境里出了五种不一样的问题。总之在更换 gradle 版本和 JDK 版本(还不能是最新版)之后,终于能正常跑了。

# 文件的上传与下载

游戏展示图片或视频需要上传下载,游戏文件也需要上传下载,它们又分为公开可见和私密可见。在开发的第二个月,我负责解决这个问题。其实就是一个带有权限管理的云存储。

上传时交由 Vert.X 的 StaticHandler 处理,会自动转储成带 GUID 的无扩展名文件,放在临时文件夹里。责任链传递到我们自己的 Handler,将文件转移到存放目录(可以是大容量磁盘),在日志里记录上传者、文件类型、是否公开等。

对于不公开文件的下载鉴权实际上不归我这部分管,因为鉴权本身非常复杂(例如用户是否购买游戏、是否购买 DLC、是否拥有查看权等)。这里暴露一个接口供其他业务调用。

# 好友在线状态

用关系型数据库存储好友在线状态?我们认为是不可取的。用户在是否在线,正在玩什么游戏,变动很快,不应该用关系型数据库存储。最终项目里用全局 HashMap 存储了这些信息。其实可以用 Redis。如果能继续优化项目,这是一个可以改进的地方。

判断用户是否在线不能只依赖于主动离线,客户端可能会闪退。心跳包也许是一个好的设计。我们的思路是,用户但凡触发任何一个实时后端事务,都会更新最后在线时间,超过一个值就判断为离线。责任链的第一棒就负责干这个事。

# 客户端通知

开发的前三个月,大家一直以为做纯 HTTP 服务器就可以了。但是客户端通知、好友聊天、邀请上线是我们不想放弃的功能。这就需要服务器支持即时通信。临时选用了 STOMP 协议。可惜的是后端所有东西都写好了,前端最后一个星期已经没有时间适配了,所以这个功能是遗憾放弃的。

# 有一个好队伍是什么体验

参与项目的五个同学都非常棒!我们合作非常高效,没有出现过互相甩锅的情况。

管理项目进度用的工具是 Notion 的进度版,它可以非常方便地设置完成度,把任务 Assign 给每个人,并且追踪进度。团队交流的工具是 Slack,作为一个开发专用的交流工具,它能和 git 深度结合,每个 push 都能被发送到群聊里(就能及时抓到谁 force push 了)。

我们还有互相代码审计的习惯,虽然不是强制的。在 Github 里可以给代码加评论,对于团队初期磨合特别有用。

# Acknowledgement

前端真的是戳中我自己的审美点。主页面的版式借鉴了 EPIC,但是我觉得比它们更好看。

做完了这次项目,我下定决心去学现代化的 Go 和 Rust。知识栈需要不断更新~