写代码的人大概都经历过这种事:项目上线前看着代码没啥问题,一上线就出幺蛾子。后来团队引入静态代码分析,把检查环节卡在提交之前,情况才算好转。我在 Windows 环境下从零搭过 SonarQube 和 Sonar Scanner 的组合,踩了不少坑,把整个搭建配置过程整理出来,给打算在自己电脑上或公司内网搭一套代码质量分析平台的同学做个参考。这篇东西适合后端开发者、测试工程师、运维同学,也适合想给团队建立代码质量门禁但没有专门服务器资源的小团队。
1. 环境准备:版本搭配与Windows下的几个坑
1.1 版本选型是最容易被忽略的起跑线
第一次搭建的人往往一上来就下载最新版 SonarQube,然后卡在启动环节。SonarQube 对 JDK 版本有硬性要求,比如 9.x 系列要求 JDK 17,10.x 系列要求 JDK 17 或 21,而早年用的 7.x 系列只要 JDK 8。版本不对,服务直接起不来,错误日志里会明确提示缺失 Java 11/17 之类。
我建议直接用目前还在维护的 LTS 版本,比如 SonarQube 9.9 LTS 配 JDK 17,这是比较省心的组合。数据库方面,默认自带 H2 只适合试用,一重启数据就没了,真正要用必须换 PostgreSQL。9.9 对 PostgreSQL 的兼容范围是 13 到 15,我实测 14 和 15 都没问题。
这里有一个基础版本搭配表,可以直接照着准备:
| 组件 | 推荐版本 | 说明 |
|---|---|---|
| SonarQube | 9.9 LTS 或 10.x | LTS 优先,稳定且资料多 |
| JDK | 17(64位) | 9.9 必须 17,10.x 要求 17+ |
| PostgreSQL | 14 或 15 | 9.9 兼容 13~15,别用 16 |
| Sonar Scanner | 与 Server 同版本或更高 | 版本差异过大可能认证失败 |
1.2 Windows下JDK配置要注意的细节
Windows 上装 JDK 有两个坑:一是环境变量配错,二是装了多个 JDK 导致版本混乱。SonarQube 启动时读的是 JAVA_HOME,不是 PATH 里的 java,所以务必在系统变量里设置JAVA_HOME指向 JDK 安装目录,然后在 PATH 最前面加上%JAVA_HOME%\bin。设置完在命令提示符里跑java -version确认版本,注意别看到 1.8 就觉得完事了。
另一个细节是位数。SonarQube 服务端是 Java 应用,跑在 64 位 JDK 下内存管理更从容。如果你机器上装了 32 位 JDK,后面调大内存参数时经常会报Could not reserve enough space for object heap,排查半天才发现是 JDK 位数不对。所以统一用 64 位 JDK 17,不要混用。
1.3 PostgreSQL 准备:用户、密码和数据库一步到位
数据库这一步很多人直接跳过,觉得默认 H2 够了,等第二次启动发现之前配置的分析记录全没了才后悔。PostgreSQL 装好后,用 pgAdmin 或者命令行创建 SonarQube 专用的用户和数据库,不要用 postgres 超级用户去跑,原则是权限最小化。
我用命令行演示一次,Windows 下直接打开 psql 工具执行:
CREATE USER sonar_user WITH ENCRYPTED PASSWORD 'sonar_pass'; CREATE DATABASE sonar_db OWNER sonar_user; GRANT ALL PRIVILEGES ON DATABASE sonar_db TO sonar_user;记住这个库名和账号密码,后面配置 sonar.properties 要原样填进去。PostgreSQL 安装完默认监听 5432 端口,如果改过端口,后面配置里也要对得上。还要检查 PostgreSQL 的pg_hba.conf,确认本地连接方式是md5或scram-sha-256,否则 SonarQube 连接时会弹认证失败。
1.4 路径、防火墙和“不要用管理员”三条规矩
Windows 下安装路径一定不要带中文、空格和特殊符号,比如把 SonarQube 解压到D:\Program Files这种路径就会莫名奇妙出问题。我习惯建一个干净的目录,比如D:\sonar\sonarqube-9.9和D:\sonar\sonar-scanner。
防火墙方面,SonarQube 默认监听 9000 端口,如果你要局域网内其他同事访问,需要在 Windows 防火墙入站规则里放行 TCP 9000。自己在本机调试则不用管。最后一条:启动 SonarQube 时不要右键“以管理员身份运行” StartSonar.bat,因为管理员模式下关闭命令窗口不会让 Java 进程退出,而是直接杀掉服务,反而更容易出现端口未释放的问题。普通用户双击启动脚本就行。
2. SonarQube 服务端安装:从解压到首次初始化
2.1 下载和解压的正确姿势
SonarQube 的发行包是 zip 格式,解压后目录结构大概是bin、conf、lib、logs、extensions这几个。第一次拿到包时,不要急着启动,先把conf\sonar.properties打开,把数据库和端口配置改好。
这里有个经验:解压之后先看一眼logs目录是不是空的,如果里面有报错日志,说明之前有人启动过,最好重新解压一遍,避免残留配置干扰排错。我遇到过解压过程中被杀毒软件拦截部分文件的情况,结果启动时缺 class 文件直接崩溃。Windows Defender 偶尔会误杀启动脚本或 jar,建议把 Sonar 目录加入信任区。
2.2 sonar.properties 里的核心配置项
打开conf\sonar.properties,大部分内容都是注释状态,把需要的那几行取消注释并改成自己的值。关键配置如下:
sonar.jdbc.username=sonar_user sonar.jdbc.password=sonar_pass sonar.jdbc.url=jdbc:postgresql://localhost:5432/sonar_db sonar.web.port=9000 sonar.web.javaOpts=-Xmx2048m -Xms1024msonar.web.port默认就是 9000,如果 9000 被占用可以改成 9001,但记住后面访问地址和 Sonar Scanner 里都要跟着改。sonar.web.javaOpts是 Web 进程的堆内存参数,机器内存 8G 的话给 2G 起步,太小了跑大项目分析时容易卡死。
还有一个参数sonar.path.data决定数据文件存放位置,默认在安装目录下。Windows 下我建议把它指到一个独立目录,比如D:\sonar\data,这样以后升级 SonarQube 时可以直接保留数据目录,不用整体拷贝。
2.3 启动脚本与“本机访问不了”问题
启动方式很简单,进入bin\windows-x86-64目录,双击StartSonar.bat。这里要注意,如果操作系统是 64 位,目录一定是windows-x86-64;如果装成 x86 版本,启动脚本在bin\windows-x86下,两者不要搞混,否则日志里会一直报找不到平台动态库。
启动后不要立刻打开浏览器,SonarQube 首次启动要做数据库初始化和数据迁移,这个阶段要等 1 到 3 分钟。判断标准是看命令行窗口出现了类似SonarQube up的提示,这时再访问http://localhost:9000。如果页面打不开,优先看logs\sonar.log和logs\web.log。
在这个环节最容易犯的错误是启动后本机能访问但局域网其他电脑访问不了。先检查防火墙是不是放行了 9000 端口,再确认sonar.properties里没有把sonar.web.host设置成127.0.0.1。如果确实设置了,改成0.0.0.0或干脆注释掉,让服务监听所有网卡。
2.4 首次初始化:登录、改密码、建 Token
浏览器访问http://localhost:9000,默认账号是admin,默认密码也是admin,首次登录系统会强制你修改密码。改完之后,进入用户菜单,在“My Account”里找到 Security 选项卡,生成一个 Token。这个 Token 就是后面 Sonar Scanner 连接服务端的凭证。
Token 只显示一次,一定要复制保存。当年我搭完环境顺手关了页面,之后配 Sonar Scanner 的时候回来再取,结果又生成了一次,弄得口令管理乱七八糟。
服务端首次联网会下载一些插件,比如语言分析插件、SCM 统计插件,国内网络环境下偶尔会超时。不影响核心功能,后面配置扫描器时再手动补充离线插件就行。我建议同时安装一个中文语言包插件,在 Administration 的 Marketplace 里搜索 Chinese Pack,装完重启服务端就是中文界面了,排查问题方便很多。
3. Sonar Scanner 客户端:选型与环境配置
3.1 用哪种方式跑分析
SonarQube 的客户端不止一种,最基础的是 Sonar Scanner CLI,适合本地手动跑、脚本定时跑;Maven 项目用mvn sonar:sonar插件;Gradle 项目用sonarqube插件;IDE 里也有 SonarLint 插件。团队刚起步时建议统一用 Sonar Scanner CLI,它不依赖构建工具,任何语言都能分析,后续接 CI 流程也方便。
我个人的实践是:开发者在本地用 SonarLint 做编码时实时提示,真正做质量门禁和统计时用 Sonar Scanner CLI 去分析。两者使用的是同一套规则库,但一个负责边写边查,一个负责提交前全量扫描。
3.2 下载、解压与环境变量
Sonar Scanner 的 zip 包解压后目录里有个bin\sonar-scanner.bat,Windows 下把SONAR_SCANNER_HOME指向解压目录,然后在 PATH 里追加%SONAR_SCANNER_HOME%\bin。配置完成后,在新的命令行窗口执行sonar-scanner -v,能看到版本信息就说明设置成功。
这里有个干扰项:如果你同时装了 Jenkins 或 Maven,PATH 里可能有其他同名命令。我建议在执行分析的命令行窗口里先用echo %SONAR_SCANNER_HOME%确认指向是否正确,避免莫名其妙的“命令未找到”。
另外,Sonar Scanner 的conf\sonar-scanner.properties里默认写了一个sonar.host.url示例值为http://localhost:9000,保持这个值就行,也可以在里面预先写好 Token。但我更推荐把 Token 放到项目级的sonar-project.properties里,因为 Scanner 的全局配置是全机器共享的,写死 Token 容易被误传。
3.3 sonar-project.properties 是分析行为的核心
执行sonar-scanner命令时,它会读取项目根目录下的sonar-project.properties文件。这个文件告诉 Scanner 要分析哪些代码、代码用什么语言、编译产物在哪、哪些文件要排除。
我的一个典型配置长这样:
sonar.projectKey=my-project-key sonar.projectName=我的项目 sonar.projectVersion=1.0 sonar.sources=src sonar.java.binaries=target/classes sonar.sourceEncoding=UTF-8 sonar.exclusions=**/*DTO.java,**/generated/**sonar.projectKey是项目的唯一标识,多个项目不能重复,我习惯用公司名加项目名的形式,比如order-service。sonar.sources指定源码目录,Java 项目必填sonar.java.binaries,否则 Java 分析器拿不到 class 文件,很多语义级规则跑不起来。sonar.exclusions用来排除自动生成的代码,比如 DTO、接口文档生成类,避免误报刷屏。
如果项目源码在子目录,比如 Maven 多模块项目,sonar.sources可以写module1/src,module2/src这种逗号分隔形式。模块多的话,可以直接在根目录配置后,命令进入某个子模块再执行,Scanner 会自动识别当前目录。
3.4 执行扫描:看到 SUCCESS 才算开始
配置完整后,在项目根目录打开命令行,执行:
sonar-scanner第一次跑会下载对应的分析器插件,等个十几秒到几分钟,然后开始逐文件分析。日志末尾出现ANALYSIS SUCCESS说明分析完成,看到BUILD SUCCESS更完整,此时可以去 Web 界面查看报告。如果日志里出现Project does not exist. Please create it first这样的提示,代表服务端还没这个项目,直接在 9000 端口页面上手动新建项目即可,或者手动传sonar.projectKey去匹配已有项目。实际上新版 Scanner 会自动创建项目,但如果服务端禁止自动创建项目,就需要先手动建。
日志中有一段INFO: ANALYSIS SUCCESS, Time:这样的输出,这个是 Scanner 自己打印的,表示本地处理完成。真正的服务端入库成功要看几秒后的EXECUTION SUCCESS。
4. 拿真实项目跑通一次完整分析
4.1 准备一个简单的 Java 项目
我建议第一次练习时建一个最小的 Maven Java 项目,目录结构如下:
D:\demo-project\ pom.xml src\main\java\com\demo\DemoApplication.java src\main\java\com\demo\OrderService.javapom.xml 里不用加 Sonar 插件,因为我们要用命令行 Scanner 独立分析。先在项目目录下执行mvn clean package,确保target\classes里有编译产物,因为sonar.java.binaries指向这里。
代码里故意留点问题——比如一个空的异常捕获、一个可以提升为局部变量的类字段、一个简单的 SQL 拼接——这样分析结果里才能看到不同类型的问题,便于熟悉页面。
4.2 命令执行与日志解读
在项目根目录放好sonar-project.properties,然后执行:
sonar-scanner日志里有一个关键阶段Sensor JavaXmlSensor和Sensor JavaSecuritySensor,看到这些说明 Java 分析插件已经加载。完成后继续执行:
sonar-scanner -Dsonar.projectKey=demo-project -Dsonar.sources=src -Dsonar.java.binaries=target/classes -Dsonar.sourceEncoding=UTF-8这条命令等价于把配置文件里的参数用命令行方式传入,方便临时覆盖配置。如果是首次运行,控制台还会输出你的默认 Token 有没有配置,没配置的话会提示 401 错误,这时去conf\sonar-scanner.properties里补上sonar.token即可。
运行完看日志末尾几行:
INFO: ANALYSIS SUCCESS, Time: 15s INFO: EXECUTION SUCCESS4.3 Web 界面里看什么
回到http://localhost:9000,左侧会出现demo-project。点进去能看到四个维度:可靠性(Bugs)、安全性(Vulnerabilities)、可维护性(Code Smells)和覆盖率(Coverage)。新建项目的话覆盖率为空是正常的,因为还没有关联覆盖率报告。
质量门禁(Quality Gate)显示绿色 Passed 或红色 Failed,默认的门禁规则只有几条,比如新增代码覆盖率不低于 80%、没有新增 Bug 等。这个门禁可以在 Administration 的 Quality Gates 里自定义。我在团队里就是卡新增代码覆盖率 80% 和新增问题为零这两条,太宽松没意义,太严格又会阻碍交付。重点还是得结合团队现状慢慢调。
初次分析完,点进OrderService.java文件,能看到每一行代码对应的 warning、note 或 error。左侧筛选器可按类型和严重程度过滤,建议先把 Blocker 和 Critical 级别的处理完,Minor 的积攒到一定量再统一清。
4.4 增量分析与全量分析的差别
SonarQube 默认只对新代码的质量门禁负责,历史遗留问题只做记录。在后台任务里可以触发“全部重分析”,把整个项目重新过一遍。Windows 局域网环境里如果项目很大,全量分析时间比较长,建议放到夜间定时跑。
我在单位就是用 Windows 计划任务定时执行sonar-scanner,每天凌晨分析一次主项目,早上看一眼质量门禁趋势。这个方案比 Jenkins 轻量,适合还没有专门 CI 服务器的小团队。
5. 常见问题排查:从日志到解决方案的完整链路
5.1 启动失败先看 logs 目录,别看浏览器
SonarQube 服务端启动失败时,浏览器会一直转圈或者直接拒连,这时候打开logs目录下的sonar.log,里面写了完整的启动链路。最典型的错误是数据库连接失败,日志里会出现:
Caused by: org.postgresql.util.PSQLException: FATAL: password authentication failed for user "sonar_user"这个基本就是sonar.properties里的用户名密码错了。还有一种情况是权限失败,日志里提示Access denied for user,这是 PostgreSQL 端的问题,回头检查pg_hba.conf。所有数据库问题有个共性——日志会明确告诉你哪个环节失败,不要一上来就重装。
5.2 9000 端口被占用
本机同时装了 Jenkins、Nginx 或者其他 Web 服务时,SonarQube 默认的 9000 端口很容易撞车。日志里会看到:
BindException: Address already in use: bind解决办法有两个:要么修改 SonarQube 的sonar.web.port,要么把占用端口的进程关掉。在 Windows 下查占用进程用:
netstat -ano | findstr 9000 taskkill /PID 你的进程号 /F我不建议直接 kill,因为可能杀掉另一个重要服务。改端口更稳妥,但注意同时要改 Sonar Scanner 的sonar.host.url。
5.3 JDK 版本不匹配的报错
如果日志里出现:
Unsupported class file major version 61.0说明当前 JDK 版本太老,SonarQube 9.9 要求的是 JDK 17,class file major version 61 对应 JDK 17。如果出现这个错误,去检查JAVA_HOME是不是被旧版本占了,或者系统 PATH 里第一个 Java 是 8。顺带提醒,执行java -version看到的是 PATH 里的版本,和 JAVA_HOME 可能不一致,排查时两个都要确认。
5.4 分析完成但页面看不到任何问题
这种情况最气人——日志显示 SUCCESS,页面也出现了项目卡,但问题列表是空的。我遇到过两次,原因分别是:sonar.java.binaries指向的目录不存在;以及sonar.sources写成了绝对路径但项目文件在相对路径下认不到。
建议检查三件事:配置文件里sonar.sources是否写对了相对路径;sonar.java.binaries是否指向编译输出目录;以及sonar.exclusions是否过滤掉了所有问题文件。还有一个隐藏原因:Windows 下编码问题,项目源码是 GBK 而sonar.sourceEncoding写了 UTF-8,导致中文注释乱码且部分规则无法匹配,问题数看着少了一大截。统一改 UTF-8 并从配置里指定编码后,问题列表就正常了。
5.5 调整 Java 进程内存避免崩溃
默认的 JVM 内存配置比较保守,分析大型项目时 Web 端或后台任务可能直接崩溃。日志里出现OutOfMemoryError时,打开conf\wrapper.conf找到wrapper.java.additional相关配置,把最大堆内存调大:
wrapper.java.additional=-Xms1024m wrapper.java.additional=-Xmx2048m同时也把sonar.web.javaOpts里的-Xmx调成一致。Windows 32 位 JDK 到 1.5G 堆内存就撑不住了,所以还是那句话,用 64 位 JDK 是省心前提。
6. 最后的实战体会与一个小技巧
搭完这一整套环境后最大的体会是:SonarQube 的安装本身不难,难的是在团队里把流程跑起来。工具放在那没人用等于零,要让开发者觉得“它是帮我提前发现问题”,而不是“又多了一个被卡的流程”。我现在的做法是每个项目在合并请求前强制跑一次 Sonar Scanner,质量门禁没过不让合入,连续跑两个迭代后,线上缺陷率确实有明显下降。
最后分享一个小技巧:Windows 下把分析命令封装成一个 bat 脚本,放到项目根目录旁边,比如scan.bat,内容就两行:
@echo off cd /d %~dp0 sonar-scanner以后双击就完成分析。如果想做每日自动扫描,就把这个 bat 注册到 Windows 计划任务里,触发器选每天凌晨两点,起始目录填项目路径,稳稳当当。这个方案虽然比不上一键部署的 CI 流水线,但对于没地理条件的本地或小团队来说,已经足够轻量实用。等以后规模大了再迁移到专门的 CI 上,数据和规则都还在,迁移成本也不高。