flink项目脚手架

# Flink 项目开发指南

> 基于 dataact-access 项目。Flink 1.20 + JDK 11。

---

## 一、项目结构

```
your-flink-project/
├── pom.xml                          # 根 POM(聚合父模块)
├── module-common/                   # 公共模块(工具类、框架基类,无 Flink 依赖)
│   ├── pom.xml
│   └── src/main/java/com/xxx/common/
│       ├── Main.java
│       ├── app/
│       │   └── StreamCommonFrame.java    # Flink 作业抽象基类
│       └── utils/
│           ├── EnvironmentArgUtils.java  # 命令行参数解析
│           ├── PropertyLoaderUtils.java  # Properties 文件加载
│           ├── CustomLogger.java
│           ├── LogUtil.java
│           ├── DateTimeUtil.java
│           └── AESUtil.java
├── module-app/                     # 业务模块(Kafka → ODPS/MySQL)
│   ├── pom.xml
│   └── src/main/
│       ├── java/com/xxx/access/
│       │   ├── Main.java
│       │   ├── app/                # Flink 作业入口
│       │   ├── bean/               # POJO 实体(含 conf/、enums/)
│       │   ├── source/             # 数据源
│       │   ├── func/               # Flink 处理函数(flatmap/、map/)
│       │   ├── sink/               # 自定义 Sink
│       │   ├── dao/
│       │   └── utils/
│       └── resources/
│           ├── log4j.properties
│           └── module-app-{env}.properties
└── module-ota/                     # 业务模块(Pulsar → ODPS)
    └── ...                          # 结构同上
```

| 模块 | 职责 | 数据源 → 目标 |
|------|------|--------------|
| `common` | 框架基类、工具类 | — |
| `app` | Kafka 消息处理业务 | Kafka → ODPS / MySQL |
| `ota` | Pulsar 消息处理业务 | Pulsar → ODPS |

---

## 二、Maven 多模块配置

### 2.1 根 POM

```xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="..." ...>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.yourcompany</groupId>
    <artifactId>your-project-root</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>

    <modules>
        <module>your-project-common</module>
        <module>your-project-app</module>
        <module>your-project-ota</module>
    </modules>

    <properties>
        <java.version>11</java.version>
        <maven.compiler.source>${java.version}</maven.compiler.source>
        <maven.compiler.target>${java.version}</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.30</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>
```

### 2.2 业务模块 POM(app 示例)

```xml
<parent>
    <artifactId>your-project-root</artifactId>
    <groupId>com.yourcompany</groupId>
    <version>1.0-SNAPSHOT</version>
</parent>

<artifactId>your-project-app</artifactId>

<properties>
    <flink.version>1.20.0</flink.version>
    <scala.binary.version>2.12</scala.binary.version>
</properties>

<dependencies>
    <!-- Flink 核心 (provided:集群已有) -->
    <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-streaming-java</artifactId>
        <version>${flink.version}</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-table-api-java-bridge</artifactId>
        <version>${flink.version}</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-table-planner_${scala.binary.version}</artifactId>
        <version>${flink.version}</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-clients</artifactId>
        <version>${flink.version}</version>
        <scope>provided</scope>
    </dependency>

    <!-- 连接器 (打入 fat jar) -->
    <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-connector-kafka</artifactId>
        <version>${flink.version}</version>
    </dependency>
    <!-- ODPS / MySQL 根据实际选择 -->

    <!-- 模块间依赖 -->
    <dependency>
        <groupId>com.yourcompany</groupId>
        <artifactId>your-project-common</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.11.0</version>
            <configuration>
                <release>${java.version}</release>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>3.5.1</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals><goal>shade</goal></goals>
                    <configuration>
                        <shadedArtifactAttached>true</shadedArtifactAttached>
                        <shadedClassifierName>fat</shadedClassifierName>
                        <filters>
                            <filter>
                                <artifact>*:*</artifact>
                                <excludes>
                                    <exclude>META-INF/*.SF</exclude>
                                    <exclude>META-INF/*.DSA</exclude>
                                    <exclude>META-INF/*.RSA</exclude>
                                </excludes>
                            </filter>
                        </filters>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

<!-- IDEA 本地运行:将 provided 改为 compile -->
<profiles>
    <profile>
        <id>add-dependencies-for-IDEA</id>
        <activation>
            <property><name>idea.version</name></property>
        </activation>
        <dependencies>
            <dependency>
                <groupId>org.apache.flink</groupId>
                <artifactId>flink-streaming-java</artifactId>
                <version>${flink.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.flink</groupId>
                <artifactId>flink-clients</artifactId>
                <version>${flink.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.flink</groupId>
                <artifactId>flink-table-api-java-bridge</artifactId>
                <version>${flink.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.flink</groupId>
                <artifactId>flink-table-planner_${scala.binary.version}</artifactId>
                <version>${flink.version}</version>
            </dependency>
        </dependencies>
    </profile>
</profiles>
```

### 2.3 公共模块 POM

不依赖 Flink,仅工具类 + 基类:hutool、commons-codec、commons-lang3、log4j、lombok。

---

## 三、核心开发模式

### 3.1 框架基类 StreamCommonFrame

使用 **模板方法模式** 统一作业生命周期:

```java
@Data
public abstract class StreamCommonFrame implements Serializable {
    Logger logger;
    String configFile;           // 子类设置,如 "my-app-%s.properties"
    protected Properties props;

    public void run(String[] args) {
        // 1. 解析 -Denv=test 参数
        String env = EnvironmentArgUtils.getEnv(args);
        System.setProperty("env", env);
        // 2. 加载对应环境的 Properties 文件
        this.props = PropertyLoaderUtils.getInstance(
            String.format(configFile, env)).getProperties();
        // 3. 子类实现业务逻辑
        process();
    }

    protected abstract void process();
}
```

### 3.2 作业入口模板

```java
@Slf4j
public class YourApp extends StreamCommonFrame {

    static String configProps = "your-app-%s.properties";

    public static void main(String[] args) {
        YourApp app = new YourApp();
        app.setConfigFile(configProps);
        app.run(args);
    }

    @Override
    protected void process() {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env);

        // Source → Transformation → Sink
        KafkaSource<String> source = KafkaSourceInstance.create(props);
        DataStreamSource<String> ds = env.fromSource(source,
            WatermarkStrategy.noWatermarks(), "kafka-source");

        SingleOutputStreamOperator<YourBean> beanDs = ds
            .map(new YourMapFunc())
            .filter(new YourFilterFunc(props));

        // 输出到 ODPS (Flink SQL)
        registerOdpsTable(tableEnv, props);
        tableEnv.createTemporaryView("v", beanDs);
        tableEnv.executeSql("insert into odps_table select ... from v");

        env.execute("YourApp");
    }
}
```

### 3.3 三种输出模式

| 模式 | 适用场景 | 示例 |
|------|---------|------|
| **Flink SQL → ODPS** | MaxCompute 写入 | `tableEnv.executeSql("insert into odps_table select ... from v")` |
| **DataStream Sink → MySQL** | 自定义 JDBC 写入 | `dataStream.addSink(new CustomMysqlSink(props))` |
| **StatementSet** | 同时写入多表 | `statementSet.addInsertSql(sql1); statementSet.addInsertSql(sql2); statementSet.execute()` |

### 3.4 Source 封装(简洁版)

```java
// Kafka
public static KafkaSource<String> create(Properties props) {
    return KafkaSource.<String>builder()
        .setBootstrapServers(props.getProperty("bootstrap.server"))
        .setTopics(props.getProperty("topic"))
        .setGroupId(props.getProperty("group"))
        .setStartingOffsets(OffsetsInitializer.committedOffsets(
            "latest".equals(props.get("auto.offset.reset"))
                ? OffsetResetStrategy.LATEST : OffsetResetStrategy.EARLIEST))
        .setValueOnlyDeserializer(new SimpleStringSchema())
        .build();
}

// Pulsar
public static PulsarSource<String> create(PulsarParamConf conf) {
    return PulsarSource.builder()
        .setServiceUrl(conf.getPulsarUrl())
        .setAdminUrl(conf.getAdminUrl())
        .setTopics(conf.getTopic())
        .setStartCursor(StartCursor.earliest())
        .setDeserializationSchema(new SimpleStringSchema())
        .setSubscriptionName(conf.getGroup())
        .setConfig(PULSAR_ENABLE_AUTO_ACKNOWLEDGE_MESSAGE, true)
        .build();
}
```

---

## 四、配置文件规范

多环境:`module-{env}.properties`(dev / test / prod)。

```properties
bootstrap.server=kafka-host:9092
your.topic=your-topic
your.group=your-group
auto.offset.reset=earliest

endPoint=http://service.region.maxcompute.aliyun-inc.com/api
project=your_project
accessId=xxx
accessKey=xxx
your.tableName=your_odps_table
```

加载方式:`PropertyLoaderUtils.getInstance("your-app-dev.properties").getProperties()`(单例,线程安全)。

---

## 五、Maven 常用命令

```bash
mvn clean package -pl your-project-app -am          # 构建模块及依赖
mvn clean package -DskipTests                       # 跳过测试
mvn clean install -DskipTests                       # 安装到本地仓库
mvn dependency:tree -pl your-project-app            # 查看依赖树
```

### 常见问题

| 问题 | 解决 |
|------|------|
| 找不到 common 类 | 先 `mvn install -pl common` 安装到本地 |
| Flink 类找不到(本地) | IDEA 自动激活 `add-dependencies-for-IDEA` profile |
| 子模块版本与父不同 | 子模块独立声明 `<version>` 即可 |

---

## 六、版本兼容性

| Flink | JDK | Scala | Kafka Connector | Pulsar Connector |
|-------|-----|-------|----------------|------------------|
| 1.20.0 | 11 | 2.12 | 1.20.0 | 4.1.0-1.20 |

启动参数:`-Denv=test`(必需),`-Dpm=3`(并行度,可选)。
posted @ 2026-09-23 13:25  ---江北  阅读(4)  评论(0)    收藏  举报
TOP