实战指南:从规则编写到违规管理)
大数据流处理批处理数据工程【免费下载链接】flink项目地址https://gitcode.com/gh_mirrors/fli/flink点击查看免费下载Apache Flink 在flink-architecture-tests模块中基于 ArchUnit 建立了一套面向架构约束的自动化测试体系把哪些代码可以出现在哪里、谁可以依赖谁、公开 API 必须如何标注等隐式约定变成每次构建都会强制执行的可验证规则。本文将以该模块为核心讲解其三大子模块的职责划分、生产代码与测试代码两套执行模型的设计差异并结合仓库源码逐条解读现网规则API 可见性注解、Connector 依赖边界、Table API 配置项规范、ITCase 测试约束等最后给出测试失败时的标准处理流程与新增规则的完整操作步骤帮助你在理解原理的同时具备直接上手编写和维护架构测试的能力。模块全景一个中心两大类别三个子模块考虑到类路径隔离classpath isolation与架构测试规则的组织方式flink-architecture-tests将架构测试划分为两个顶层类别生产代码架构测试production code architectural tests测试代码架构测试test code architectural tests由于这两类测试都需要共享一部分 ArchUnit 扩展整个模块进一步拆分为三个子模块子模块职责flink-architecture-tests-base提供生产代码与测试代码架构测试共用的 ArchUnit 扩展如GivenJavaClasses、Conditions、Predicates、ImportOptions等公共工具flink-architecture-tests-production集中定义并执行针对生产代码的架构规则详见其 READMEflink-architecture-tests-test集中定义针对测试代码的架构规则但规则在各子模块本地构建执行详见其 README两套执行模型为何生产代码与测试代码的架构测试基建不同一个容易引起困惑的设计是flink-architecture-tests-production既集中实现又集中执行规则而flink-architecture-tests-test只集中实现规则执行则分散到每个开发了测试代码的子模块中。flink-architecture-tests-test的 README 给出了三个理由降低类路径复杂度若要在中心模块集中执行测试代码架构规则每个子模块都必须额外产出 test-jar而 IntelliJ 并不完全支持所有 Maven 配置例如 exclusion 过滤器这会导致同一套规则在 Maven 与 IntelliJ 下行为不一致此外产出更多 test-jar 也会显著增加项目复杂度。关注点分离测试代码应被视为模块内部实现通过为每个子模块单独维护违规存储violation store可以保持这种模块内聚的边界与生产代码测试不同测试代码之间共享代码很少集中执行带来的类缓存性能收益并不适用。灵活性每个子模块除了引入flink-architecture-tests-test中定义的通用规则还可以在本地继续开发模块专属的架构测试。生产代码架构测试集中执行与规则全解执行入口与类导入策略生产代码架构测试的入口是 ArchitectureTest.javaAnalyzeClasses( packages org.apache.flink, importOptions { ImportOption.DoNotIncludeTests.class, ImportOptions.ExcludeScalaImportOption.class, ImportOptions.ExcludeShadedImportOption.class }) public class ArchitectureTest { ArchTest public static final ArchTests COMMON_TESTS ArchTests.in(ProductionCodeArchitectureBase.class); }AnalyzeClasses声明了要分析的类范围包org.apache.flink下的全部生产类并通过三个ImportOption过滤掉测试类、Scala 类与 shaded 代码。其中ExcludeScalaImportOption与ExcludeShadedImportOption在 ImportOptions.java 中实现分别通过.*/scala/.*与.*/shaded/.*正则排除对应位置的类排除 shaded 类不仅是为了避免测试外部被 shade 进org.apache.flink.shaded.*的代码更是为了控制内存占用。COMMON_TESTS通过ArchTests.in(ProductionCodeArchitectureBase.class)引入 ProductionCodeArchitectureBase.java后者聚合了三组规则ArchTest public static final ArchTests API_ANNOTATIONS ArchTests.in(ApiAnnotationRules.class); ArchTest public static final ArchTests TABLE_API ArchTests.in(TableApiRules.class); ArchTest public static final ArchTests CONNECTORS ArchTests.in(ConnectorRules.class);将全部生产代码架构测试放在一起运行而非在每个模块单独运行可以复用已导入的类以获得更好的性能——这一点在 生产子模块 README 中明确说明。ApiAnnotationRules公开 API 的可见性注解纪律ApiAnnotationRules.java 定义了四条规则全部用FreezingArchRule.freeze()包裹冻结规则见下文违规存储一节ANNOTATED_APIS位于org.apache.flink..api..包、且不在..internal..包内的所有公开类必须至少带有Internal、Experimental、PublicEvolving、Public、Deprecated中的一种可见性注解。PUBLIC_API_METHODS_USE_ONLY_PUBLIC_API_TYPES被Public注解或在Public类中声明的公开方法其返回值与参数类型必须位于org.apache.flink..之外、shaded 包中或本身带有Public/Deprecated注解。PUBLIC_EVOLVING_API_METHODS_USE_ONLY_PUBLIC_EVOLVING_API_TYPES对PublicEvolving方法有同样的类型约束允许的类型注解集合为Public、PublicEvolving、Deprecated。NO_CALLS_TO_VISIBLE_FOR_TESTING_METHODS生产代码不得调用标注了VisibleForTesting的方法。规则通过自定义DescribedPredicateJavaMethodCall判定调用目标并放行以下合法场景调用方自身被VisibleForTesting标注、调用方与被调用方属于同一个类或互为内外类关系。其中用于校验方法叶子类型leaf types的haveLeafTypes条件实现在 Conditions.java 中对于数组类型取基础组件类型对于泛型类型同时检查类型自身与其类型参数再对方法返回值、参数、异常类型逐一校验。ConnectorRulesConnector 只能依赖公开 APIConnectorRules.java 定义了CONNECTOR_CLASSES_ONLY_DEPEND_ON_PUBLIC_API规则覆盖org.apache.flink.connector..与org.apache.flink.streaming.connectors..两个包Connector 生产代码排除被Deprecated标注的类只能依赖以下三类位于 connector 包之外的 Flink 公开类直接或间接带有Public/PublicEvolving注解或封闭在外层公开类中org.apache.flink..之外的外部类connector 包内部或org.apache.flink.util..工具包内的类。这条规则的意图是保证 Connector 不穿透 Flink 内部实现细节只面向稳定的公开 API 编程。TableApiRulesTable API 的配置项与可见性规范TableApiRules.java 包含四条规则CONFIG_OPTIONS_IN_OPTIONS_CLASSESorg.apache.flink.table..包中声明的公开静态ConfigOption字段必须声明在类名以Options结尾的类中或声明在FactoryUtil中。TABLE_FACTORIES_CONTAIN_NO_CONFIG_OPTIONS实现了DynamicTableFactory的类中不允许直接声明ConfigOption字段。CONNECTOR_OPTIONS_PACKAGE类名以ConnectorOptions或FormatOptions结尾排除含Json的类的类必须位于org.apache.flink..table包且带有PublicEvolving或Public注解。ALL_CLASSES_IN_TABLE_API_SHOULD_HAVE_VISIBILITY_ANNOTATIONS位于flink-table-api-java、flink-table-api-java-bridge、flink-table-common、flink-table-api-bridge-base等公开 Table API 模块中的所有公开类都必须显式标注可见性注解。该规则通过正则.*/flink-table-(api-(bridge-base|java(|-bridge))|common)/.*匹配类源码位置来识别公开 Table API 模块可见从源码结构上规则的覆盖面直接与这几个模块的物理路径绑定。测试代码架构测试中心定义、模块内执行通用规则集合TestCodeArchitectureTestBase.java 是测试代码架构测试的通用基类目前聚合了ITCaseRulesArchTest public static final ArchTests ITCASE ArchTests.in(ITCaseRules.class);ITCaseRules.java 定义了两条针对集成测试的规则INTEGRATION_TEST_ENDING_WITH_ITCASE继承自org.apache.flink.test.util.AbstractTestBase的非抽象类类名必须以ITCase结尾。ITCASE_USE_MINICLUSTER顶层、非抽象的*ITCase类必须使用 MiniCluster 资源或扩展。规则接受四种合规写法JUnit 5通过RegisterExtension注册MiniClusterExtensionflink-runtime内部用InternalMiniClusterExtension其他包用MiniClusterExtension或MiniClusterTestEnvironmentJUnit 5类上使用ExtendWith(MiniClusterExtension.class)JUnit 4通过Rule使用MiniClusterWithClientResourceJUnit 4通过ClassRule使用MiniClusterWithClientResource。此外还内置了恰好命中其一的约束例如flink-runtime包必须使用InternalMiniClusterExtension而不能用公共的MiniClusterExtension。除 ITCase 规则外BanJunit4Rules.java 定义了NO_NEW_ADDED_JUNIT4_TEST_RULE已完成 JUnit 5 迁移的模块其org.apache.flink..包中的类不得依赖junit或org.junit包Junit4 is forbidden, please use Junit5 instead。如何在子模块初始化第一个测试代码架构测试若某个 Flink 子模块此前从未编写过测试代码架构测试flink-architecture-tests-test的 README 给出了推荐模板在该子模块的子模块/src/test/resources下创建archunit.properties并以 archunit.properties 与 log4j2-test.properties 为模板仓库内对应文件路径为 flink-architecture-tests-test/src/test/resources/archunit.properties在包org.apache.flink.architecture下开发 ArchUnit 测试类推荐命名为TestCodeArchitectureTest通过ArchTest public static final ArchTests COMMON_TESTS ArchTests.in(TestCodeArchitectureTestBase.class)引入通用测试如需要在包org.apache.flink.architecture.rules下开发模块专属规则。作为参考实现flink-connector-files模块给出了完整范例。其 TestCodeArchitectureTest.java 使用AnalyzeClasses(packages org.apache.flink.connector.file, importOptions {ImportOption.OnlyIncludeTests.class, ExcludeScalaImportOption, ExcludeShadedImportOption})限定只分析该模块的测试类并引入TestCodeArchitectureTestBase中的公共规则同时该模块还维护了自己的 archunit.properties。测试失败了怎么办两类情况的处理流程架构测试可能因代码库变更而失败主 README 将失败原因归结为两种情况并给出了截然不同的处理方式情况一你修复了一个既有违规如果你恰好消除了一个已经存在的违规那么只需要把更新后的违规存储文件violation store file加入你的提交commit测试此时应当通过。这是因为违规存储本身是当前可接受违规清单清单条目减少意味着规则被满足。情况二你的变更引入了新违规新违规应当不惜一切代价避免。处理顺序是首先评估被标记的违规是否属实如果属实优先重构你的代码以从根源上避免违规如果认为代码不应被判为违规即规则本身有缺陷请提交 JIRA issue以便改进规则。当确实需要记录一个新违规时按以下步骤操作打开该子模块下的archunit.properties启用freeze.refreezetrue重新运行测试将配置改回原样撤销对配置的修改新违规此时应已被追加到现有违规存储中。需要特别注意的是默认情况下archunit.properties中freeze.store.default.allowStoreUpdatetrue允许移除既有违规但新增违规会失败——这正是允许消除历史包袱、禁止制造新包袱的设计意图相关注释见 生产模块 archunit.properties。如何编写一条新的架构规则编写规则前应通读 ArchUnit 用户指南但结合 Flink 仓库的实践有四个要点必须遵守1. 既有违规必须冻结FreezingArchRule如果规则对应的违规无法立即全部修复规则必须用FreezingArchRule.freeze()包裹从而将规则登记到记录既有违规的违规存储中。存储文件位于violations目录仓库中为archunit-violations例如 flink-architecture-tests-production/archunit-violations其中 stored.rules 维护了规则描述 → 违规存储 UUID的映射。新生成的存储文件需要加入你的提交。2. 为冻结规则指定固定描述.as调用FreezingArchRule.freeze().as(String newDescription)为规则设置固定描述。这条看似不起眼的建议背后有实际工程意义规则描述会被用作违规存储中规则的关键字key。如果没有固定描述每次规则表述微调都会生成新的违规存储而旧的无用存储必须手工清理固定描述可以把维护成本降到最低。3. 允许创建违规存储为了允许生成新的违规存储文件需要打开子模块中的archunit.properties并启用freeze.store.default.allowStoreCreationtrue该配置在仓库中默认注释见 archunit.properties。4. 规则必须排除非 Java 类ArchUnit 对 Scala 类支持不佳所有规则都应利用GivenJavaClasses中的方法把范围限制在 Java 类上。GivenJavaClasses.java 提供了ArchRuleDefinition#classes()、noClasses()的 Java 类等价版本javaClassesThat()、noJavaClassesThat()其底层通过 SourcePredicates.java 的areJavaClasses()判定类源文件名是否包含.java。如何测试 Scala 类ArchUnit 不支持 Scala。虽然它在字节码层面运作、原则上也能处理由 Scala 编译出的类但实践中 Scala 特有的构造如伴生对象、隐式参数等会产生大量误报。因此所有架构规则都应排除非 Java 类除了规则内部使用GivenJavaClasses/SourcePredicates.areJavaClasses()进行二次过滤导入阶段也应通过ImportOptions.ExcludeScalaImportOption匹配.*/scala/.*尽量不导入 Scala 类——正如 ImportOptions.java 注释所言这是一个尽力而为的尝试并不完美所以规则层的过滤仍然必要。如何新增一个被测试的模块对于生产代码架构测试若要新增一个被测试的模块只需在 flink-architecture-tests-production/pom.xml 中将它添加为测试依赖。当前该模块已将被测的 Flink 模块显式列出包括核心模块flink-coreTable API / SQLflink-table-common、flink-table-api-java、flink-table-api-java-bridge、flink-table-code-splitter、flink-table-runtime、flink-table-planner、flink-sql-client、flink-sql-gateway-api、flink-sql-gatewayConnector 模块flink-connector-base、flink-connector-files、flink-connector-hive、flink-connector-datagen。此外该模块的 surefire 配置将测试收敛到单个 JVM 进程forkCount1并设置大堆内存-Xmx${flink.XmxMax}原因正如 pom 注释所述我们向内存中加载了大量类这是集中执行架构测试特有的资源考量。修改既有规则后如何重新生成违规存储如果修改了一条既有规则而非新增需要重新生成所有违规存储。flink-architecture-tests-test的 README 给出了在项目根目录执行的标准命令rm -rf find . -type d -name archunit-violations mvn test -Dtest*TestCodeArchitectureTest* -DfailIfNoTestsfalse -Darchunit.freeze.refreezetrue -Darchunit.freeze.store.default.allowStoreCreationtrue -Dfast第一步删除所有archunit-violations目录第二步通过-Darchunit.freeze.refreezetrue与-Darchunit.freeze.store.default.allowStoreCreationtrue两个系统属性覆盖archunit.properties中的默认配置让测试以重新冻结模式运行并重建全部违规存储。注意仓库是只读的实际操作时请在你的本地工作副本中执行。小结一条贯穿始终的纪律纵观flink-architecture-tests模块可以看到一套一致的工程纪律架构规则负责划定边界违规存储负责管理历史包袱freeze机制让规则可以逐步收紧而不是一次性阻塞全部开发。无论你是修复了一个既有违规更新存储文件并提交、引入了一个新违规优先重构代码、必要时向规则本身开 JIRA还是编写一条全新的规则冻结 固定描述 允许建存储 排除 Scala核心原则都是相同的——让架构约束可执行、可演进、可追溯这正是 Flink 这样一个大规模开源项目能在数百个模块间长期维持 API 边界与依赖纪律的关键基础设施。赞分享大数据流处理批处理数据工程【免费下载链接】flink项目地址https://gitcode.com/gh_mirrors/fli/flink点击查看免费下载相关推荐TNG/ArchUnit架构检查实战指南典型场景与规则示例TNG/ArchUnit架构检查实战指南典型场景与规则示例 架构检查的必要性 在软件开发中随着项目规模扩大代码架构往往会逐渐腐化。TNG/ArchUnit开发工具软件架构5 分钟跑通大麦抢票ticket-purchase 自动抢票工具实操教程Appium Selenium 双端5 分钟跑通大麦抢票ticket purchase 自动抢票工具实操教程Appium Selenium 双端 手动抢热门演出点立即预订到看见已GUI 自动化RPAp3c规则测试框架ExtendRuleTst与测试用例编写指南p3c规则测试框架ExtendRuleTst与测试用例编写指南 引言为什么需要专业的规则测试框架 在Java开发领域代码规范检查工具如PMDProgr代码质量静态分析Lint开发工具上一篇PUBG雷达系统完整指南5分钟跑通一个开源战场地图透视下一篇CoolProp 热力学物性计算一站式开源物性数据库5分钟告别查表创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考