乐闻世界logo
搜索文章和话题

服务端面试题手册

RPC 框架中的负载均衡算法有哪些?它们各有什么优缺点和适用场景?

负载均衡是 RPC 框架中的核心组件,负责将请求分发到多个服务实例,提高系统性能和可用性:常见负载均衡算法:1. 随机算法(Random)原理:随机选择一个服务实例加权随机:根据实例权重设置选择概率优点:实现简单,请求分布均匀缺点:不考虑实例当前负载适用场景:实例性能相近的场景实现示例: public class RandomLoadBalancer { private List<ServiceInstance> instances; private Random random = new Random(); public ServiceInstance select() { int index = random.nextInt(instances.size()); return instances.get(index); } }2. 轮询算法(Round Robin)原理:按顺序依次选择服务实例加权轮询:根据权重分配请求比例优点:请求分布均匀,实现简单缺点:不考虑实例响应时间差异适用场景:实例性能相近的场景实现示例: public class RoundRobinLoadBalancer { private List<ServiceInstance> instances; private AtomicInteger index = new AtomicInteger(0); public ServiceInstance select() { int idx = index.getAndIncrement() % instances.size(); return instances.get(idx); } }3. 最少连接算法(Least Connections)原理:选择当前连接数最少的实例优点:考虑实例当前负载,动态分配缺点:需要维护连接数统计适用场景:请求处理时间差异较大的场景实现示例: public class LeastConnectionsLoadBalancer { private List<ServiceInstance> instances; public ServiceInstance select() { return instances.stream() .min(Comparator.comparingInt(ServiceInstance::getActiveConnections)) .orElse(null); } }4. 一致性哈希算法(Consistent Hash)原理:将请求和服务实例映射到哈希环上优点:相同请求总是路由到同一实例实例增减时影响范围小支持会话保持缺点:实现复杂,可能分布不均适用场景:需要会话保持或缓存一致的场景实现示例: public class ConsistentHashLoadBalancer { private TreeMap<Integer, ServiceInstance> ring = new TreeMap<>(); public void addInstance(ServiceInstance instance) { for (int i = 0; i < 100; i++) { int hash = hash(instance.getAddress() + "#" + i); ring.put(hash, instance); } } public ServiceInstance select(String key) { int hash = hash(key); Map.Entry<Integer, ServiceInstance> entry = ring.ceilingEntry(hash); if (entry == null) { entry = ring.firstEntry(); } return entry.getValue(); } }5. 最小响应时间算法(Least Response Time)原理:选择平均响应时间最短的实例优点:动态适应实例性能变化缺点:需要维护响应时间统计适用场景:实例性能差异较大的场景6. IP 哈希算法(IP Hash)原理:根据客户端 IP 进行哈希优点:相同客户端总是访问同一实例缺点:可能导致负载不均适用场景:需要会话保持的场景7. 加权算法(Weighted)原理:根据实例权重分配请求加权随机:按权重随机选择加权轮询:按权重比例轮询优点:可以根据实例性能分配不同权重适用场景:实例性能差异较大的场景Dubbo 负载均衡配置:<dubbo:reference interface="com.example.UserService" loadbalance="random"/><!-- 可选值:random, roundrobin, leastactive, consistenthash -->gRPC 负载均衡:客户端负载均衡:客户端维护服务列表并选择实例服务端负载均衡:通过代理(如 Envoy)进行负载均衡支持策略:轮询、随机、最少连接等Nginx 负载均衡配置:upstream backend { # 轮询 server 192.168.1.1:8080; server 192.168.1.2:8080; # 加权轮询 server 192.168.1.1:8080 weight=3; server 192.168.1.2:8080 weight=1; # IP 哈希 ip_hash; # 最少连接 least_conn;}选择建议:实例性能相近:随机、轮询需要会话保持:一致性哈希、IP 哈希实例性能差异大:加权算法、最少连接、最小响应时间缓存一致性要求高:一致性哈希简单场景:随机、轮询注意事项:结合健康检查剔除故障实例定期更新服务实例列表监控负载均衡效果根据实际情况调整算法参数
阅读 0·2月22日 14:03

什么是分布式链路追踪?主流的链路追踪工具有哪些?它们如何工作?

链路追踪是分布式系统中快速定位问题和分析性能的重要工具,能够追踪请求在多个服务之间的调用链路:核心概念:1. Trace(追踪)一次完整的请求调用链路从客户端发起请求到最终响应的整个过程包含多个 Span2. Span(跨度)一次具体的调用操作包含开始时间、结束时间、操作名称等Span 之间通过父子关系形成调用树3. Span ID唯一标识一个 Span用于构建调用链路4. Trace ID唯一标识一次完整的追踪所有相关 Span 共享同一个 Trace ID5. Parent Span ID标识当前 Span 的父 Span用于构建调用层次关系6. Annotation(注解)记录关键事件的时间点如 CS(Client Send)、SR(Server Receive)、SS(Server Send)、CR(Client Receive)7. Baggage(行李)在调用链路中传递的键值对数据用于在服务间传递上下文信息主流链路追踪工具:1. Zipkin特点:Twitter 开源,基于 Google Dapper 论文优势:成熟稳定,社区活跃支持多种语言可视化界面友好劣势:存储性能一般功能相对简单适用场景:中小型分布式系统2. Jaeger特点:Uber 开源,兼容 Zipkin API优势:性能优秀,支持高并发支持多种存储后端功能更完善劣势:相对较新适用场景:高性能要求的分布式系统3. SkyWalking特点:国产开源,专注于 APM优势:功能全面(链路追踪、性能监控、日志分析)对 Java 支持好中文文档完善劣势:其他语言支持相对较弱适用场景:Java 为主的微服务架构4. Pinpoint特点:Naver 开源,专注于 Java优势:无代码侵入详细的性能分析劣势:只支持 Java资源占用较高适用场景:Java 单一语言环境5. OpenTelemetry特点:CNCF 托管,统一的可观测性标准优势:统一的 API 和 SDK多语言支持与多种后端兼容劣势:相对较新,生态还在发展适用场景:需要统一可观测性标准的项目实现原理:1. 上下文传递在服务调用时传递 Trace ID 和 Span ID通过 HTTP Header、RPC 元数据等方式传递示例: // gRPC 传递上下文 Context ctx = Context.current().withValue(TRACE_ID_KEY, traceId); stub.withDeadlineAfter(timeout, TimeUnit.MILLISECONDS) .sayHello(request, ctx);2. 拦截器/过滤器在请求入口和出口拦截记录调用开始和结束时间示例: @Component public class TraceInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String traceId = generateTraceId(); MDC.put("traceId", traceId); return true; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { MDC.remove("traceId"); } }3. 采样策略固定采样率:按固定比例采样动态采样:根据请求特征动态调整错误优先:优先采样错误请求4. 数据上报异步上报,避免影响业务性能支持批量上报,减少网络开销支持多种传输协议(HTTP、gRPC、Kafka)Spring Cloud Sleath 集成示例:@SpringBootApplication@EnableZipkinServerpublic class ZipkinServerApplication { public static void main(String[] args) { SpringApplication.run(ZipkinServerApplication.class, args); }}// 客户端配置spring: zipkin: base-url: http://localhost:9411 sleuth: sampler: probability: 0.1 # 采样率 10%使用场景:1. 性能分析识别慢查询和慢服务分析调用链路中的性能瓶颈优化系统性能2. 故障排查快速定位问题服务追踪错误传播路径分析故障根因3. 依赖分析了解服务间依赖关系识别不合理的调用优化服务架构4. 容量规划分析系统负载分布预测资源需求优化资源配置最佳实践:合理设置采样率,平衡性能和可观测性结合日志和监控,形成完整的可观测性体系定期分析链路数据,优化系统性能使用统一的 Trace ID,方便跨系统追踪注意敏感信息保护,避免在链路中传递敏感数据
阅读 0·2月22日 14:03

Gradle 和 Maven 有什么区别?如何选择?

Gradle 与 Maven 是两个最流行的 Java 构建工具,它们各有优缺点。以下是两者的详细对比:历史背景Maven发布时间:2004年开发者:Apache Software Foundation设计理念:约定优于配置(Convention over Configuration)配置文件:XML(pom.xml)Gradle发布时间:2008年开发者:Gradle Inc.设计理念:结合 Ant 的灵活性和 Maven 的约定配置文件:Groovy/Kotlin DSL(build.gradle)核心差异对比1. 配置方式Maven (XML)<!-- pom.xml --><project> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>my-app</artifactId> <version>1.0.0</version> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>3.0.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.11.0</version> <configuration> <source>17</source> <target>17</target> </configuration> </plugin> </plugins> </build></project>Gradle (Groovy DSL)// build.gradleplugins { id 'java' id 'org.springframework.boot' version '3.0.0'}group = 'com.example'version = '1.0.0'java { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17}dependencies { implementation 'org.springframework.boot:spring-boot-starter-web:3.0.0'}2. 构建性能Maven首次构建:较慢,需要下载依赖增量构建:基本支持,但不如 Gradle 高效并行构建:支持,但配置复杂构建缓存:有限支持Gradle首次构建:较快,增量构建优化增量构建:非常高效,只处理变更的文件并行构建:原生支持,配置简单构建缓存:强大的本地和远程缓存支持3. 依赖管理Maven<dependencies> <!-- 固定版本 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>3.0.0</version> </dependency> <!-- 使用属性 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> <version>${spring.boot.version}</version> </dependency> <!-- 依赖管理(BOM) --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>3.0.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement></dependencies>Gradledependencies { // 固定版本 implementation 'org.springframework.boot:spring-boot-starter-web:3.0.0' // 使用版本目录(推荐) implementation libs.spring.boot.web // 使用 BOM implementation platform('org.springframework.boot:spring-boot-dependencies:3.0.0') implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-data-jpa'}4. 生命周期Maven# Maven 生命周期cleanvalidatecompiletestpackageverifyinstalldeploy# 执行命令mvn clean installmvn clean packageGradle# Gradle 任务(无固定生命周期)cleanbuildtestjarinstallpublish# 执行命令./gradlew clean build./gradlew clean jar5. 插件系统Maven<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.11.0</version> <configuration> <source>17</source> <target>17</target> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>3.0.0</version> </plugin> </plugins></build>Gradleplugins { // 二进制插件 id 'java' id 'org.springframework.boot' version '3.0.0' // 脚本插件 apply from: 'gradle/checkstyle.gradle'}// 插件配置java { sourceCompatibility = JavaVersion.VERSION_17}springBoot { mainClass = 'com.example.Application'}优缺点对比Maven 优点标准化:严格遵循约定,项目结构统一稳定性:成熟稳定,生态系统完善学习曲线:相对简单,易于上手IDE 支持:所有主流 IDE 都有良好支持文档:文档丰富,社区活跃Maven 缺点灵活性差:XML 配置冗长,难以自定义构建速度:大型项目构建速度较慢依赖管理:版本冲突处理不够灵活扩展性:插件开发相对复杂Gradle 优点灵活性高:Groovy/Kotlin DSL 表达力强构建速度快:增量构建和缓存机制优秀依赖管理:灵活的依赖解析和版本管理多语言支持:支持 Java、Kotlin、Groovy、Scala 等可扩展性:插件开发简单,易于扩展Gradle 缺点学习曲线:需要学习 Groovy/Kotlin DSL复杂性:灵活性可能导致配置复杂标准化:不如 Maven 严格,可能导致项目结构不一致文档:虽然文档丰富,但相对分散适用场景适合使用 Maven 的场景传统企业项目:需要严格遵循标准简单项目:不需要复杂的构建逻辑团队熟悉度:团队已经熟悉 MavenCI/CD 集成:需要与现有 Maven 生态系统集成依赖管理简单:不需要复杂的依赖处理适合使用 Gradle 的场景大型项目:需要高效的增量构建复杂构建逻辑:需要自定义构建流程多模块项目:需要灵活的模块间依赖Android 开发:Android Studio 默认使用 Gradle性能要求高:需要快速构建和缓存迁移指南从 Maven 迁移到 Gradle# 使用 Gradle 的 Maven 插件生成初始配置./gradlew init --type pom# 或使用在线工具# https://start.spring.io/ 可以生成 Gradle 项目从 Gradle 迁移到 Maven# 使用 Maven 的 Gradle 插件mvn com.github.ekryd.sortpom:sortpom-maven-plugin:3.3.0:sort性能对比数据构建时间对比(基于相同项目)| 操作 | Maven | Gradle | 差异 ||------|-------|--------|------|| 首次构建 | 120s | 90s | Gradle 快 25% || 增量构建(无变更) | 45s | 5s | Gradle 快 89% || 增量构建(少量变更) | 60s | 15s | Gradle 快 75% || 清理构建 | 30s | 20s | Gradle 快 33% |最佳实践建议选择 Maven 如果项目结构简单,不需要复杂构建逻辑团队已经熟悉 Maven需要与现有 Maven 生态系统集成重视标准化和一致性选择 Gradle 如果项目规模大,需要高性能构建需要自定义构建逻辑团队愿意学习新技术需要支持多种语言或平台结论Maven 和 Gradle 都是优秀的构建工具,选择哪一个取决于项目需求和团队情况:Maven:适合传统、标准化、简单的项目Gradle:适合现代、高性能、复杂的项目两者可以共存,Gradle 可以导入 Maven 项目,Maven 也可以使用 Gradle 插件。选择时应该基于实际需求,而不是盲目追求新技术。
阅读 0·2月21日 19:11

什么是 Gradle Wrapper?如何生成和使用它?

Gradle Wrapper 是 Gradle 的一个重要特性,它允许项目使用特定版本的 Gradle 进行构建,而无需在开发者的机器上预先安装 Gradle。以下是 Gradle Wrapper 的详细说明:Gradle Wrapper 简介Gradle Wrapper 是一个脚本和一组 JAR 文件,用于下载和运行特定版本的 Gradle。它确保所有开发者和 CI/CD 系统使用相同版本的 Gradle 进行构建。Wrapper 文件结构project/├── gradle/│ └── wrapper/│ ├── gradle-wrapper.jar│ └── gradle-wrapper.properties├── gradlew├── gradlew.bat└── build.gradle文件说明gradlew:Unix/Linux/macOS 上的可执行脚本gradlew.bat:Windows 上的批处理脚本gradle/wrapper/gradle-wrapper.jar:Wrapper JAR 文件gradle/wrapper/gradle-wrapper.properties:Wrapper 配置文件生成 Wrapper使用 Gradle 命令生成# 生成 Wrapper(使用当前 Gradle 版本)gradle wrapper# 指定 Gradle 版本gradle wrapper --gradle-version 8.0# 使用发布版本gradle wrapper --gradle-distribution-url https://services.gradle.org/distributions/gradle-8.0-bin.zip在 build.gradle 中配置// build.gradlewrapper { gradleVersion = '8.0' distributionType = Wrapper.DistributionType.BIN distributionPath = wrapperPath archivePath = wrapperPath}Wrapper 配置gradle-wrapper.properties# Gradle 分发下载 URLdistributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip# 分发基础 URL(可选)distributionBase=GRADLE_USER_HOME# 分发路径(可选)distributionPath=wrapper/dists# Zip 缓存基础 URL(可选)zipStoreBase=GRADLE_USER_HOME# Zip 缓存路径(可选)zipStorePath=wrapper/dists分发类型Gradle Wrapper 提供三种分发类型:bin:仅包含二进制文件,体积小,下载快 distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zipall:包含二进制文件和源代码,体积大,适合需要查看源代码的情况 distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.ziponly:仅包含源代码(不常用) distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-src.zip使用 Wrapper基本使用# 使用 Wrapper 执行 Gradle 任务./gradlew build# Windows 上使用gradlew.bat build# 查看可用任务./gradlew tasks# 清理项目./gradlew clean指定 JVM 选项# 设置 JVM 内存./gradlew build -Dorg.gradle.jvmargs="-Xmx2048m -XX:MaxMetaspaceSize=512m"# 使用特定的 Java 版本JAVA_HOME=/path/to/java17 ./gradlew build传递项目属性# 传递项目属性./gradlew build -Pprofile=production# 传递系统属性./gradlew build -Dspring.profiles.active=production自定义 Wrapper自定义分发位置// build.gradlewrapper { gradleVersion = '8.0' distributionType = Wrapper.DistributionType.BIN // 使用自定义仓库 distributionUrl = 'https://my-company.com/gradle/gradle-8.0-bin.zip'}使用本地 Gradle 分发// gradle-wrapper.propertiesdistributionUrl=file\:///path/to/local/gradle-8.0-bin.zip配置网络代理# 设置代理./gradlew build -Dhttp.proxyHost=proxy.example.com -Dhttp.proxyPort=8080# 在 gradle.properties 中配置# gradle.propertiessystemProp.http.proxyHost=proxy.example.comsystemProp.http.proxyPort=8080systemProp.https.proxyHost=proxy.example.comsystemProp.https.proxyPort=8080版本管理更新 Wrapper 版本# 更新到最新版本./gradlew wrapper --gradle-version=8.1# 更新到特定版本./gradlew wrapper --gradle-version=8.0.2# 更新到发行候选版本./gradlew wrapper --gradle-version=8.2-rc-1检查当前版本# 查看 Wrapper 使用的 Gradle 版本./gradlew --version# 查看 Wrapper 配置cat gradle/wrapper/gradle-wrapper.properties最佳实践1. 提交 Wrapper 文件到版本控制# 提交以下文件到 Gitgit add gradlew gradlew.batgit add gradle/wrapper/gradle-wrapper.jargit add gradle/wrapper/gradle-wrapper.properties# 不要提交下载的 Gradle 分发echo "gradle/wrapper/dists/" >> .gitignore2. 使用固定版本// build.gradlewrapper { gradleVersion = '8.0' // 使用固定版本 distributionType = Wrapper.DistributionType.BIN}3. 在 CI/CD 中使用 Wrapper# GitHub Actions 示例- name: Build with Gradle run: ./gradlew build# Jenkins Pipeline 示例sh './gradlew build'# GitLab CI 示例script: - ./gradlew build4. 优化下载速度// 使用国内镜像wrapper { gradleVersion = '8.0' distributionUrl = 'https://mirrors.cloud.tencent.com/gradle/gradle-8.0-bin.zip'}故障排除1. Wrapper 脚本没有执行权限# 添加执行权限chmod +x gradlew2. 下载失败# 手动下载 Gradle 分发wget https://services.gradle.org/distributions/gradle-8.0-bin.zip# 放到正确的目录mkdir -p ~/.gradle/wrapper/dists/gradle-8.0-bincp gradle-8.0-bin.zip ~/.gradle/wrapper/dists/gradle-8.0-bin/3. 版本冲突# 清理本地缓存rm -rf ~/.gradle/wrapper/dists/# 重新下载./gradlew build4. 网络问题# 使用离线模式./gradlew build --offline# 配置代理./gradlew build -Dhttp.proxyHost=proxy.example.com -Dhttp.proxyPort=8080安全考虑1. 验证 Wrapper JAR# 验证 Wrapper JAR 的校验和shasum gradle/wrapper/gradle-wrapper.jar2. 使用 HTTPS# gradle-wrapper.propertiesdistributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip3. 限制网络访问// 在企业环境中,可以限制 Wrapper 只能从内部仓库下载wrapper { distributionUrl = 'https://internal-repo.example.com/gradle/gradle-8.0-bin.zip'}高级用法1. 多项目中的 Wrapper# 在根项目生成 Wrapper./gradlew wrapper# 所有子项目都可以使用同一个 Wrapper./gradlew :module1:build./gradlew :module2:build2. 自定义 Wrapper 脚本# 修改 gradlew 脚本以添加自定义逻辑# 例如:设置环境变量、检查 Java 版本等3. 使用 Gradle 版本目录// gradle/libs.versions.toml[versions]gradle = "8.0"// 在 build.gradle 中使用wrapper { gradleVersion = libs.versions.gradle.get()}
阅读 0·2月21日 18:10

Gradle 插件有哪些类型?如何创建和使用自定义插件?

Gradle 插件是扩展 Gradle 功能的主要机制,通过插件可以添加新的任务、配置和约定。以下是 Gradle 插件的详细说明:插件类型1. 二进制插件(Binary Plugins)二进制插件是实现 Plugin 接口的类,通常打包为 JAR 文件。// 应用二进制插件plugins { id 'java' id 'org.springframework.boot' version '3.0.0' id 'com.android.application' version '8.0.0'}2. 脚本插件(Script Plugins)脚本插件是包含构建逻辑的 Groovy 或 Kotlin 脚本文件。// 应用脚本插件apply from: 'gradle/checkstyle.gradle'apply from: file('gradle/codenarc.gradle')apply from: new File(rootDir, 'gradle/common.gradle')应用插件的方式使用 plugins DSL(推荐)plugins { // Gradle 核心插件(无需版本号) id 'java' id 'application' id 'war' // 社区插件(需要版本号) id 'org.springframework.boot' version '3.0.0' id 'com.github.spotbugs' version '5.0.14' id 'io.spring.dependency-management' version '1.1.0' // 使用插件 ID id 'com.android.application' version '8.0.0' apply false}使用 apply 方法(旧方式)// 应用核心插件apply plugin: 'java'apply plugin: 'application'// 应用社区插件apply plugin: 'org.springframework.boot'apply plugin: 'com.github.spotbugs'// 使用类路径buildscript { repositories { mavenCentral() } dependencies { classpath 'org.springframework.boot:spring-boot-gradle-plugin:3.0.0' }}apply plugin: 'org.springframework.boot'常用插件Java 插件plugins { id 'java'}// Java 插件提供的任务// - compileJava: 编译 Java 源代码// - compileTestJava: 编译测试代码// - test: 运行测试// - jar: 打包 JAR 文件// - javadoc: 生成 JavadocApplication 插件plugins { id 'application'}application { mainClass = 'com.example.Main' applicationDefaultJvmArgs = ['-Xmx1024m']}// 提供的任务// - run: 运行应用程序// - distZip: 创建 ZIP 分发包// - distTar: 创建 TAR 分发包// - installDist: 安装应用程序War 插件plugins { id 'war'}war { archiveFileName = 'myapp.war' webAppDirName = 'src/main/webapp'}// 提供的任务// - war: 创建 WAR 文件Spring Boot 插件plugins { id 'org.springframework.boot' version '3.0.0'}springBoot { mainClass = 'com.example.Application'}// 提供的任务// - bootRun: 运行 Spring Boot 应用// - bootJar: 创建可执行 JAR// - bootWar: 创建可执行 WAR自定义插件创建插件项目结构custom-plugin/├── build.gradle├── settings.gradle└── src/ └── main/ ├── groovy/ │ └── com/ │ └── example/ │ └── CustomPlugin.groovy └── resources/ └── META-INF/ └── gradle-plugins/ └── com.example.custom-plugin.properties实现插件类// CustomPlugin.groovypackage com.exampleimport org.gradle.api.Pluginimport org.gradle.api.Projectclass CustomPlugin implements Plugin<Project> { @Override void apply(Project project) { // 创建扩展 def extension = project.extensions.create('customConfig', CustomExtension) // 创建任务 def helloTask = project.tasks.register('hello') { group = 'Custom' description = 'Says hello' doLast { println "Hello, ${extension.name}!" } } // 配置项目 project.afterEvaluate { println "Project ${project.name} configured with custom plugin" } }}class CustomExtension { String name = 'World' int timeout = 30 void timeout(int timeout) { this.timeout = timeout }}定义插件 ID# com.example.custom-plugin.propertiesimplementation-class=com.example.CustomPlugin发布插件// build.gradleplugins { id 'java-gradle-plugin' id 'maven-publish'}gradlePlugin { plugins { customPlugin { id = 'com.example.custom-plugin' implementationClass = 'com.example.CustomPlugin' } }}publishing { repositories { maven { url = uri('../repo') } }}使用自定义插件// 在 settings.gradle 中添加插件仓库pluginManagement { repositories { maven { url = uri('../repo') } gradlePluginPortal() }}// 在 build.gradle 中应用插件plugins { id 'com.example.custom-plugin'}customConfig { name = 'Gradle' timeout 60}插件配置插件块配置plugins { id 'java' id 'checkstyle'}checkstyle { toolVersion = '10.3' configFile = file('config/checkstyle/checkstyle.xml' ignoreFailures = false showViolations = true}扩展配置// 使用插件提供的扩展java { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 withSourcesJar() withJavadocJar()}test { useJUnitPlatform() testLogging { events 'passed', 'skipped', 'failed' }}插件依赖管理插件版本管理// 使用版本目录[plugins]spring-boot = { id = "org.springframework.boot", version = "3.0.0" }dependency-management = { id = "io.spring.dependency-management", version = "1.1.0" }// 在 build.gradle 中使用plugins { id libs.plugins.spring.boot.get().pluginId id libs.plugins.dependency.management.get().pluginId}插件依赖解析策略settings.gradle { pluginManagement { repositories { gradlePluginPortal() mavenCentral() google() } resolutionStrategy { eachPlugin { if (requested.id.id == 'com.example.plugin') { useModule('com.example:plugin:1.0.0') } } } }}最佳实践使用 plugins DSL:优先使用 plugins DSL 而不是 apply 方法明确插件版本:为社区插件指定明确的版本号合理使用插件:只应用必要的插件,避免功能冗余自定义插件:对于重复的构建逻辑,考虑创建自定义插件插件配置:在应用插件后立即配置插件扩展插件依赖:注意插件之间的依赖关系,避免冲突插件更新:定期检查和更新插件版本以获取新功能和修复
阅读 0·2月21日 18:10

Gradle 如何管理依赖?有哪些依赖配置类型?

Gradle 的依赖管理系统是其核心功能之一,提供了强大而灵活的依赖管理能力。以下是 Gradle 依赖管理的详细说明:依赖声明基本语法dependencies { // 格式: group:name:version implementation 'org.springframework.boot:spring-boot-starter-web:3.0.0' // 使用 map 语法 implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '3.0.0'}依赖配置类型编译时依赖dependencies { // implementation:编译和运行时需要,但不暴露给依赖此项目的消费者 implementation 'org.apache.commons:commons-lang3:3.12.0' // compileOnly:编译时需要,但运行时不需要(如 Lombok) compileOnly 'org.projectlombok:lombok:1.18.24' // annotationProcessor:注解处理器 annotationProcessor 'org.projectlombok:lombok:1.18.24'}运行时依赖dependencies { // runtimeOnly:运行时需要,但编译时不需要 runtimeOnly 'mysql:mysql-connector-java:8.0.28'}测试依赖dependencies { // testImplementation:测试编译和运行时需要 testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' // testCompileOnly:测试编译时需要 testCompileOnly 'org.projectlombok:lombok:1.18.24' // testRuntimeOnly:测试运行时需要 testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0'}传递依赖dependencies { // api:编译和运行时需要,且暴露给消费者 api 'org.apache.commons:commons-math3:3.6.1' // compileOnlyApi:编译时需要,运行时不需要,但暴露给消费者 compileOnlyApi 'org.apache.commons:commons-text:1.10.0'}仓库配置常用仓库repositories { // Maven 中央仓库 mavenCentral() // Google 仓库(Android 开发常用) google() // Gradle 插件仓库 gradlePluginPortal() // 自定义 Maven 仓库 maven { url 'https://maven.aliyun.com/repository/public' name 'Aliyun Public' } // 本地 Maven 仓库 mavenLocal() // Ivy 仓库 ivy { url 'https://example.com/ivy-repo' layout 'pattern' }}仓库认证repositories { maven { url 'https://example.com/private-repo' credentials { username = 'admin' password = 'password' } }}依赖版本管理动态版本dependencies { // 最新版本(不推荐生产环境使用) implementation 'org.springframework.boot:spring-boot-starter-web:+' // 最新发布版本 implementation 'org.springframework.boot:spring-boot-starter-web:latest.release' // 最新集成版本 implementation 'org.springframework.boot:spring-boot-starter-web:latest.integration' // 版本范围 implementation 'org.apache.commons:commons-lang3:[3.10,4.0)' implementation 'org.apache.commons:commons-lang3:3.+'}使用版本目录(推荐)// 在 gradle/libs.versions.toml 中定义[versions]spring-boot = "3.0.0"commons-lang3 = "3.12.0"[libraries]spring-boot-web = { module = "org.springframework.boot:spring-boot-starter-web", version.ref = "spring-boot" }commons-lang3 = { module = "org.apache.commons:commons-lang3", version.ref = "commons-lang3" }// 在 build.gradle 中使用dependencies { implementation libs.spring.boot.web implementation libs.commons.lang3}使用 ext 属性ext { springBootVersion = '3.0.0' commonsLang3Version = '3.12.0'}dependencies { implementation "org.springframework.boot:spring-boot-starter-web:${springBootVersion}" implementation "org.apache.commons:commons-lang3:${commonsLang3Version}"}依赖排除排除特定依赖dependencies { implementation('org.springframework.boot:spring-boot-starter-web:3.0.0') { exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat' }}全局排除configurations { all { exclude group: 'org.slf4j', module: 'slf4j-log4j12' }}依赖解析策略强制指定版本configurations.all { resolutionStrategy { force 'org.apache.commons:commons-lang3:3.12.0' }}冲突解决configurations.all { resolutionStrategy { // 失败策略 failOnVersionConflict() // 使用最新版本 failOnVersionConflict() // 使用特定版本 force 'org.apache.commons:commons-lang3:3.12.0' }}传递依赖管理查看依赖树# 查看所有依赖./gradlew dependencies# 查看特定配置的依赖./gradlew dependencies --configuration implementation# 查看特定任务的依赖./gradlew :app:dependencies停止传递依赖dependencies { implementation('com.example:library:1.0.0') { transitive = false }}BOM(Bill of Materials)管理导入 BOMdependencies { // 导入 Spring Boot BOM implementation platform('org.springframework.boot:spring-boot-dependencies:3.0.0') // 导入后无需指定版本 implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-data-jpa'}自定义 BOM// 创建 platform 模块dependencies { api platform('com.example:bom:1.0.0')}最佳实践使用版本目录:统一管理依赖版本,避免版本冲突避免使用动态版本:在生产环境中使用固定版本合理选择依赖配置:根据实际需求选择 implementation、api 等定期更新依赖:使用 ./gradlew dependencyUpdates 检查更新使用 BOM:对于多个相关依赖,使用 BOM 统一管理版本排除不必要的依赖:减少依赖冲突和安全风险使用依赖分析工具:如 Gradle 的 dependencyInsight 任务
阅读 0·2月21日 18:10

Gradle 的增量构建是如何工作的?如何优化构建性能?

Gradle 的增量构建是其性能优化的核心特性之一,它通过只处理变更的文件来显著提高构建速度。以下是 Gradle 增量构建的详细说明:增量构建原理增量构建基于任务的输入和输出声明,Gradle 会跟踪这些文件的变化,只有当输入文件发生变化时才重新执行任务。工作流程首次构建:执行任务并记录输入和输出的哈希值后续构建:比较当前输入的哈希值与记录的哈希值决策:如果输入未变化且输出存在,跳过任务执行(UP-TO-DATE)如果输入变化或输出不存在,重新执行任务输入和输出声明基本输入输出tasks.register('processFiles') { // 输入文件 inputs.file('input.txt') inputs.dir('src/main/resources') // 输出目录 outputs.dir('build/processed') doLast { copy { from 'src/main/resources' into 'build/processed' } }}多个输入输出tasks.register('combineFiles') { inputs.files('file1.txt', 'file2.txt') inputs.files(fileTree('src') { include '**/*.java' }) outputs.file('build/combined.txt') outputs.dirs('build/classes', 'build/resources') doLast { // 处理文件 }}属性输入tasks.register('generateCode') { // 属性输入 inputs.property('version', project.version) inputs.property('target', 'production') outputs.dir('build/generated') doLast { // 根据属性生成代码 }}内置任务的增量构建Java 编译任务// Java 插件提供的编译任务自动支持增量构建tasks.withType(JavaCompile).configureEach { options.incremental = true // 启用增量编译}测试任务test { // 测试任务自动支持增量构建 useJUnitPlatform() // 配置测试输入 testLogging { events 'passed', 'skipped', 'failed' }}自定义增量任务使用增量任务 APIabstract class IncrementalTask extends DefaultTask { @Incremental @InputDirectory abstract DirectoryProperty getInputDirectory() @OutputDirectory abstract DirectoryProperty getOutputDirectory() @TaskAction void execute(InputChanges inputChanges) { if (inputChanges.incremental) { inputChanges.getFileChanges(inputDirectory).each { change -> if (change.fileType == FileType.FILE) { switch (change.changeType) { case ChangeType.ADDED: println "新增文件: ${change.file}" processFile(change.file) break case ChangeType.MODIFIED: println "修改文件: ${change.file}" processFile(change.file) break case ChangeType.REMOVED: println "删除文件: ${change.file}" removeOutput(change.file) break } } } } else { // 非增量构建,处理所有文件 println "执行全量构建" inputDirectory.get().asFile.eachFileRecurse { file -> if (file.isFile()) { processFile(file) } } } } void processFile(File file) { // 处理单个文件 } void removeOutput(File file) { // 删除对应的输出文件 }}// 注册任务tasks.register('incrementalProcess', IncrementalTask) { inputDirectory.set(file('src/main/resources')) outputDirectory.set(file('build/processed'))}增量构建配置启用增量构建// gradle.propertiesorg.gradle.caching=trueorg.gradle.parallel=true任务级别配置tasks.withType(JavaCompile).configureEach { options.incremental = true}tasks.withType(Test).configureEach { // 测试任务默认支持增量构建}构建缓存本地构建缓存// gradle.propertiesorg.gradle.caching=true远程构建缓存buildCache { local { enabled = true } remote(HttpBuildCache) { url = 'https://cache.example.com/cache/' enabled = true push = true // 允许推送缓存 credentials { username = 'user' password = 'password' } }}缓存任务输出tasks.register('expensiveTask') { outputs.cacheIf { true } // 启用缓存 doLast { // 执行耗时操作 }}增量构建最佳实践1. 明确声明输入输出tasks.register('customTask') { // 明确声明所有输入 inputs.files('config.xml', 'properties.json') inputs.property('env', System.getenv('ENV')) // 明确声明所有输出 outputs.dir('build/output') doLast { // 任务逻辑 }}2. 使用文件树tasks.register('processResources') { inputs.dir('src/main/resources').withPropertyName('resources') outputs.dir('build/resources').withPropertyName('output') doLast { copy { from inputs.dir into outputs.dir } }}3. 避免不必要的输入tasks.register('compileJava') { // 只包含必要的输入文件 inputs.files(fileTree('src/main/java') { include '**/*.java' exclude '**/generated/**' }) outputs.dir('build/classes')}4. 使用属性输入tasks.register('generateConfig') { inputs.property('database.url', project.findProperty('db.url')) inputs.property('database.username', project.findProperty('db.username')) outputs.file('build/config/application.properties') doLast { // 生成配置文件 }}调试增量构建查看任务状态# 查看任务是否为 UP-TO-DATE./gradlew build --info# 查看详细的增量构建信息./gradlew build --debug强制重新执行任务# 强制重新执行特定任务./gradlew clean build# 强制重新执行任务(不清理输出)./gradlew build --rerun-tasks# 强制重新执行特定任务./gradlew :app:compileJava --rerun-tasks分析构建性能# 生成构建报告./gradlew build --scan# 查看任务执行时间./gradlew build --profile常见问题和解决方案1. 任务总是重新执行问题:任务没有正确声明输入输出解决方案:tasks.register('problemTask') { // 确保所有输入都被声明 inputs.files('input.txt') inputs.property('version', project.version) // 确保所有输出都被声明 outputs.dir('build/output')}2. 输出文件被外部修改问题:输出文件被其他进程修改,导致缓存失效解决方案:tasks.register('sensitiveTask') { outputs.upToDateWhen { // 自定义 UP-TO-DATE 检查逻辑 true }}3. 增量构建不生效问题:任务不支持增量构建解决方案:// 使用 @Incremental 注解abstract class MyIncrementalTask extends DefaultTask { @Incremental @InputDirectory abstract DirectoryProperty getInputDir() @OutputDirectory abstract DirectoryProperty getOutputDir()}性能优化建议启用构建缓存:显著提高重复构建的速度使用并行构建:利用多核 CPU 加速构建优化任务依赖:减少不必要的任务执行使用增量编译:Java 编译任务默认支持避免在配置阶段执行耗时操作:将逻辑移到执行阶段使用配置缓存:减少配置时间合理使用增量任务 API:对于文件处理任务特别有效
阅读 0·2月21日 18:10

Gradle 的生命周期包括哪些阶段?每个阶段的作用是什么?

Gradle 的生命周期分为三个主要阶段:1. 初始化阶段(Initialization Phase)作用:确定哪些项目将参与构建,并为每个项目创建 Project 实例执行内容:读取 settings.gradle 或 settings.gradle.kts 文件根据 include 语句确定项目结构为每个包含的项目创建 Project 实例执行 init.gradle(如果存在)示例: // settings.gradle rootProject.name = 'my-project' include 'app', 'library', 'common'2. 配置阶段(Configuration Phase)作用:执行所有项目的构建脚本,构建任务依赖图执行内容:执行每个项目的 build.gradle 或 build.gradle.kts配置项目属性、插件、依赖创建和配置所有任务建立任务之间的依赖关系特点:即使只执行一个任务,也会配置所有项目可以通过 gradle -m 或 --dry-run 查看将要执行的任务优化技巧: // 使用 onlyIf 或 enabled 来跳过不必要的配置 tasks.register('myTask') { onlyIf { project.hasProperty('enableMyTask') } }3. 执行阶段(Execution Phase)作用:按照任务依赖图执行实际的任务执行内容:只执行在命令行中指定的任务及其依赖任务执行任务的 action 逻辑支持增量构建,只处理变更的文件特点:任务的执行顺序由依赖关系决定支持并行执行(通过 --parallel 参数)支持持续构建(通过 --continuous 参数)生命周期钩子Gradle 提供了多个生命周期钩子来在特定阶段执行自定义逻辑:// 初始化阶段gradle.projectsLoaded { println "所有项目已加载"}// 配置阶段gradle.beforeProject { project -> println "配置项目: ${project.name}"}gradle.afterProject { project -> println "项目 ${project.name} 配置完成"}// 执行阶段gradle.taskGraph.whenReady { graph -> println "任务图已准备就绪"}gradle.taskGraph.beforeTask { task -> println "准备执行任务: ${task.name}"}gradle.taskGraph.afterTask { task, state -> println "任务 ${task.name} 执行完成,状态: ${state.failure ? '失败' : '成功'}}}性能优化建议减少配置阶段的工作量:将不必要的逻辑移到执行阶段使用延迟初始化:使用 tasks.register() 而不是 tasks.create()避免在配置阶段进行 I/O 操作:如网络请求、文件读写等使用配置缓存:通过 --configuration-cache 参数启用
阅读 0·2月21日 18:10

Gradle 如何实现多项目构建?如何配置项目间的依赖关系?

Gradle 的多项目构建功能允许开发者在一个构建中管理多个相关的项目,这对于大型应用程序和微服务架构非常有用。以下是 Gradle 多项目构建的详细说明:多项目构建结构基本目录结构my-project/├── settings.gradle├── build.gradle├── app/│ ├── build.gradle│ └── src/├── library/│ ├── build.gradle│ └── src/└── common/ ├── build.gradle └── src/settings.gradle 配置// settings.gradlerootProject.name = 'my-project'// 包含子项目include 'app', 'library', 'common'// 使用相对路径包含项目include ':data:repository'project(':data:repository').projectDir = new File(rootDir, 'modules/data/repository')// 排除项目// include 'excluded-module'项目配置根项目配置// build.gradle (根项目)allprojects { group = 'com.example' version = '1.0.0' repositories { mavenCentral() }}subprojects { apply plugin: 'java' java { sourceCompatibility = JavaVersion.VERSION_17 } dependencies { implementation 'org.slf4j:slf4j-api:2.0.7' }}特定项目配置// app/build.gradledependencies { implementation project(':library') implementation project(':common') testImplementation project(':common').sourceSets.test.output}// library/build.gradledependencies { api project(':common')}项目依赖项目间依赖// 使用 project() 方法dependencies { implementation project(':library') testImplementation project(':common').sourceSets.test.output // 使用配置 implementation project(path: ':library', configuration: 'runtimeClasspath')}依赖配置// 在被依赖的项目中定义配置// library/build.gradleconfigurations { apiElements { canBeResolved = false canBeConsumed = true attributes { attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, 'java-api')) } }}// 在依赖项目中使用// app/build.gradledependencies { implementation project(path: ':library', configuration: 'apiElements')}项目属性和配置项目属性// 定义项目属性ext { springBootVersion = '3.0.0' junitVersion = '5.9.0'}// 在子项目中访问// app/build.gradledependencies { implementation "org.springframework.boot:spring-boot-starter-web:${springBootVersion}"}条件配置// 根据项目名称条件配置configure(subprojects.findAll { it.name.startsWith('web-') }) { apply plugin: 'war'}// 根据项目属性条件配置subprojects { if (project.hasProperty('enableJacoco')) { apply plugin: 'jacoco' }}共享配置使用配置注入// build.gradle (根项目)subprojects { // 配置所有子项目 apply plugin: 'java' // 配置 Java 编译 tasks.withType(JavaCompile).configureEach { options.encoding = 'UTF-8' options.compilerArgs << '-Xlint:unchecked' } // 配置测试 test { useJUnitPlatform() testLogging { events 'passed', 'skipped', 'failed' } }}使用约定插件// buildSrc/src/main/groovy/JavaLibraryPlugin.groovyclass JavaLibraryPlugin implements Plugin<Project> { void apply(Project project) { project.with { apply plugin: 'java-library' java { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } dependencies { api platform('org.springframework.boot:spring-boot-dependencies:3.0.0') } } }}// 在子项目中使用// library/build.gradleplugins { id 'java-library-convention'}构建任务在所有项目中运行任务# 在所有项目中运行 clean 任务./gradlew clean# 在所有项目中运行 test 任务./gradlew test# 运行特定项目的任务./gradlew :app:build./gradlew :library:test任务依赖// app/build.gradletasks.named('build') { dependsOn ':library:build', ':common:build'}// 根项目 build.gradletasks.register('buildAll') { dependsOn subprojects.collect { "${it.path}:build" }}多项目构建最佳实践1. 合理的项目划分// 按功能模块划分include 'core', 'api', 'web', 'data', 'service'// 按层次划分include 'common', 'infrastructure', 'domain', 'application'2. 共享依赖管理// build.gradle (根项目)ext { versions = [ springBoot: '3.0.0', junit: '5.9.0', mockito: '5.0.0' ] libs = [ springBootWeb: "org.springframework.boot:spring-boot-starter-web:${versions.springBoot}", junit: "org.junit.jupiter:junit-jupiter:${versions.junit}", mockito: "org.mockito:mockito-core:${versions.mockito}" ]}// 在子项目中使用// app/build.gradledependencies { implementation libs.springBootWeb testImplementation libs.junit testImplementation libs.mockito}3. 使用版本目录// gradle/libs.versions.toml[versions]spring-boot = "3.0.0"junit = "5.9.0"[libraries]spring-boot-web = { module = "org.springframework.boot:spring-boot-starter-web", version.ref = "spring-boot" }jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" }[plugins]java = { id = "java" }spring-boot = { id = "org.springframework.boot", version = "3.0.0" }// 在子项目中使用// app/build.gradleplugins { id libs.plugins.java.get().pluginId}dependencies { implementation libs.spring.boot.web testImplementation libs.jupiter}4. 避免循环依赖// 检查循环依赖./gradlew :app:dependencies --configuration runtimeClasspath// 使用依赖分析工具plugins { id 'com.github.dependency-license-report' version '2.5'}性能优化并行构建// gradle.propertiesorg.gradle.parallel=trueorg.gradle.caching=trueorg.gradle.configureondemand=true配置优化// 使用延迟配置subprojects { tasks.register('customTask') { // 任务只在需要时创建 }}// 避免在配置阶段执行耗时操作subprojects { // 不要在这里进行网络请求或文件 I/O}常用命令# 查看项目结构./gradlew projects# 查看所有任务./gradlew tasks# 查看特定项目的任务./gradlew :app:tasks# 查看项目依赖./gradlew :app:dependencies# 构建所有项目./gradlew build# 构建特定项目./gradlew :app:build# 清理所有项目./gradlew clean# 并行构建./gradlew build --parallel
阅读 0·2月21日 18:10