alibaba/Sentinel
View on GitHubSentinel GraalVM SPI 重复加载问题 | Duplicate SPI loading problem under GraalVM
Open
#3012 opened on Jan 3, 2023
area/spihelp wanted
Description
Issue Description
Spring Boot使用 sentinel-core模块时,打包成native镜像 , spi被加载了2次
How to reproduce it (as minimally and precisely as possible)
- 复现demo
package com.demo;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.util.ArrayList;
import java.util.List;
@SpringBootApplication
public class NativeDemoApp {
private static final String RES_KEY = "test";
public static void main(String[] args) {
SpringApplication.run(NativeDemoApp.class, args);
init();
System.out.println("========== 开始执行测试方法 ==========");
for (int i = 0; i < 3; i++) {
String result = test(i);
System.out.println(String.format("========== 第%s次执行结果: %s ==========", i + 1, result));
}
}
public static void init() {
System.out.println("========== 开始加载sentinel规则... ==========");
List<FlowRule> rules = new ArrayList<FlowRule>();
FlowRule rule1 = new FlowRule();
rule1.setResource(RES_KEY);
// Set max qps to 20
rule1.setCount(1);
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule1.setLimitApp("default");
rules.add(rule1);
FlowRuleManager.loadRules(rules);
System.out.println("========== sentinel规则加载完成! ==========");
}
public static String test(int i) {
Entry entry = null;
try {
entry = SphU.entry(RES_KEY);
} catch (BlockException ex) {
return "BlockException...";
} finally {
if (entry != null) {
entry.exit();
}
}
return "调用成功!";
}
}
Maven 相关配置
<properties>
<java.version>17</java.version>
<sentinel.version>1.8.6</sentinel.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>${sentinel.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<jvmArguments>
-agentlib:native-image-agent=config-merge-dir=src/main/resources/META-INF/native-image/
</jvmArguments>
</configuration>
</plugin>
</plugins>
</build>
- 执行
mvn -Pnative spring-boot:run来生成反射文件 - 执行
mvn -Pnative native:compile生成native镜像 - 生成后执行对应的镜像可以看到以下错误
**2023-01-03T19:11:36.354+08:00 INFO 20736 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2023-01-03T19:11:36.354+08:00 INFO 20736 --- [ main] com.demo.NativeDemoApp : Started NativeDemoApp in 0.124 seconds (process running for 0.129)
========== 开始加载sentinel规则... ==========
INFO: Sentinel log output type is: file
INFO: Sentinel log charset is: utf-8
INFO: Sentinel log base directory is: C:\Users\ruanx\logs\csp\
INFO: Sentinel log name use pid is: false
INFO: Sentinel log level is: INFO
========== sentinel规则加载完成! ==========
========== 开始执行测试方法 ==========
com.alibaba.csp.sentinel.spi.SpiLoaderException: [com.alibaba.csp.sentinel.init.InitFunc]Found repeat alias name for com.alibaba.csp.sentinel.metric.extension.MetricCallbackInit and com.alibaba.csp.sentinel.metric.extension.MetricCallbackInit,SPI configuration file=META-INF/services/com.alibaba.csp.sentinel.init.InitFunc
at com.alibaba.csp.sentinel.spi.SpiLoader.fail(SpiLoader.java:525)
at com.alibaba.csp.sentinel.spi.SpiLoader.load(SpiLoader.java:383)
at com.alibaba.csp.sentinel.spi.SpiLoader.loadInstanceListSorted(SpiLoader.java:169)
at com.alibaba.csp.sentinel.init.InitExecutor.doInit(InitExecutor.java:46)
at com.alibaba.csp.sentinel.Env.<clinit>(Env.java:36)
at com.alibaba.csp.sentinel.SphU.entry(SphU.java:85)
at com.demo.NativeDemoApp.test(NativeDemoApp.java:63)
at com.demo.NativeDemoApp.main(NativeDemoApp.java:40)
Exception in thread "main" com.alibaba.csp.sentinel.spi.SpiLoaderException: [com.alibaba.csp.sentinel.slotchain.SlotChainBuilder]Found repeat alias name for com.alibaba.csp.sentinel.slots.DefaultSlotChainBuilder and com.alibaba.csp.sentinel.slots.DefaultSlotChainBuilder,SPI configuration file=META-INF/services/com.alibaba.csp.sentinel.slotchain.SlotChainBuilder
at com.alibaba.csp.sentinel.spi.SpiLoader.fail(SpiLoader.java:525)
at com.alibaba.csp.sentinel.spi.SpiLoader.load(SpiLoader.java:383)
at com.alibaba.csp.sentinel.spi.SpiLoader.loadFirstInstanceOrDefault(SpiLoader.java:229)
at com.alibaba.csp.sentinel.slotchain.SlotChainProvider.newSlotChain(SlotChainProvider.java:44)
at com.alibaba.csp.sentinel.CtSph.lookProcessChain(CtSph.java:205)
at com.alibaba.csp.sentinel.CtSph.entryWithPriority(CtSph.java:136)
at com.alibaba.csp.sentinel.CtSph.entry(CtSph.java:176)
at com.alibaba.csp.sentinel.CtSph.entry(CtSph.java:315)
at com.alibaba.csp.sentinel.SphU.entry(SphU.java:85)
at com.demo.NativeDemoApp.test(NativeDemoApp.java:63)
at com.demo.NativeDemoApp.main(NativeDemoApp.java:40)**
预期正确的执行结果:
2023-01-03T19:17:15.920+08:00 INFO 21724 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2023-01-03T19:17:15.926+08:00 INFO 21724 --- [ main] com.demo.NativeDemoApp : Started NativeDemoApp in 1.057 seconds (process running for 1.52)
========== 开始加载sentinel规则... ==========
========== sentinel规则加载完成! ==========
========== 开始执行测试方法 ==========
========== 第1次执行结果: 调用成功! ==========
========== 第2次执行结果: BlockException... ==========
========== 第3次执行结果: BlockException... ==========
Tell us your environment
Windows 11 , JDK17 , Spring Boot 3.0
Anything else we need to know?
是由于mvn -Pnative spring-boot:run 这一步在reflect-config.json文件会添加以下配置
// 这里省略其他相关配置
{
"name":"com.alibaba.csp.sentinel.slots.DefaultSlotChainBuilder",
"methods":[{"name":"<init>","parameterTypes":[] }]
}
从而导致SPI被加载了2次
如果省略 mvn -Pnative spring-boot:run 这一步(reflect-config.json文件中不包含上面的反射配置) , 直接执行 mvn -Pnative native:compile 生成native镜像 , 则程序符合预期.
目前想到的解决方案是修改SpiLoader.java line:399 的判断逻辑 , 对于重复的加载的类将错误改为警告
if (classMap.containsKey(aliasName)) {
Class<? extends S> existClass = classMap.get(aliasName);
fail("Found repeat alias name for " + clazz.getName() + " and "
+ existClass.getName() + ",SPI configuration file=" + fullFileName);
}