From e014dcdce2d5b3a128c204e5e447598866a48045 Mon Sep 17 00:00:00 2001 From: kennethcheng Date: Thu, 17 Sep 2026 09:36:44 +0800 Subject: [PATCH] docs:README --- AGENTS.md | 50 +++++++++++++++ README.md | 179 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 229 insertions(+) create mode 100644 AGENTS.md create mode 100644 README.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..87c63f1 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,50 @@ +# AGENTS.md + +`spark_project` — Spark(Scala + Java 双实现)学习项目,含三套分层练习与 MySQL 写入示例表。 + +## Setup commands + +- 拉依赖:`mvn -DskipTests dependency:resolve` +- 编译:`mvn compile`(同时编译 `src/main/java` 与 `src/main/scala`) +- 打包:`mvn package`(产出 `target/spark_project-1.0-SNAPSHOT-jar-with-dependencies.jar`) +- 跑测试:`mvn test`(当前 `src/test/` 为空,等价空跑) +- 清理:`mvn clean` +- 单文件运行入口:IDE 里把目标 `object ExercisesX` / `class Xxx` 作为 `main` 直接 run,或: + `java -cp target/spark_project-1.0-SNAPSHOT-jar-with-dependencies.jar org.example.App` + +## Project layout + +- `pom.xml` — Maven 配置,启用 `scala-maven-plugin` 与 `maven-assembly-plugin`,强制 JDK 8 / Scala 2.11 +- `src/main/java/org/example/core/` — Java 版 4 个核心 Demo(`GroupByKeyDemo` / `MyPartitioner` / `RddCreateDemo` / `RepartitionVsCoalesce`) +- `src/main/scala/org/example/App.scala` — Scala 入口(`local[2]` 模式跑 `word.txt` WordCount) +- `src/main/scala/org/example/core/` — 与 Java 同名的 Scala 版 4 个 Demo +- `src/main/scala/org/exercises/exercises1/exercises/` — 第 1 课练习(Spark 入门) +- `src/main/scala/org/exercises/exercises2/exercises/` — 第 2 课练习(RDD 算子:map / filter / flatMap / reduce / foreach) +- `src/main/scala/org/exercises/exercises3/exercises/` — 第 3 课练习(PairRDD / reduceByKey / WordCount) +- `src/main/scala/org/exercises/exercisesN/demo/` — 上述练习对应的演示实现 +- `src/main/resources/` — `log4j.properties` / `word.txt` / MySQL 建表 SQL `create_table.sql` +- `src/test/java/` — 测试目录(JUnit 3.8.1 已声明但当前为空) + +## Code style + +- Java 8(`maven.compiler.source/target=8`)与 Scala 2.11.12 共存;新文件优先 Scala,统一参考现有 demo 的写法 +- Spark Master 全部固定为 `local` / `local[2]`,本机调试无需集群 +- 中文注释和**中文 commit message** 是项目常态(参考最近 6 个 commit),不要切换成英文 + +## Testing instructions + +- 当前 `src/test/` 没有任何用例,跑测试等价于空跑 +- 加测试用 `src/test/java/`,包路径跟随被测包(`org.example` / `org.exercises.xxx`),JUnit 3.8.1(继承 `TestCase`,方法名 `testXxx`) +- 所有测试通过后再提 PR + +## PR & commit conventions + +- 默认分支:`main`,从 `main` 拉 feature 分支,**禁止直接 push 到 `main`** +- Commit 风格:参考现有 `feat:ChatGPT 第 X 课:主题` 形式,保持**简体中文 + 一句话主题** +- 单节课 / 单道练习的提交保持原子;本仓库暂无 CI(`.github/workflows/` 未配置),提 PR 前请本地跑通 `mvn test` + +## Security + +- `.gitignore` 已忽略 `target/`、`.idea/`、`*.iml`、`.classpath` 等构建 / IDE 产物 +- MySQL / Hadoop 凭据**不要**写进 `src/main/resources/`,需要外部化(环境变量或 `*.local.properties`,并加入 `.gitignore`) +- 不要把含个人 token 的 fat jar 提交到仓库 diff --git a/README.md b/README.md new file mode 100644 index 0000000..7f7b528 --- /dev/null +++ b/README.md @@ -0,0 +1,179 @@ +# Spark 学习项目(spark_project) + +跟着 ChatGPT 课程的 Spark 学习笔记,用 **Scala + Java 双实现** 覆盖 Spark Core 的 RDD 基础、PairRDD、WordCount,并通过 MySQL 写入的示例表把练习串成一个迷你数仓案例。 + +> 课程三讲(一→三)一→一对应当前 `src/main/scala/org/exercises/exercises{1,2,3}` 三套练习;另有「线下课:RDD 创建/操作/重分区」的一份 Java 版 Demo。 + +## 技术栈 + +| 类别 | 版本 | +| ---------- | ------------------------------------------ | +| JVM | Java 8 | +| 语言 | Scala 2.11.12 + Java 8(双实现) | +| 计算引擎 | Apache Spark 2.4.3(core + sql) | +| 分布式 | Hadoop Client 2.7.3 | +| 数据库 | MySQL 8.0.28 | +| 连接池 | HikariCP 4.0.3 | +| JDBC 工具 | commons-dbutils 1.6 | +| 测试 | JUnit 3.8.1 | +| 构建 | Maven 3 + scala-maven-plugin 4.8.1 + maven-assembly-plugin 3.7.1 | + +## 项目结构 + +``` +spark_project/ +├── pom.xml # Maven 配置(强制 JDK 8 / Scala 2.11) +├── src/main/java/org/example/ +│ ├── core/ +│ │ ├── GroupByKeyDemo.java # Java 版 4 个核心 Demo +│ │ ├── MyPartitioner.java +│ │ ├── RddCreateDemo.java +│ │ └── RepartitionVsCoalesce.java +│ └── AppTest.java # JUnit 默认骨架 +├── src/main/scala/org/example/ +│ ├── App.scala # Scala 入口:local[2] 模式 + word.txt WordCount +│ ├── test.scala +│ └── core/ # Scala 版同名 4 个 Demo +│ ├── GroupByKeyDemo.scala +│ ├── MyPartitioner.scala +│ ├── RddCreateDemo.scala +│ └── RepartitionVsCoalesce.scala +├── src/main/scala/org/exercises/ +│ ├── exercises1/ # 第 1 课:Spark 入门 +│ │ ├── demo/ # 演示 Demo1~5 +│ │ └── exercises/Exercises1..5.scala # 课后 5 题(题目以中文注释形式嵌入文件头) +│ ├── exercises2/ # 第 2 课:RDD 算子(map / filter / flatMap / reduce / foreach) +│ └── exercises3/ # 第 3 课:PairRDD / reduceByKey / WordCount +└── src/main/resources/ + ├── create_table.sql # MySQL:Top10 平均评分电影 + 类别平均评分 + ├── log4j.properties + └── word.txt +``` + +## 快速上手 + +### 1. 准备环境 + +- **JDK 8**(`pom.xml` 已锁定 `maven.compiler.source/target=8`,高版本会编译失败) +- **Maven 3.6+**(仓库根目录留有 `.mvn/`,但还没生成 `mvnw`,自行安装一个 `mvn` 到 `PATH` 即可) +- 一台能跑 `local` / `local[2]` 模式的机器即可,**不需要真正的 Spark 集群** + +### 2. 编译与打包 + +```bash +# 1) 拉依赖 +mvn -DskipTests dependency:resolve + +# 2) 编译(Scala + Java 同时) +mvn compile + +# 3) 跑测试(当前 src/test/ 仍为空,等价于空跑) +mvn test + +# 4) 打 fat jar(含 Spark / Hadoop / MySQL 等所有依赖) +mvn package + +# 5) 产出路径 +ls target/spark_project-1.0-SNAPSHOT-jar-with-dependencies.jar +``` + +### 3. 跑一个 Scala 入口 + +两种方式二选一: + +**方式 A:IDE 直跑**。在 IntelliJ IDEA 里直接打开 `pom.xml`,把以下任一对象作为 `main` 入口运行: + +- `org.example.App` — `local[2]` 模式读 `src/main/resources/word.txt`,做一次 `count + filter + flatMap → reduceByKey` 的 WordCount +- `org.exercises.exercises3.exercises.Exercises5` — 纯数据集合版 WordCount(输出 `hello=3, spark=3, scala=1, is=1, powerful=1`) + +**方式 B:命令行跑 fat jar**。 + +```bash +java -cp target/spark_project-1.0-SNAPSHOT-jar-with-dependencies.jar \ + org.example.App +``` + +> 默认 `pom.xml` 没配 `exec-maven-plugin`,所以 `mvn exec:java` 用不了,要么走方式 A,要么上 fat jar。 + +## 三套练习索引 + +### `exercises1` — 第 1 课:Spark 是什么,以及第一个 Spark 程序 + +源:`src/main/scala/org/exercises/exercises1/` +主题:`SparkConf` / `SparkContext` 创建、并行化集合、读取外部文件。 +代表题: + +| 编号 | 题目要点 | +| ----- | ------------------------------------------------- | +| Ex 1 | 搭一个 `SparkContext`,`setMaster("local")` | +| Ex 2 | 从本地/集合源构造 RDD | +| Ex 3 | 基础转换算子 | +| Ex 4 | 触发动作算子 | +| Ex 5 | 综合:第一个本地 WordCount | + +### `exercises2` — 第 2 课:RDD 常用算子 + +源:`src/main/scala/org/exercises/exercises2/` +主题:单值 RDD 的转换与动作。 + +| 编号 | 算子 | +| ----- | ------------ | +| Ex 1 | `map` | +| Ex 2 | `filter` | +| Ex 3 | `flatMap` | +| Ex 4 | `reduce` | +| Ex 5 | `foreach` | + +### `exercises3` — 第 3 课:PairRDD 与 reduceByKey + +源:`src/main/scala/org/exercises/exercises3/` +主题:键值对 RDD 常用算子与正式 WordCount。 + +| 编号 | 算子 / 内容 | +| ----- | -------------------------------------------------------- | +| Ex 5 | 正式练 WordCount:`flatMap → map → reduceByKey → collect` | + +> `exercises1/2/3/demo/` 目录下还有 5 个对应演示,可在写练习前打开当作参考实现。 + +## MySQL 结果表 + +`src/main/resources/create_table.sql` 里两张表是后续评分分析的目标输出: + +| 表名 | 主用途 | +| -------------------------- | ---------------------------------- | +| `top_ten_avg_ratings` | TopN 平均评分电影(`movie_id` 唯一) | +| `genres_average_rating` | 类型平均评分(`genres` 唯一) | + +依赖侧(`pom.xml`)已经预备好: + +- `mysql-connector-java` 8.0.28 +- `HikariCP` 4.0.3 +- `commons-dbutils` 1.6 + +下一步只要写真实的 Spark SQL / DataFrame → JDBC 写入逻辑即可。使用前请在 MySQL 里先执行 `create_table.sql`。 + +## 提交规范 + +仓库已有 6 条 commit,主题风格统一为**简体中文 + 简短摘要**: + +``` +feat:ChatGPT 第 1 课:Spark 是什么,以及第一个 Spark 程序 +feat:ChatGPT 第 2 课:RDD 常用算子 +feat:ChatGPT 第 3 课:PairRDD 与 reduceByKey +线下课:RDD的创建、RDD的操作、RDD重分区 +feat:首次提交 +``` + +继续沿用 `feat:<主题>` 形式,**中文一句话主题**,变更保持原子(新题 / 新课一个 commit)。默认分支 `main`,从 `main` 拉 feature 分支再合,禁止直接 push。 + +## 安全约定 + +- `target/`、`.idea/`、`*.iml` 等构建/IDE 产物已在 `.gitignore` +- **MySQL / Hadoop 凭据**一律外部化(环境变量或 `*.local.properties`,并加入 `.gitignore`),不要写进 `src/main/resources/` +- 不要把含个人 token 的 fat jar 提交到仓库 + +## 下一步 + +- [ ] `src/test/` 仍为空,按练习补 JUnit 3 测试用例 +- [ ] 把 Spark SQL/DataFrame 的分析结果真正写回 `top_ten_avg_ratings`、`genres_average_rating` +- [ ] 加一份 `.mvn/wrapper/maven-wrapper.properties` 让团队对齐 Maven 版本