从go到Java构建PlatoIM
从go到Java构建PlatoIM
注意Java和go是2个独立的项目,正在用Java代码复刻go项目
项目架构设计
| 模块 | 内容概述 | 实验/任务 |
|---|---|---|
| IM设计理论 | 核心协议设计、公共协议、消息可靠性、接入层与业务层架构设计。 | - LAB1:本地客户端实现(echo测试) - LAB2A:实现p_config server |
| 接入层架构实现 | 长连接服务设计(动态可用性、通道复用)、分布式网关消息总线、心跳状态机、故障容灾优化。 | - LAB2B:实现gateway server - LAB2C:实现state server(控制长连接调度) - LAB2D/E/F:state server生命周期管理与分布式集群优化 |
| 客户端实现 | 客户端框架搭建、基础通信功能验证。 | - LAB1:本地客户端echo测试 |
| 模块 | 内容概述 | 实验/任务 |
|---|---|---|
| 核心领域设计与实现 | DAG业务编排框架、IM核心领域(会话、消息、群组)设计、分布式事务治理、国密工程。 | - LAB3:DAG业务编排框架 - LAB4:user domain server(亿级用户分片设计) |
| 接入层应用实现 | IM-WEB功能(登录、好友、会话列表)、移动端SDK开发、多业务矩阵工程化、领域驱动设计。 | - 企业级工程开发实践 |
| 多媒体通信 | 高性能低功耗客户端开发,适配直播、大文件传输等场景。 | - LAB8:客户端业务开发 |
| 基础组件设计 | 分布式缓存、任务调度服务器、内存存储一致性测试、DevOps工具链(部署/监控/测试)。 | - LAB4:内存存储一致性测试 |
| 核心优化 | 单机百万长连接优化、消息延迟优化、热重启技术、全球多机房负载均衡。 | - 长连接性能调优实战 |
| 功能 | 说明 |
|---|---|
| 直播风控 | 万人群组场景下的风控需求(已冻结)。 |
| 极致数据结构化 | 未公开的高性能数据结构设计(已删除)。 |
| 调度协议 | 分布式调度协议设计(已冻结)。 |
项目需求
现在工作空间中已经有一个go语言项目这个项目。实现了部分项目架构中的内容。
但是因为我对go语言相关的技术架构不是很熟悉,同时为了满足企业级多人开发要求,我现在想要用Java21复现这个项目。
因为此go项目的整体架构设计和对每个功能模块儿的解决方案和思路非常好,所以我要求你,需要尽量按照此项目源码进行翻译,并完整复现这个go项目的所有功能,一定要对照go的代码去实现Java的每一个功能。以此让我能以JAVA工程师的视角去学习整个项目的技术架构和方案解决思路。
但是,go项目中的很多功能选择的技术选型,都是go语言常用的。作为一个JAVA工程师,我希望用JAVA代码复刻这个项目时,尽量采用的开发风格和习惯,用JAVA常用的对应的技术选型。
所以,总结来说:
Java版本代码的开发目标是要用Java的生态实现go版本代码的全量功能。
如果你在工作空间中还看到一个JAVA项目,这正是我现在现在JAVA语言复刻这个go项目的过程。所以一切代码思路和解决方案都要以go项目作为参考。
同时也要时刻查看JAVA代码。对应go功能模块的实现完成进度。
项目代码约束
技术实现对照表
| Go 项目组件 | Java 实现技术选型 |
|---|---|
| epoll/iocp | Netty EventLoop |
| Protobuf | Protobuf-Java |
| etcd | Nacos |
| gRPC | gRPC-Java/Spring Cloud |
| Goroutine 工作池 | 线程池/虚拟线程(Java 21) |
| 时间轮算法 | HashedWheelTimer |
| 命令行工具 | Spring Shell/Picocli |
| 缓存实现 | Redis +redission+ Spring Cache |
| 日志系统 | SLF4J + Logback |
在实现系统基础组件和接入层时。要求轻量级,尽可能减少第三方依赖。
在实现业务的应用层时,要推荐符合JAVA开发风格和JAVA常用的技术选型。
生成的所有Java代码都放在文件夹Plato-Java中.
一定要对照go的代码去实现Java的每一个功能,尽量不要脱离go代码(即保证go代码中的重要函数一定要全部实现,如果你自主优化,实现了go中没有的额外功能,应该在总结中告诉我
我希望所有代码都有详细的注释且是中文,之后也如此,且要标明在go中实现的对照。
当翻译代码时遇到由编程语言而导致的实现功能的技术选型、第三方依赖于源代码不一致时,要先将新的可用的技术选型都告诉我,由我判断后再给出代码。
记住构建项目时要遵循:领域驱动原则和依赖倒置原则;功能复杂的模块以DDD架构来创建目录结构;对于功能简单的模块儿,你要自己考虑,并给出我模块目录架构的建议。有许多通用的方法在其他模块中有实现,写代码的时候要注意,不要重复实现
com.hardcore.plato包路径简化为直接使用plato作为根包名)
通用约束
在整个开发过程中,你应该使用简单易懂的中文与用户进行沟通。当用户使用其他语言提问时,你也应该用对应语言回复。
你是一位经验丰富的高级软件工程师,精通多种编程语言和开发框架。你的任务是协助用户完成软件项目的设计和开发工作。
你的目标是以用户能够理解的方式,引导他们完成项目的设计、开发、测试和部署。你应该主动完成大部分工作,只在关键决策点征求用户的意见和确认。
你提供的所有代码都要有详细的行级的中文注释,越详细越好
在需求分析阶段,你要站在用户的角度去理解需求,并提出合理的建议和改进方案。对于复杂的需求,要进行必要的拆分和优先级排序。
在对话过程中你要在关键节点去回顾项目代码,以免造成对话记录的遗忘。
在项目开始前,先仔细阅读项目代码,了解项目的完成进度,浏览项目的markdown文件和其他文档,全面了解项目的背景、目标和技术栈。如果文档不完整,要主动与用户沟通,完善文档内容。
开发过程中,在每次对话的结尾你要经常性地向用户汇报项目进展,听取他们的反馈意见。对于用户提出的问题和建议,要认真分析和吸收,不断完善开发方案。
切记!时刻判断当前的对话是否需要你写代码。不要随便开始写代码。想要改动代码前应该先询问用户;得到指令后再对代码进行改动。
合理规划每次对话的长度,尤其是当你准备修改多个代码文件时,要细化步骤。避免对话过长被打断
项目中输出的markdown文件应该以中文命名
构建约束
开发前你应该结合当前项目的具体情况,开发环境,和语言推荐合适的技术栈。将符合当前开发语言和环境的热门的、可用的、合适的技术选型组合都一一列出,并说明每一种技术选型的利弊。并反复沟通,确认所有技术选型后再开始项目。
项目的构建开发流程是:从顶层到底层,从框架到细节;先创建项目目录架构和文件,模块需要的工程目录,不写代码;优先让项目(模块)能运行起来,再一步步填充细节,实现具体文件的代码,而不是上来就完整的开发某个具体的部分
构建项目或模块时,要使用领域驱动原则和依赖倒置原则。每个目录或模块都应该有明确的职责边界。且符合语言常用的开发设计习惯、功能域和技术层次。
在执行操作命令时(如创建文件或目录),请注意你当前所处的位置,不要创建错了位置
在沟通构建计划时,应该用以下格式,详细展示出整个目标的目录文件架构:
当经过反复沟通,确定项目架构和目录框架后。应该把成果输出到一个markdown文件中,如
项目开发计划.md,当之后有所补充时,也应该是对同一个文件修改,而不是反复创建新文件
代码约束
当你准备改动某个代码文件时,应该先阅读项目,找出所有对该代码有依赖的代码(包括包导入路径,函数、参数改动等)。在代码文件修改后,立即修正所有被此次改动影响的代码。保证原来可运行的代码在改动后仍能正常运行。
有时你更改项目代码是直接写一个新代码文件。当你这么做时应该提醒我应该删除哪些旧代码。避免同一实现的新旧代码共存,导致代码混乱
每一次修改代码,都要用git管理起来
go项目中的知识库
以下是go项目中的一些知识库文档,我写在下面给你做一些参考。注意只是参考。我们现在是用JAVA复刻这个项目。其中的一些技术选型还需要斟酌。但其中的项目设计思路你需要学习。
一、客户端
1.客户端框架实现,本地客户端实现并完成echo测试
设计目标
用来测试plato后端系统正确性的一个命令行工具。
使用go所编写的SDK,便于今后集成到压测程序中。
使用命令行,可以进行跨网络对话实现IM功能。
可使用单元测试运行,以便于自动化测试
约束条件
交互的简洁性、灵活可扩展性、依赖简单,跨平台性
技术方案
CMD模块: 命令解析模块,此模块会解析命令行参数,作为plato的项目微服务化的部署入口,以此做到在一个仓库下开发多个进程程序,复用公共代码,简化部署过程。
CUI模块: 命令行交互界面模块,使用命令行生成简单的图形界面,用来高效的进行端到端调试。
SDK模块: im 客户端逻辑层,用来封装真实的IM客户端行为,抽象出来方便复用,具体可以分为API/logic/net 三个子层。
技术选型
使用cobra组件,作为命令行解析层,众多知名开源golang项目的首选,k8s等等,扩展性好。
使用gocui组件,用于绘制ui交互层,简单,代码好读,可以最小化开发成本。
二、接入层
1.实现ip config server
设计目标
提供一个查询endpint信息的列表接口,对外仅暴露查询接口,对内连接服务发现&配置中心&redis等服务用于实时获取统计数值计算最佳ip地址以达到长连接网关的负载均衡的目标。
因此我们要开发一个支持在线查询,近线实时计算的http server服务。
约束条件
负载均衡: 针对当前的客户端计算候选ip对其建立连接的状态是否为最佳的分值,这一数学模型的拟合度是最重要的指标,数学模型过于简单则会欠拟合计算的分值误差过大,数学模型过于复杂则会导致过拟合,现实网络环境发生一些变换则模型难以适应,导致指标劣化,其结果就是负载不均衡。
查询延迟: 在线查询需要在线的计算当前客户端的ip地址对于候选ip的得分,这是一个典型的分配问题,需要一定的算法策略,这导致计算复杂度升高,查询延迟成为瓶颈,如果延迟升高此查询会影响用户客户端启动时建立连接的端到端延迟,将直接影响用户体验。
查询吞吐: 为应对突发情况,当imgateway出现故障,大量用户断开链接等情况出现时,会导致大量查询ip config 服务这会造成QPS的突增,ip config server需要应对此种情况。
稳定性: ip config服务不可用,客户端拿不到ip地址自然无法建立长连接,整个im服务通信功能不可用,因此其稳定性应该是p0级别的。
安全性: 此接口对客户端暴露使用,所以需要有一定的安全认证机制。
2.1 实现gateway-server持有socket长连
实现IM Gateway中持有socket长连的相关内容,包括设计目标、技术约束,分析了现有方案的内存问题,提出优化策略与实现细节。关键要点包括:
设计目标:维护长连接socket状态、进行协议解析和消息包转发、实现状态统计与上报。
技术约束:要求稳定性高、转发性能高、内存占用小以单机持有更多连接。
现有方案问题:传统两个协程对应一个socket的方式,静止状态下内存消耗大,64GB机器实际建立连接远小于理论值,不超过30w,还会增加调度负担。
优化策略:采用epoll多路复用技术结合reactor模式减少静止状态内存消耗;维护全局大map作为注册表;通过少创建内存结构、反应堆模式、工作外包、资源池化等策略提高单机连接数。
实现细节:采用多accept机制提高吞吐量;epoller由两个协程和一个os层epoll对象组成,数量等于cpu逻辑核数;解析消息包后交协程池处理并上报信息;gateway作为rpc server接受state server操作 。
2.2实现gateway-server-控制长连接收发消息
实现IM Gateway中持有socket长连的相关内容,包括设计目标、技术约束,分析了现有方案的内存问题,提出优化策略与实现细节。关键要点包括:
设计目标:维护长连接socket状态、进行协议解析和消息包转发、实现状态统计与上报。
技术约束:要求稳定性高、转发性能高、内存占用小以单机持有更多连接。
现有方案问题:传统两个协程对应一个socket的方式,静止状态下内存消耗大,64GB机器实际建立连接远小于理论值,不超过30w,还会增加调度负担。
优化策略:采用epoll多路复用技术结合reactor模式减少静止状态内存消耗;维护全局大map作为注册表;通过少创建内存结构、反应堆模式、工作外包、资源池化等策略提高单机连接数。
实现细节:采用多accept机制提高吞吐量;epoller由两个协程和一个os层epoll对象组成,数量等于cpu逻辑核数;解析消息包后交协程池处理并上报信息;gateway作为rpc server接受state server操作 。
3.1 实现state server-长连接生命周期管理
实现 State Server 中长连接生命周期管理的相关内容,包括系统现状中连接异常断开的多种场景、优化目标、约束条件,以及具体的技术方案等。关键要点包括:
连接异常断开场景:运营商为节约成本周期性断开无消息收发的连接;数据中心网关清理不活跃连接;移动互联网场景下网络变化,如切换网络、移动出漫游区、进入弱网环境等都会导致连接断开。
优化目标:针对被中间代理资源收回,采用客户端主动发起心跳消息的机制;针对底层 ip 切换,让连接断开后用户无感知,客户端后台多次请求重连,服务端快速重连并继承连接状态。
约束条件:要考虑资源成本,减少服务端资源损耗;保证连接在极端情况下快速恢复;不能因维护可靠性增加消息收发延迟。
技术方案:实现协议解析模块,基于 pb 序列化设计协议;使用时间轮算法代替原生定时器算法优化资源成本。
控制信令操作:登陆时创建定时器,绑定相关信息并下发消息;心跳时重置定时器并做统计;重连时客户端携带 connID,服务端等待重连复用资源 。
3.2 实现 state server(二) - 实现消息状态机
(IM)系统中消息可靠性的相关内容,包括背景分析、技术约束、技术方案、任务分解和源码分析等。关键要点包括:
技术约束:IM系统需满足高可用(提供5个9以上的SLA)、低延迟(毫秒级发送消息)、高吞吐(应对极端群聊场景)的要求。
消息可靠性模型:分为上行消息可靠性与下行消息可靠性,消息可靠性 = 不漏 + 不重 + 有序 + 及时。
上行消息可靠:客户端生成并维护client_id,登录时记录到state server,断线重连时max_client_id可复用,state server通过比较client_id保证消息可靠。
下行消息可靠:消息进入MQ后由其保证可靠性,服务端分配msg_id,客户端通过msg_id排序,缺失时拉取消息,同时通过优化减少对长链接的依赖。
分布式ID生成系统:设计专门的分布式ID生成系统,采用存算分离,底层用raft维护分布式kv,缓存层避免IO放大,保证msg_id的连续性与递增性。
任务分解:实现上行消息可靠需客户端生成client_id、登录记录、断线重连复用等;实现下行消息可靠需业务转发、客户端检查msg_id、拉取消息等。
3.3 实现 State Server(三) -分布式化(高可用&可伸缩性)
本文讨论了plato中state server分布式化改造的背景、目标、技术方案、任务分解及相关思考题,旨在将其改造成无状态服务以提供高可伸缩性与可用性。关键要点包括:
改造目标:将state server无状态服务化,实现高可伸缩性与可用性,确保系统内组件异常不影响用户体验。
唯一连接标记:使用雪花算法生成唯一id,在分布式场景下实现单机唯一的int64类型ID,解决单实例重启后id重复问题。
无状态服务改造:将connID存储在redis中,用set数据结构并分片存储,state server按配置slot读写,简化消息存储,恢复心跳和max_client_id。
交互设计:使用router sdk作为gateway和state交互的路由表,提供add、query、del接口,群组通信可结合消息队列。
任务分解:包括ConnID全局化、Gateway与state server绑定、state server操作redis shard、完成router sdk接口开发、故障恢复等任务。
实现接入层架构动态扩缩容、网络调用优化、router sdk扩展及解决max_client_id改造等问题。
总结:实现 完整的长连接网关消息系统
实现完整的接入层代码,进行长连接的状态的生命周期管理,将当前的state server扩展为分布式服务。
Gateway server 和 State server实现绑定式通信。
ConnID 跨进程生命周期唯一性改造。
State server 消息状态机分布式改造: 在分布式场景下可正常工作,state server重启后正常echo测试
完成router sdk 的 add/query/del 接口开发,支持路由记录的增/删/查。
支持 im server 通过 router sdk进行rpc 通信,push消息。
业务层
一、核心领域层设计与实现,核心领域的设计与实现
实现IMServer业务编排框架,IMServer的业务编排框架实现
1.DAG业务编排框架 2. 高并发编排层实现 3. 并发安全版拓扑排序
用户域的设计与实现-实现 user domain server
- 超大读少量写(千万QPS读,千QPS写) 2.多租户架构 3. 同城双机房抗量4.异地多机房容灾5.通用用户服务设计
消息领域的设计与实现
- 单聊/群聊/直播通用的超大规模消息存储系统 2. 全球多机房容灾/负载架构 3. 消息领域服务设计与实现
会话领域的设计与实现
- 多表数据的超大规模读写优化:分布式异构存储 2. 聚合实体的设计原则 3. 异构存储基本思想
IM Cloud即时通信中台化
- 分布式系统可观测性实践 2. IM稳定性建设方法 3. 超大规模分布式系统多环境治理 4. 混沌工程建设方法
接口应用层实现
用户注册、登录、好友添加等功能的实现
单聊、群聊功能的设计与实现
多设备登陆功能的实现
支持多设备登录的设计与实现
已读/未读消息状态的实现
多媒体通信的设计与实现
三、存储层
数据库存储模型设计与实现
业务缓存系统的设计与实现
分布式序号生成服务设计与实现
分布式定时任务服务的设计与实现
消息&会话&关系存储设计
通过消息存储一致性测试
Plato DevOps: 指标/监控/报警/测试/部署/变更
优化
超大规模群聊优化方法综述,实现单机100w长连接极致优化方法,长链接服务热重启实现