2、开发环境搭建:JDK、Maven、Git、Docker、IDE配置、本地集群模拟

说实话,搭建开发环境这件事,很多新手觉得不值一提。但我在带团队时发现,环境不一致导致的问题,往往比业务逻辑bug更难排查。你想想看,一个推荐系统涉及实时流处理、离线训练、模型服务,每个组件都有自己的依赖版本。版本一乱,整个平台就崩了。

这一章,我带你把环境一次性搭好。咱们的目标是:任何一台新机器,照着这份指南,半小时内就能开始写代码。

核心原则:所有工具版本统一,路径规范,避免使用系统自带的旧版本。

2.1 JDK:选对版本,省一半的坑

实时推荐平台的核心组件——Kafka、Flink、HBase——都依赖Java。我个人习惯用JDK 8,虽然JDK 11和17已经普及,但很多大数据框架对JDK 8的支持最稳定。

我曾经在项目里试过用JDK 11跑Flink 1.13,结果遇到序列化兼容问题,排查了两天才发现是JDK版本导致的。从那以后,我老老实实回归JDK 8。

安装步骤

  1. 下载JDK 8u202(这是Oracle JDK 8的最后一个免费商用版本)
  2. 解压到 /usr/local/java/jdk1.8.0_202
  3. 配置环境变量
# 编辑 ~/.bashrc 或 ~/.zshrc
export JAVA_HOME=/usr/local/java/jdk1.8.0_202
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar

# 验证
java -version
# 输出应为:java version "1.8.0_202"

避坑指南:不要用系统包管理器(apt/yum)安装的OpenJDK,版本太旧。我曾经在Ubuntu上直接 apt install default-jdk,结果装了个1.8.0_252,和Flink的某些特性不兼容。

2.2 Maven:统一依赖管理

推荐平台依赖的第三方库少说也有几十个。Maven能帮你管理版本冲突,避免「在我机器上能跑」的尴尬。

我建议用Maven 3.6.3,这个版本稳定,兼容性好。3.8.x开始对镜像仓库有更严格的校验,容易导致私服配置失败。

配置要点

  1. 解压到 /usr/local/maven/apache-maven-3.6.3
  2. 配置环境变量 export MAVEN_HOME=...; export PATH=$MAVEN_HOME/bin:$PATH
  3. 修改 conf/settings.xml,添加阿里云镜像
<mirrors>
  <mirror>
    <id>aliyunmaven</id>
    <mirrorOf>central</mirrorOf>
    <name>阿里云公共仓库</name>
    <url>https://maven.aliyun.com/repository/public</url>
  </mirror>
</mirrors>

注意:国内网络环境下,不配镜像的话,第一次构建可能要等半小时。我见过同事等得怀疑人生,结果发现是Maven在下载依赖时超时了。

2.3 Git:版本控制与分支策略

推荐平台的代码通常分多个模块:数据接入、特征工程、模型训练、API服务。Git的分支管理直接影响团队协作效率。

我个人习惯用Git 2.30+,这个版本对 main 分支的默认命名支持更好。

分支策略建议

分支 用途 说明
main 生产就绪代码 只允许从 release 分支合并
develop 日常开发集成 所有 feature 分支合并到这里
feature/* 新功能开发 命名如 feature/real-time-recall
release/* 发布前测试 只修bug,不加新功能
hotfix/* 紧急修复 直接从 main 拉出,修复后合并回 main 和 develop

我的经验:在 .gitignore 里一定要加上 target/、*.log、.idea/、*.iml。我曾经看到有人把编译产物提交到仓库,导致仓库体积膨胀到2GB,clone一次要10分钟。

2.4 Docker:环境一致性的终极方案

实时推荐平台依赖的中间件很多:Kafka、ZooKeeper、Redis、MySQL。如果每台机器都手动安装,配置差异会让你崩溃。

Docker能保证开发、测试、生产环境完全一致。我建议用Docker 20.10+,配合 docker-compose 一键启动所有依赖。

安装Docker

# Ubuntu
sudo apt update
sudo apt install docker.io docker-compose

# 启动并设置开机自启
sudo systemctl start docker
sudo systemctl enable docker

# 将当前用户加入docker组(避免每次sudo)
sudo usermod -aG docker $USER
# 重新登录后生效

注意:将用户加入docker组相当于赋予root权限,生产环境不要这样做。但开发环境为了方便,我一般都这么配。

docker-compose 示例

在项目根目录创建 docker-compose.yml:

version: '3.8'
services:
  zookeeper:
    image: wurstmeister/zookeeper:3.4.6
    ports:
      - "2181:2181"

  kafka:
    image: wurstmeister/kafka:2.13-2.8.1
    ports:
      - "9092:9092"
    environment:
      KAFKA_ADVERTISED_HOST_NAME: localhost
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
      KAFKA_CREATE_TOPICS: "click-stream:3:1,rec-result:3:1"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock

  redis:
    image: redis:6.2
    ports:
      - "6379:6379"

  mysql:
    image: mysql:5.7
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: root123
      MYSQL_DATABASE: rec_platform

启动命令:docker-compose up -d,所有中间件就都跑起来了。

2.5 IDE配置:IntelliJ IDEA 调优

我主力用IntelliJ IDEA,社区版就够用。但有几个配置必须调,不然开发体验会很差。

必装插件

  • Lombok:减少样板代码,推荐平台的数据模型类特别多
  • Maven Helper:可视化依赖树,排查冲突神器
  • GitToolBox:在代码行尾显示最后提交信息,方便追责(开玩笑的)
  • Rainbow Brackets:多层括号用不同颜色区分,写Flink的链式调用时特别有用

JVM参数调优

推荐平台的项目通常很大,IDEA默认的堆内存不够。修改 Help > Edit Custom VM Options:

-Xms2048m
-Xmx4096m
-XX:ReservedCodeCacheSize=512m
-XX:+UseG1GC

我的习惯:把 Build, Execution, Deployment > Build Tools > Maven > Runner 里的 Delegate IDE build/run actions to Maven 勾上。这样构建行为完全由Maven控制,避免IDEA和Maven的构建结果不一致。

2.6 本地集群模拟:用Docker搭一套迷你集群

实时推荐平台的核心是流处理。你不可能每次调试都连生产集群。我习惯在本地用Docker模拟一套迷你集群,包含Kafka、Flink、Redis、MySQL。

下面这张图展示了本地集群的架构:

本地迷你集群架构图 数据源 模拟点击流 Kafka 消息队列 Flink 实时计算 MySQL 用户/物品元数据 Redis 特征缓存/实时结果 HBase 用户行为历史 说明 • 实线箭头:数据流方向(点击流 -> Kafka -> Flink -> 存储) • 虚线箭头:Flink 实时关联 MySQL 中的维度表(用户画像、物品属性) • Redis 存储实时推荐结果,供 API 服务查询 • HBase 存储用户历史行为,用于离线特征计算

启动集群

# 一键启动所有服务
docker-compose up -d

# 查看状态
docker-compose ps

# 验证Kafka
docker exec -it rec-platform_kafka_1 kafka-topics.sh --list --bootstrap-server localhost:9092
# 应该看到 click-stream 和 rec-result 两个topic

我的经验:第一次启动时,Kafka和ZooKeeper的启动顺序很重要。docker-compose虽然配置了 depends_on,但ZooKeeper完全就绪需要几秒钟。我习惯在启动后等5秒再操作Kafka,或者加一个健康检查脚本。

2.7 验证环境:跑一个Hello World

环境搭好了,咱们跑一个最简单的Flink程序验证一下:

import org.apache.flink.streaming.api.scala._

object HelloFlink {
  def main(args: Array[String]): Unit = {
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    
    val stream = env.socketTextStream("localhost", 9999)
    stream
      .flatMap(_.split(" "))
      .map((_, 1))
      .keyBy(0)
      .sum(1)
      .print()
    
    env.execute("Hello Flink")
  }
}

启动后,在另一个终端执行 nc -lk 9999,输入一些单词,看控制台是否输出词频统计结果。能跑通,说明环境没问题。

注意:如果报错 java.lang.NoClassDefFoundError: scala/Product,说明Flink和Scala版本不匹配。Flink 1.13对应Scala 2.12,检查pom.xml里的scala.version是不是2.12。

好了,环境搭完了。你可能会觉得步骤有点多,但相信我——这一章花的时间,会在后续开发中十倍地省回来。环境一致了,bug就少了一半。接下来,咱们就可以真正开始写推荐系统的代码了。