TDS 工作总结

XDSDK 部分

Posted by Lnn on October 21, 2023

image.png

youxiu

image.png

角色

XDSDK 技术负责人,后端工程师

整个团队的实际负责人,研发实线汇报,产品等虚线,大致 12 人左右( HR 核算的人力成本)。 团队包括:客户端、后端等实线,产品同事向自己的产品专业线汇报。

业务范围

心动 100 多款自研或者代理游戏玩家的登录、用户、支付功能。 开发者文档

聚合支付:年支付流水 20 亿元+;登录:支撑多个百万 DAU 的游戏,峰值 QPS 1 W+。

工作内容

新交接过的团队和业务

管理

做管理动作之前,首先要了解现状,对团队存在的目的了解清楚,否则很容易做无用功。然后考虑清楚哪些是要优先做的,哪些是可以往后长期建设的。

团队存在的目的

  • 为玩家提供好的、稳定的登录、用户、内购支付体验
  • 为内部游戏提供便于接入且稳定的登录、用户、内购支付中台
  • 影响游戏和公司营收:
    • 登录:影响用户进来,游戏会买量,登录稳定性、转化会直接影响游戏支出,买量了之后用户注册不进来都是直接的成本。
    • 支付:支付稳定性和支付转化直接影响公司营收。

现状

  • 故障频发
  • 需求、版本管理混乱
  • 外部抱怨、投诉较多
  • 上层管理者不满意原有负责人的管理质量

优先级最高的

  • 降低故障率
  • 缓解外部压力

次优先级

  • 让需求管理有序
  • 项目管理有节奏
  • 研发质量改善形成制度
  • 提升人员质量

对外沟通

  • 游戏研发、策略、运营:
    • 统一对外沟通对接人员:我和产品经理
    • 固定的会议沟通,重点游戏每个月一次,非重点游戏两个月一次
    • 共享团队 OKR
    • 偶尔参加重点游戏的周会
  • 客服、财务、翻译等横向人员
    • 统一对外沟通对接人员:产品经理
    • 固定的会议沟通,每个季度一次
  • 跨办公室沟通
    • 新加坡:固定的会议沟通,每月沟通一次

团队内

识别核心瓶颈

  • 工程质量意识差
  • 团队结构不合理,以 2 ~ 3 年工作经验的居多,资深工程师较少
  • 没有固定的项目管理人员
  • 产品经理很年轻

主动沟通

  • 每周全员周会、每月计划会议
  • 研发:3 周一次 one one
  • 产品:一个月一次 one one
  • 其他合作伙伴:季度 one one

工程改进

后面技术部分会详细提到,策略是:小步改进和重构

需求和项目管理

  • 需求管理:尊重产品经理的前提下,让需求更有延续性一些,需要关注长期价值
  • 项目管理:
  • 统一需求对接人:产品接需求,但是项目由我管。需求不直接提给一线研发。
  • 需求排优先级。
  • Jira 需求看板,全员可见,共享给业务方。
  • 半个月小版本,一个月大版本。

研发制度性改进

  • 逐渐加入代码评审
  • 单元测试
  • 自动化集成测试
  • 有故障一定要有复盘和可沉淀的文档(共享全公司)
  • 架构设计需要有架构评审或者 peer review

成员调整

  • 原团队以 2 ~ 3 年工作经验的居多,资深工程师较少
  • 让实在跟不上节奏的人离开,裁掉 1 人
  • 组织招聘 2 人,制定新人融入计划
  • 适当放权,培养骨干:客户端、服务端分别由其中一人牵头组织

和团队一起制定 OKR

  • 季度 OKR 由全团队共同制定,先民主后集中的方式
  • OKR 共享给业务方,使版本更具有前瞻性
  • 适当投资长期价值的事情:自动化工具、管理后台功能逐步沉淀,历史遗留

    向上管理

  • 日常:多主动沟通,不要害怕求助,及时预警风险。
  • 定期沟通机制:与直属 leader 每个月固定一次 one one 反馈我的进度、思考、计划、困难等。
  • 不拘于外界帮助:技术支持帮助答疑让研发不用直接陷入日常的答疑里, SRE 帮助运维让研发不用直接陷入基础设施的运维里。

技术

把一个混沌的系统治理到相对不那么混沌的状态并不是一朝一夕的事情,也不可能只可通过做一两件事情就做到。寻求持续且渐进的改进,以小步前进,每个小步都带来改进,最终实现系统和全面的转型。渐进不是妥协。恰恰相反,它是在明确且坚定的最终目标下选择更坚实和可持续的路径。改变必须从理解和尊重现状开始。

架构

国内、海外,全球发行

登录系统

  • 前台:网站、H5、小程序、客户端 SDK
  • 业务网关:校验签名、应用信息、路由
  • 登录渠道-聚合:国内、海外等渠道
  • 登录风控:登录日志、登录设备等
  • 业务监控:全局、单个登录渠道的登录成功率,QPS、RT 等

用户系统

  • 前台:网站、H5、小程序、客户端 SDK
  • API-Gatway:校验签名、应用信息,无路由
  • 业务逻辑:channel 负责登录渠道逻辑,notify 通知, schedule 定时
  • 业务监控:新增注册用户等

支付

  • 前台:网站、H5、小程序、客户端 SDK
  • 支付业务网关:校验签名、应用信息、用户信息、参数校验、支付路由
  • 支付订单 order: 预订单
  • 支付渠道 channel:国内、海外等
  • 支付到账:trade 各种校验:风控、防沉迷等
  • 到账通知:notify HTTP 通知下游
  • 后台:资金核算、管理后台
  • 业务监控:支付全生命周期

拆分

拆分原则

一开始尝试引入 DDD 的思想来推进,但是考虑到团队成员的经验和项目的阶段,就没有。而是采用*简单且符合直觉的设计,更有利于小团队。

  • 单一职责。服务或模块只为一个业务目标或功能领域服务。这有助于确保系统的每个部分都保持简单和专注,从而易于理解和维护。
  • 自治性。每个服务控制自己的数据和逻辑,并且尽可能减少对其他服务的依赖。自治性越高,服务间的耦合度就越低,系统的整体可靠性和稳定性也就越高。
  • 模块化。有助于并行开发和测试,同时也使得未来的功能扩展和重构变得更加容易。
  • 可重用。设计时应尽量使得这些组件能够在不同的场景和系统中重复使用,以减少开发工作量和提高效率。
  • 解耦。应该通过定义清晰的接口和协议来解耦服务,同时封装内部逻辑,只暴露必要的操作和数据。保证了服务的独立性。
  • 在可能的情况下,优先选择异步通信机制(如消息队列),以减少服务间的直接依赖。这可以提高系统的响应性和可靠性,特别是在分布式环境中。

  • 共用模块: client 游戏应用信息、uniqueId 全局 id。

    登录与用户

  • 登录系统和用户系统拆分,隔离
    • 登录系统的职责
      • 凭证验证:接收用户提供的登录凭证(如用户名和密码、手机短信验证码、电子邮件链接、社交媒体账号等),并验证这些凭证的有效性。
      • 第三方认证集成:登录系统将处理与这些第三方服务(例如Google, Facebook, Twitter等)的交互,包括重定向到第三方登录页面、处理回调请求等。
      • 令牌管理:一旦用户成功验证,登录系统会生成一个令牌(Token),如JWT(JSON Web Tokens)。这个令牌将用于用户后续请求的身份验证和授权。负责管理令牌的生命周期,包括令牌的刷新和失效处理。
      • 安全性保障:图形验证码、登录风控:设备、次数等等。
      • 引入网关:限流、熔断、context 传递、检查和解析JWT令牌来确认用户是否登录以及令牌的有效性、渠道路由、按游戏路由。
      • 其他:登录设备、登录日志等。
    • 用户系统的职责
      • 用户信息
        • 基础信息:可变性弱、可变性强
        • 其他游戏信息
      • 安全策略:可登录设备管理
      • 第三方登录渠道关联信息
      • 绑定、解绑、注销等用户管理
  • 代码拆分:user 账户,channel 负责渠道,notify 通知, schedule 定时
    • manager 管理后台独立
  • 聚合支付系统拆分
    • 代码拆分:account 账户、channel 渠道、promotion 营销、order 订单、trade 交易中控、product 商品,notify 通知、merch 商户、common 公共
    • 对账系统独立。

工程改进

支付

  • 发货通知异步通知从内存改为 RocketMQ ,发布重启期间,不会导致无法通知到下游。
  • 引入支付业务网关:可按重点游戏隔离、可按支付渠道隔离。
  • 网关引入限流:Spring-Cloud-Gataway。
  • 引入业务监控(登录、支付全生命周期):metrics、Prometheus、Grafana。
  • 引入 RedissonDistributedLocker 解决订单数据不一致问题。
  • 代码抽象:策略模式、责任链模式等。

瞬时暴增流量

  • 平时架构要考虑到,负载均衡、防止单点、数据库读写分离,拆库拆表。
  • 流量预估:比如 8 /2 原则,之前的经验,等等。
  • 降级方案:也要有预案,保障重点游戏,核心收入游戏,新游戏,提前对游戏分级。
  • 限流:令牌桶限流,引入 Spring-Cloud-Gateway。

工作成果

工程质量明显提升

  • 从经常故障 -> 3 个月内无故障 -> 6 个月内无故障-> 一年内无故障。
  • 代码评审、单元测试、自动化测试、CI/CD 等形成长期可持续的研发制度。
  • 资金对账系统:减少财务资金损失。
  • 支撑多个新游戏上线,无明显故障。

外部沟通改善

  • 建立固定的沟通机制
  • 获得一些外部合作伙伴的认可

需求、项目管理进入正轨化

  • 半月小版本,一个月大版本
  • 需求和项目管理进入正轨化

    个人荣誉

  • 成为团队负责人,研发向我实线汇报,项目组其他同事虚线。
  • 2022 年度优秀员工, 100 多人的大团队里唯一一个年度优秀员工。 youxiu

image.png

教训与收获

管理

做得比较好的

  • 主动沟通:主动与 leader、下属、外部合作者沟通,主动去约一个会议,一两次之后大家也就更加放得开了。
  • 时间管理:不害怕拒绝别人,并不是所有的事情都需要自己去参与。
  • 对业务的敏感性:不排斥站在别人的角度思考问题,如果一个外部合作伙伴抱怨了你一次,可能 TA 还有另外数个问题没好意思说出来向你抱怨。这时候自己主动迈出一小步,往往效果大不同。
  • 坦诚:沟通有效的前提是坦诚和信任,不管是与主管、下属、外部合作伙伴都能坦诚沟通。

做得不太好的

  • 谨记:你做的任何一项管理动作可能不会立马带来好的或者坏的影响,但是在之后的某个日子给你带来直接的好的或者坏的结果。
  • 比较晚才意识到:跟不上节奏的员工其实很大改变,这时候结束彼此的合作关系反而是更高效的,不要害怕裁人。
  • 员工的意愿:一个管理者很难改变一个员工的主动能动性,薪资、福利等外部激励手段往往能起到兴奋剂的作用。但是员工内在的自我驱动能力才更为关键。

技术

做得比较好的

  • 寻求持续且渐进的改进,以小步前进,每个小步都带来改进,最终实现系统和全面的转型。这种方式效果很好,下一份工作或许可以借鉴。
  • 非常快的融入了创业公司的范围里,特别是 LeanCloud 的技术氛围。
  • 把 LeanCloud 的软件工程实践(代码评审、故障总结、自动化测试等等)带到了 XDSDK 团队里,也带来了很好的效果。
  • 系统架构上能够除了注意功能性需求的扩展性以外也更加关注非功能性的设计,比如可观测性、高可用等等。

做得不太好的

  • 技术的广度不够:带的是混合团队,但是在技术的广度上还不够,比如前端、客户度。创业型的公司往往缺的是从 0 ~ 1 的人。这可能需要很长的时间去弥补。

LeanCloud

角色

RTM 小组 Tech Lead ,业务上主要负责:即时通信、消息推送、即时语音三个方向。

工作内容

  • 即时通信、消息推送、即时语音三个系统去 Clojure 技术栈,进展不大
  • 建设 Clojure 技术栈向 Golang 转移过程中所需要的公共组件建设,类似基础架构的工作

即时语音

  • 二道贩子封装了腾讯云的语音服务
  • 针对游戏场景做了一些封装

应用元数据

核心应用 提供 GRPC 服务 MySQL + 本地缓存

  • 适合存储频繁访问且数据量不大的数据
  • 无网络延迟
  • 一致性要求不高

使用下面引入的本地缓存组件

本地缓存

开源组件

  • 原来用的 caffeine ,因此继续用 Golang 版本的 caffeine
  • TinyLFU
  • 自定义 reloader 异步更新值,如果后端发生错误依旧使用旧值。
  • 性能好
  • 其他选项:BigCache、FreeCache、Sync.Map

缓存策略:tinylfu maxsize:600,refreshtime:10m,ResilientReloader

Web MiddleWare

gin.HandlerFunc

  • auth: 解析参数+ grpc 确认是否有权限。grpc/metadata + OutgoingContext + golang Context
  • log:
    • 选型:logrus、zap、zerolog
    • slowlog:耗时超过一定时间的自动记录 log ,然后发送到 sentry
    • panic 和 error 发送到 :sentry ,zerolog
      • 在 recover mw(grpc、web) 中使用
  • metric to prometheus
    • request counter :Counter
    • request latency:Histogram
  • 懒加载 Route
    • Gin 的 RouteGroup 不满足
    • 减少不必要的中间件函数调用,从而提高路由处理的性能。只有当路由被请求时,相关的中间件函数才会被应用,避免了在路由注册阶段就执行所有中间件函数的开销。可以将路由的定义模块化,使得不同部分的路由可以独立定义和管理,更好地实现代码的组织和复用。
    • gin.HandlersChain
    • Mount、Dock 方法
  • recover 异常恢复
  • error status format 统一对外错误格式和响应码

GRPC MiddleWare

UnaryServerInterceptor

  • auth 两个 rpc 服务间调用的授权
  • log
    • slowlog:耗时超过一定时间的自动记录 log ,然后发送到 sentry
    • panic 和 error 发送到 :sentry ,zerolog
  • error:
  • recover
    • error 发送到 :sentry ,zerolog

Json-RPC client

Json-RPC client

基于 json-rpc 2.0 protocol, protocol 大致是:

    --> {"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1}
    <-- {"jsonrpc": "2.0", "result": 19, "id": 1}

为了便于测试,提供了一个 mock server

工作成果

  • 多个基础组件,实际的工作比上面列出来的多得多。
  • 支撑了存储从 Clojure 向 Golang 的迁移,支撑新开业务 lake、语音通信服务的建设。

收获

  • 开发基础组件的体验,收获一些 Golang 开发的最佳实践,比如 Functional Options 模式,大量使用了这种模式

遗憾

  • 只参与了其中一个业务系统在 Golang 上的迁移
  • 没能全面了解 LeanCloud 所有系统的全貌