Sharding Sphere 配置错误导致启动失败问题排查

环境:

  • Spring Boot 2.7
  • JDK17
  • Sharding Sphere 5.1.1

报错信息:

org.apache.shardingsphere.spi.exception.ServiceProviderNotFoundException: 
No implementation class load from SPI `org.apache.shardingsphere.readwritesplitting.spi.ReplicaLoadBalanceAlgorithm` with type `null`.

原因分析:

从报错信息可以确认是配置文件读取为空导致的异常

排查配置文件是否配置正确,如yml缩进是否正确,字段大小写是否正确

由于配置文件都是官网直接copy,不应该会出现问题,但是坑的是我只copy了具体的某项配置,比如下面配置模式相关参数:

官网直接给的配置是具体某项的配置,并没有给具体的前缀

mode:
  type: Memory

而我的前缀(spring.sharding-sphere)是IDEA智能提示出来的,如下,使用了中划线的写法,

spring:
  sharding-sphere:
  	mode:
  	  type: Memory

sharding-sphere不能使用中划线写法,会导致读取配置文件数据失败,造成空指针异常

解决方案

修改 sharding-sphereshardingsphere即可

追根溯源

方法一:直接分析日志

查看详细报错信息:

org.apache.shardingsphere.spi.exception.ServiceProviderNotFoundException: No implementation class load from SPI `org.apache.shardingsphere.readwritesplitting.spi.ReplicaLoadBalanceAlgorithm` with type `null`.
	at org.apache.shardingsphere.spi.typed.TypedSPIRegistry.getRegisteredService(TypedSPIRegistry.java:76) ~[shardingsphere-spi-5.1.1.jar:5.1.1]
	at org.apache.shardingsphere.spring.boot.registry.AbstractAlgorithmProvidedBeanRegistry.lambda$registerBean$2(AbstractAlgorithmProvidedBeanRegistry.java:78) ~[shardingsphere-jdbc-spring-boot-starter-infra-5.1.1.jar:5.1.1]
	at java.util.LinkedHashMap.forEach(LinkedHashMap.java:684) ~[na:1.8.0_361]
	at org.apache.shardingsphere.spring.boot.registry.AbstractAlgorithmProvidedBeanRegistry.registerBean(AbstractAlgorithmProvidedBeanRegistry.java:77) ~[shardingsphere-jdbc-spring-boot-starter-infra-5.1.1.jar:5.1.1]
	at org.apache.shardingsphere.readwritesplitting.spring.boot.algorithm.ReadwriteSplittingAlgorithmProvidedBeanRegistry.postProcessBeanDefinitionRegistry(ReadwriteSplittingAlgorithmProvidedBeanRegistry.java:43) ~[shardingsphere-readwrite-splitting-spring-boot-starter-5.1.1.jar:5.1.1]
	at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:311) ~[spring-context-5.3.29.jar:5.3.29]

可以看到调用链:

PostProcessorRegistrationDelegate.java:311
ReadwriteSplittingAlgorithmProvidedBeanRegistry.java:43
AbstractAlgorithmProvidedBeanRegistry.java:77
AbstractAlgorithmProvidedBeanRegistry.java:78
TypedSPIRegistry.java:76

直接在 (TypedSPIRegistry.java:76)行和(AbstractAlgorithmProvidedBeanRegistry.java:78) 行所在的方法打上断点

通过调试,可以看到 程序里是使用 typePrefix = "spring.shardingsphere.rules.readwrite-splitting.load-balancers“前缀来获取环境配置数据。因为我们配置的前缀是 "spring.sharding-sphere,所以导致获取的 algorithmType字段为空。

protected final void registerBean(final String prefix, final Class<T> algorithmClass, final BeanDefinitionRegistry registry) {
    boolean existPrefix = PropertyUtil.containPropertyPrefix(environment, prefix);
    if (existPrefix) {
        // 省略非关键行
        Map<String, YamlShardingSphereAlgorithmConfiguration> shardingAlgorithmMap = new LinkedHashMap<>();
        keys.forEach(each -> {
            // 省略非关键行
            String typePrefix = String.join("", prefix, each, TYPE_SUFFIX);
            String algorithmType = environment.getProperty(typePrefix); // 返回为空了
            config.setType(algorithmType);
            shardingAlgorithmMap.put(each, config);
        });
        ShardingSphereServiceLoader.register(algorithmClass);
        shardingAlgorithmMap.forEach((key, algorithmConfiguration) -> {
            ShardingSphereAlgorithm algorithm = TypedSPIRegistry.getRegisteredService(algorithmClass, algorithmConfiguration.getType(), algorithmConfiguration.getProps());
            // 省略非关键行
        });
    }
}

Sharding Sphere 配置错误导致启动失败问题排查_第1张图片

接着在(TypedSPIRegistry.java:76)中注册负载均衡相关信息的时候就抛出了空指针异常:

public static <T extends TypedSPI> T getRegisteredService(final Class<T> typedSPIClass, final String type, final Properties props) {
    Optional<T> result = findRegisteredService(typedSPIClass, type, props);
    if (result.isPresent()) {
        return result.get();
    }
    throw new ServiceProviderNotFoundException(typedSPIClass, type);
}

为什么获取 algorithmType值得入参一定是 spring.shardingsphere开头的呢?

通过调用链定位到 ReadwriteSplittingAlgorithmProvidedBeanRegistry.java:43

public final class ReadwriteSplittingAlgorithmProvidedBeanRegistry extends AbstractAlgorithmProvidedBeanRegistry<ReplicaLoadBalanceAlgorithm> {
    
    private static final String ALGORITHMS = "spring.shardingsphere.rules.readwrite-splitting.load-balancers.";
    
    public ReadwriteSplittingAlgorithmProvidedBeanRegistry(final Environment environment) {
        super(environment);
    }
    
    @Override
    public void postProcessBeanDefinitionRegistry(final BeanDefinitionRegistry registry) {
        registerBean(ALGORITHMS, ReplicaLoadBalanceAlgorithm.class, registry);
    }
}

可以看到这个类是继承于AbstractAlgorithmProvidedBeanRegistry类,并且使用 ALGORITHMS参数调用了父类的registerBean方法。

同时可以发现 ALGORITHMS 成员常量 使用的是写死的 spring.shardingsphere前缀,所以最终可以确认是配置写错导致的问题。

方法二:分析xxxPropertiesConfiguration类

路径:org.apache.shardingsphere.spring.boot.prop.SpringBootPropertiesConfiguration

通过对SpringBoot的自动装配原理可以找到 xxxPropertiesConfiguration类,查看前缀是否配置正确即可定位问题

@ConfigurationProperties(
    prefix = "spring.shardingsphere"
)
public final class SpringBootPropertiesConfiguration {
    
}

通过属性配置类源码可以看到,源码中映射了 spring.shardingsphere前缀,而我们配置的是spring.sharding-sphere:,如果我们配置的属性前缀和配置类中的映射不一致,就会导致启动失败

你可能感兴趣的:(java)