
简介一套基于CentOS 7.6、Nginx、Redis、Tomcat 8与MySQL 8.0环境的即时通讯系统全平台开源源码包面向具备一定Linux运维与Java开发基础的技术人员可用于学习IM架构设计、私有化部署及二次开发。压缩包共1569个文件约526.79MB涵盖606个jar后端程序、407个png与146个jpg界面素材、100个m4a及11个mp4演示媒体、60个js与47个css前端资源另有16个properties配置、5个sql数据库脚本及3个apk安装包等类型覆盖了从服务端、管理后台到安卓客户端的完整链路。配套搭建说明明确列出了创建4个数据库、修改默认密码、宝塔建站及启动脚本等关键步骤并附带示例域名替换与后台账号admin/密码888888便于快速跑通流程。目前已有125人学习下载适合希望快速获得一套可运行IM系统并进行源码研究与功能扩展的开发者。1. 2025即时通讯系统源码值不值得碰先看清“全平台”到底给了你什么做企业内部系统、在线教育或私域工具时最逃不开的模块就是聊天。买商业IM服务按日活计费从零写又得折腾连接管理、消息持久化和多端协议没三个月下不来。这也是2025年开源即时通讯源码包满地走的原因一套号称全平台、带搭建教程的源码解压后理论上能自建一套可私有化部署的聊天系统。它能解决的核心问题只有一个——用最低的时间成本把IM能力嵌进自己的产品。适合中小团队技术负责人、独立开发者和企业IT。接下来我按拿到源码包之后的真实流程写拆包、跑服务端、接客户端、排坑最后给到能上线前的调优方向。先说结论“全平台”不等于开箱即用很多包只给了你能跑通的最小闭环下文先把这个缺口拆透。2. 拆开源包从目录结构到技术栈判断避开“全平台”的隐藏缺口2.1 解压后先别急着跑先核对顶层目录里有哪些端拿到一个开源IM源码包我一般不会先看README而是先解压看目录。因为“全平台”这三个字在不同包里含义差别很大。有些包的全平台是“服务端AndroidiOSWeb”有些则是“服务端管理后台uniapp客户端”桌面端和鸿蒙端经常缺席。unzip latest-im-2025.zip -d ./im-project cd im-project # 只看一层目录判断模块划分 find . -maxdepth 2 -type d | sort执行后你会看到类似这样的模块划分server是服务端client/android、client/ios是原生端client/web是浏览器端client/desktop是PC桌面端admin是管理后台docs放数据库脚本和搭建文档。这一步我建议你做一个动作把目录名抄下来和搭建教程里的“功能清单”逐行对照。常见的缺口是教程说支持桌面端但client/desktop目录里只有一个空壳工程教程说全平台但鸿蒙端只是提了一嘴没有实际代码。出现这种情况不一定是作者骗你更可能是他只在某个分支或私有仓库里维护发布出来的zip包没来得及同步。所以解压后第一件事永远是对模块别对口号。2.2 用三个信号判断服务端技术栈决定你要装什么环境开源IM的服务端主流只有两条路Java系Spring Boot/Netty和Go系Gin/grpc-gateway少数老项目用C或Node.js。判断方法很简单看server目录里的依赖文件。# 在 server 目录下执行看哪个文件存在 ls -la go.mod pom.xml package.json requirements.txt 2/dev/null存在go.modGo语言编译快、部署简单我本地调试最爱这种一条go run main.go就能起服务。存在pom.xmlJava Maven项目大概率是Spring Boot跑起来要装JDK和Maven启动慢一些但生态成熟适合要长期维护的团队。存在package.jsonNode.js常见于老一代的IM开源实现聊天场景下性能不如前两者但上手门槛最低。存在requirements.txtPython多见于演示项目或带AI客服的IM不适合直接扛高并发。这里要提醒一点服务端技术栈直接决定你后面所有的环境准备。如果是Go项目你只需要Go 1.21以上如果是Java项目JDK版本和Maven仓库的镜像配置会折腾你小半天。我遇到过一个包教程里写的是Java 8但源码里用了var关键字和List.of()实际上是JDK 11的写法代码一启动就编译报错。所以别全信教程以依赖文件里的版本声明为准。2.3 “全平台”到底包含哪几端一张核对表把缺口找出来拆完包之后用下面这张表快速核对能帮你判断这套源码的投入价值。端常见目录判断是否完整的标志最容易踩的坑服务端server有配置文件、数据库脚本、启动入口数据库脚本分散在多个目录漏导入Web端client/web或web有package.json和src源码不是dist打包产物只给编译后的静态文件改不了代码Android端client/android有build.gradle和app/src缺少签名配置直接装不上iOS端client/ios有.xcodeproj或.xcworkspace证书和Bundle ID不匹配桌面端client/desktop有Electron或Qt工程文件经常只是壳聊天逻辑要自己补管理后台admin有登录接口和用户管理页面和服务端的接口地址写死对照完这张表你基本就能判断这套源码的完整度。如果只缺桌面端影响不大如果连Web端都是编译后的静态文件那后续想改功能就麻烦了因为现在2025年的IM场景里Web端是运营和客服介入最频繁的入口。拿到包先做这一步能省掉后面很多“这功能怎么没有”的困惑。3. 把服务端本地跑起来数据库初始化、消息端口与最小启动命令3.1 准备运行时先把JDK/Go、MySQL和Redis的版本对齐开源IM的服务端几乎都依赖MySQL存业务数据、Redis存在线状态和分布式锁。动手前先检查本机环境版本不对后面全是玄学报错。# 服务端是 Go 系时检查 Go 版本 go version # 服务端是 Java 系时检查 JDK 版本 java -version # 数据库和缓存 mysql --version redis-cli ping版本对齐的保守组合是MySQL 8.0 Redis 6.x/7.x Go 1.22 或 JDK 17。如果本机MySQL是5.7遇到JSON字段和窗口函数时很容易语法报错Redis低于5.0则不支持Stream数据结构而很多2025年新出的IM项目会用Stream做消息队列。我建议你在一个干净的目录里用Docker起依赖而不是污染本机的MySQL调试完可以整体销毁重建。# 用 Docker 起一个带 utf8mb4 的 MySQL 8.0 和一个 Redis 7 docker run -d --name im-mysql -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDim123456 \ -e MYSQL_DATABASEim_server \ mysql:8.0 --character-set-serverutf8mb4 --collation-serverutf8mb4_unicode_ci docker run -d --name im-redis -p 6379:6379 redis:7这里有两个参数容易忽略。第一个是MYSQL_DATABASE如果你不提前建库后面导入脚本前还要手动CREATE DATABASE纯多此一举。第二个是character-set-serverutf8mb4IM消息里全是emoji和特殊字符不用utf8mb4存进去就是乱码这个坑在安卓端发emoji时一定会炸。容器起来后用docker ps确认端口已经映射出来。3.2 初始化数据库找到SQL脚本按顺序导入而不是一把梭开源IM项目的数据库脚本一般放在docs/sql或server/sql目录下通常拆成init.sql、structure.sql、data.sql这种粒度。常见做法是按文件名前缀的编号顺序执行不能用一条命令导入整个目录因为表有外键依赖建表顺序错了会报ForeignKeyConstraintViolation。# 逐个导入不要用 *.sql 这种通配符 mysql -uroot -pim123456 im_server docs/sql/01_init.sql mysql -uroot -pim123456 im_server docs/sql/02_structure.sql mysql -uroot -pim123456 im_server docs/sql/03_data.sql导入完成后花两分钟检查关键表而不是直接启动服务。重点看有没有im_user、im_message、im_conversation这三类表以及im_message的msg_seq字段是否存在。msg_seq是消息序号很多IM的业务逻辑依赖它做消息幂等和顺序保证缺了这个字段服务端可能起得来但发消息时会一直报“消息序号为空”。# 查看表是否导入成功 mysql -uroot -pim123456 im_server -e SHOW TABLES LIKE im_%; mysql -uroot -pim123456 im_server -e SHOW CREATE TABLE im_message\G检查到msg_seq是bigint类型且带有唯一索引就可以继续下一步了。如果表结构和你本地数据库有冲突先别急着改表回到第2章核对服务端代码是不是和当前数据库脚本版本一致。我就碰到过脚本是新的、代码是旧的情况登录接口查不到用户表里的新字段服务端日志一直报Unknown column。3.3 改配置后启动服务配置文件里的4个必改项启动服务前有一道绕不开的工序改配置。开源IM的配置文件一般是config.yaml或application.yml放在server/conf或server/src/main/resources下。不要用编辑器全局替换手改下面四个位置即可。# server/config.yaml 典型配置段 server: # 监听地址必须改成 0.0.0.0否则手机/局域网设备连不上 bind: 0.0.0.0 port: 8080 mysql: host: 127.0.0.1 port: 3306 username: root password: im123456 database: im_server # 时区要和服务器一致差8小时会让离线消息的拉取范围出错 timezone: Asia/Shanghai redis: host: 127.0.0.1 port: 6379 password: # 有些项目用 db3 存放在线状态注意别和业务缓存混在一起 db: 3 jwt: # 上线前必须改默认密钥在网上是公开的谁都能伪造token secret: sx7f0a2b9c8d4e6f1a3b5c7d9e0f2a4b expire: 604800四个必改项我按优先级排一下第一是server.bind不改的话服务只监听在本机回环地址局域网设备永远连不上第二是jwt.secret开源包自带的密钥是公开的别人能拿默认密钥伪造管理员token2025年很多私搭IM被入侵都是这个原因第三是mysql.password这个不解释第四是redis.password如果你的Redis有密码就填没有就保持空字符串但生产环境必须设。改完后启动。Go项目直接在server目录下编译运行cd server go mod download go run main.go 21 | tee server.logJava项目则是cd server mvn clean package -DskipTests java -jar target/im-server.jar --spring.profiles.activelocal无论是哪种启动后别立刻看结果先等日志输出稳定重点观察有没有success或started on port字样。如果出现panic或BeanCreationException多数是数据库脚本没导全或Redis连不上回上一步排查不要带着报错硬往下走。3.4 验证服务是否起来端口监听、健康检查和日志三件事服务进程没退出不代表服务可用我见过很多次进程活着但端口没监听的情况。用下面的命令做最小验证# 看端口是否在监听8080 换成你配置里的端口 ss -lntp | grep 8080 # 请求健康检查接口返回 JSON 说明服务已就绪 curl -s http://127.0.0.1:8080/health # 实时看日志有没有异常堆栈 tail -f server.log/health是开源IM项目最常见的健康检查路径返回的JSON里一般包含status: ok和数据库连接状态。如果ss显示监听地址是127.0.0.1:8080而不是0.0.0.0:8080说明配置没生效回去改第3.3节的bind项。如果curl一直超时大概率是启动还没完成再等两三分钟Go项目冷启动要下载依赖编二进制Java项目做Mapper扫描也要一会儿。到这里服务端的最小闭环已经跑通。接下来要解决的是“客户端怎么连上它”的问题也就是下一章的连接测试。4. 客户端和Web端连接测试账号体系、会话通道与多端互通验证4.1 获取账号体系入口先注册再登录拿Token别直接连WebSocket很多人在服务端跑通后第一件事就是打开Web端页面登录结果卡在登录页不知道默认账号密码。开源IM项目的账号体系一般分两种一种是脚本里内置了种子账号另一种必须在启动后调接口注册。我建议直接调注册接口顺手把HTTP接口链路验证了。# 注册一个用户返回用户ID curl -X POST http://127.0.0.1:8080/api/v1/user/register \ -H Content-Type: application/json \ -d {username:test01,password:Pssw0rd0755,nickname:测试号} # 登录获取token curl -X POST http://127.0.0.1:8080/api/v1/user/login \ -H Content-Type: application/json \ -d {username:test01,password:Pssw0rd0755}注意注册和登录接口返回的数据结构重点看token和user_id字段。有些项目的token不是直接返回而是放在data.token里还有的项目要求先调/api/v1/ws/sign获取一个短时凭证再去连WebSocket。这里的参数有讲究密码强度要求是开源项目普遍会做的我上面的Pssw0rd0755是故意带大小写和数字的组合避免因为密码强度校验失败导致白折腾。用户名不要用中文部分老项目对中文用户名的URL编码兼容很差登录时token能拿到但WebSocket握手会莫名失败。拿到token后把它存到一个临时环境变量里后续所有带鉴权的请求都要用export IM_TOKEN上一步返回的token字符串4.2 用Node.js直连WebSocket把token放到握手阶段才不会被踢即时通讯的核心通道不是HTTP而是WebSocket。大多数开源IM项目要求客户端在握手阶段把token放在协议头或query参数里。浏览器原生WebSocket没法自定义Header所以普遍做法是ws://127.0.0.1:8080/ws?tokenxxx。下面是Node.js的最小连接脚本用来验证服务端的WS通道是否真的通了。// im-ws-test.js // 运行前提npm install ws const WebSocket require(ws); const token process.env.IM_TOKEN; const ws new WebSocket(ws://127.0.0.1:8080/ws?token${token}); // 监听连接打开事件 ws.on(open, () { console.log(ws connected); // 发送一条单聊消息给 test02 发消息 ws.send(JSON.stringify({ type: chat, conversationType: 1, // 1 单聊2 群聊 targetId: 用户ID_字符串, content: hello from test01 })); }); ws.on(message, (data) { const msg JSON.parse(data.toString()); // msg.type 常见值ack(消息回执)、chat(新消息)、online(状态变更) console.log(recv:, msg); }); ws.on(close, (code, reason) { console.log(closed:, code, reason.toString()); });这段代码里的三个参数是必调的conversationType决定消息走单聊路由还是群聊路由写错的话消息会进到不存在的会话里targetId必须是对方在服务端里的用户唯一ID不是用户名content在2025年的开源实现里普遍支持JSON字符串为的是让富文本和引用消息能在各端解析。如果你发完消息后收到ack回执说明消息已经到达服务端。4.3 两个端互通验证Web端和手机端收发同一条消息才是真通只在一个端连上WebSocket并没有证明“全平台”。必须做一次双端互通验证用前一步的Node脚本模拟一个端再用浏览器登录Web端模拟另一个端。我先说结论双端互通最容易卡在群聊会话上。单聊只需要两个用户ID群聊则要先去调CreateGroup接口建群然后把两个成员都拉进群。很多新手不知道这个顺序直接拿单聊流程套群聊结果消息发出去石沉大海。# 建一个群返回 group_id curl -X POST http://127.0.0.1:8080/api/v1/group/create \ -H Authorization: Bearer $IM_TOKEN \ -H Content-Type: application/json \ -d {name:测试群,memberIds:[用户B的ID]}把返回的group_id填进WebSocket脚本里的targetId并把conversationType改成2再跑一次。如果浏览器端能收到这条消息说明服务端的消息路由、群成员关系、离线存储都已经生效。到这一步你自己的最小闭环才算跑通。4.4 消息流水日志服务端日志里必须出现的关键记录双端互通做完后回来看一眼服务端日志。一个正常的开源IM服务端在收到消息时会打三类日志gateway.recv表示消息进了网关router.dispatch表示完成了会话路由store.save表示消息持久化成功。grep -E gateway.recv|router.dispatch|store.save server.log | tail -20三个关键字都出现说明消息链路完整。如果只有gateway.recv而没有后面的router.dispatch问题多半出在conversationType或targetId上。如果router.dispatch有但store.save没有说明消息没写进im_message表重启后消息会丢。这个问题在下一章的避坑里会继续展开。5. 搭建避坑指南服务端起来了却连不上、收不到消息的5个真实原因5.1 现象服务端进程活着客户端一直提示“连接失败”这是最常遇到的情况。客户端连不上第一反应查WebSocket端口结果ss -lntp显示服务明明在监听换到用户手机就超时。原因有两个第一监听地址绑在了127.0.0.1只允许本机访问第二云主机安全组或本地防火墙没有放行8080端口。解决修改配置文件里的server.bind为0.0.0.0重启服务后再用ss -lntp确认监听地址不再是回环IP。云主机用户还要去控制台安全组放行TCP 8080同时注意要把0.0.0.0/0配到入方向规则里只放行127.0.0.1等于没放行。本地用虚拟机调试的检查VMware/VirtualBox的网络模式是不是NATNAT模式下宿主机访问虚拟机端口要做端口转发。5.2 现象数据库脚本导入时报Invalid default value或Unknown collation报这个错大概率是你本机的MySQL版本和源码包的预期版本不一致。2025年的新项目普遍默认MySQL 8.0如果你用的是MySQL 5.7遇到utf8mb4_0900_ai_ci排序规则或DEFAULT CURRENT_TIMESTAMP的增强写法就会直接中断。解决优先把本地数据库升级到MySQL 8.0。如果项目限定必须用5.7可以用编辑器打开SQL脚本全局把utf8mb4_0900_ai_ci替换成utf8mb4_general_ci再把有ON UPDATE CURRENT_TIMESTAMP的字段定义改成DEFAULT CURRENT_TIMESTAMP。我这里强调的是“优先升级”因为改脚本替换排序规则只是绕过了语法检查某些索引长度和JSON字段的行为差异后面还会冒出来。5.3 现象A端显示消息发送成功B端始终收不到消息链路里发送成功的回执由网关返回并不代表对方已经收到。B端收不到最常见的原因是Redis里没有对方的在线通道信息。开源IM的在线状态普遍是“用户ID到网关连接”的映射存在Redis里B端上线时如果连的是另一个网关实例而这个实例没有注册到服务发现中心A端所在网关就不知道把消息投递给谁。解决查Redis里有没有在线状态键。执行redis-cli -n 3 keys *online*看返回如果是空说明B端的WebSocket握手没走完。再检查B端是不是用了HTTP用到的8080端口去连WebSocket而源码里WS端口可能是独立的8443端口搞错就会出现“逻辑上在线实际上没连上”。5.4 现象Android端打包后能安装但登录时提示“网络异常”安卓端最容易出这个问题因为这涉及两层限制。第一层是明文流量限制Android 9以上默认禁止明文HTTP而教程里的服务端地址是http://192.168.x.x:8080不是https://第二层是证书校验部分项目在调试期用的是自签名证书release包直接把证书校验写在代码里导致抓包软件都连不上。解决在Android工程的res/xml/network_security_config.xml里把调试域名加到明文流量白名单。如果源码里没提供这个配置就在AndroidManifest.xml的application节点加android:networkSecurityConfigxml/network_security_config。这是调试阶段的做法上线前务必换回HTTPS和正规证书。iOS端类似要在Info.plist里加NSAppTransportSecurity的NSAllowsArbitraryLoads但上线前必须收紧。5.5 现象教程里的搭建命令在新系统上跑不通报command not found教程是半年前写的很多命令在新系统上已经变了最常见的是apt-get、yum、dnf混用还有mysql_secure_installation命令在MySQL 8.0中已废弃。更隐蔽的是教程里用了nohup ./server 启动新系统如果没给可执行权限会直接报Permission denied。解决不要整段复制教程的命令。逐条拆开执行先which go java mysql redis-cli确认命令存在再看ls -l确认可执行文件有x权限。用chmod x server补权限。日志是最诚实的启动失败时先看错误输出里的路径和权限信息大部分“教程跑不通”都是环境变量没生效导致的。设置完export PATH$PATH:/usr/local/go/bin后要执行source ~/.bashrc让配置生效否则当前终端里还是旧PATH。6. 从能跑到能上线消息可靠性、离线推送与多端登录的进阶调优6.1 先验证消息可靠性断网重连后消息能不能补齐本地能收发消息只是起点。我用一个笨办法验证消息可靠性开着两个客户端A发一条消息后立刻断网重连后看B能不能收到这条。很多开源IM在断线期间靠消息序号做补偿重连时会拉取“上次收到的最后一条消息序号”之后的数据。如果拉不到大概率是服务端的消息序号在应用层生成而不是由数据库自增生成断了之后序号断层。验证方法是在im_message表里手动插入一条高序号记录看客户端重连后会不会补拉。如果你准备长期用这套源码这一步能直接决定是否值得继续投入。6.2 离线推送和通知渠道国内安卓厂商通道要不要接本地调试不需要推送但真要上线离线推送绕不开。2025年的开源IM方案里离线消息通常存在服务端用户下次登录时拉取但这解决不了“App在后台被杀”的场景。国内Android环境必须接入厂商推送通道小米、华为、OPPO、vivo否则App被系统回收后消息完全收不到。我的建议是先不接厂商通道用Web端和iOS端跑通业务确认产品形态稳定后再接推送。因为厂商推送需要在各厂商开放平台创建应用、配置回执调试成本远高于IM本身。这个“接入顺序”我强调了很多次真有团队在核心功能没验证时就先做推送最后厂商通道调通了消息收发却因为时序问题丢了。6.3 多端登录与在线状态踢人策略要按业务定而不是按默认走多端登录策略看起来是小事实际影响很大。开源IM的默认策略常见两种单端登录新登录踢掉旧端和多端共存。如果你做内部办公IM默认就是多端共存但要注意Web端和手机端同时在线会导致消息重复推送服务端的去重逻辑一般靠消息ID过滤如果代码里没有幂等判断客户端要自己做去重。建议在配置里把“同端互踢、跨端共存”的策略开起来这是2025年开源IM项目里比较成熟的处理方式。验证方法也很简单手机和Web同时登录一个账号用手机给另一个用户发消息Web端收到后不会重复弹出且两台设备的状态都显示在线。最后说一个我自己栽过的跟头拿到源码后我习惯先把所有依赖升到最新版结果IM项目里某个基础库升级后WebSocket握手包格式变了客户端一直握手失败排查了两天。后来我养成了习惯先按源码自带的依赖版本跑通再考虑升级。搭建这类开源项目稳定跑通永远比用最新版本重要等整个链路完全掌握后再逐个升级依赖并跑回归测试。希望这篇能帮你少走点弯路把有限的精力留给业务本身。本文还有配套的精品资源点击获取