美文网首页
Spring Cloud升级之路 - Hoxton - 2.入口

Spring Cloud升级之路 - Hoxton - 2.入口

作者: 干货满满张哈希 | 来源:发表于2020-05-28 18:47 被阅读0次

    本系列示例与胶水代码地址: https://github.com/HashZhang/spring-cloud-scaffold

    入口类注解修改

    之前的项目,我们也许会用@SpringCloudApplication作为我们入口类的注解。这个注解包括:

    @SpringBootApplication
    @EnableDiscoveryClient
    @EnableCircuitBreaker
    public @interface SpringCloudApplication {
    }
    

    其中的@EnableDiscoveryClient会启动服务发现的客户端,我们这里继续用Eureka,但是EurekaClient不需要这个注解,只要加上spring-cloud-starter-eureka-client的依赖,就会启动EurekaClient。对于@EnableCircuitBreaker这个注解,就比较麻烦了。我们引入了spring-cloud-starter-netflix-eureka-client依赖,这个依赖,包含了hystrix依赖,导致会自动启用hystrix实现CircuitBreaker接口,而我们并不想启用hystrix。并且,使用SpringCloudCircuitBreaker的抽象接口,并不能完全使用resilience4j的所有功能,spring-cloud社区维护的resilience4jstarter功能还有适用性不如resilience4j自己维护的starter

    所以,** 我们这里改为使用@SpringBootApplication作为入口类注解 **

    open-feign 的兼容

    升级后,多 FeignClient 类同一微服务,导致 feign 配置 bean 名称重复。当你的项目中存在多个不同的FeignClient类使用同一个微服务名称的时候:

    @FeignClient(value = "service-provider")
    public interface UserService {
        
    }
    @FeignClient(value = "service-provider")
    public interface RetailerService {
        
    }
    

    升级后,就会报错:

    Spring Boot 2.1.x 版本之后默认不支持同名 bean,需要增加配置 spring.main.allow-bean-definition-overriding=true。但是这是一个非常危险的配置,bean 覆盖开启,如果你定义了重复 Bean,你并不知情,这样可能导致你不确定用的是哪个 bean 导致业务问题。所以不推荐开启。

    另一个解决办法是参考:Support Multiple Clients Using The Same Service

    使用FeignClient中的contextId配置:

    @FeignClient(value = "service-provider", contextId = "UserService")
    public interface UserService {
        
    }
    @FeignClient(value = "service-provider", contextId = "RetailerService")
    public interface RetailerService {
        
    }
    

    但是如果你这样的的FeignClient非常的多,那么改起来也是比较麻烦。我们这里可以考虑使用类的全限定名作为 contextId。考虑在项目中创建两个和框架中的代码同名同路径的类,分别是org.springframework.cloud.openfeign.FeignClientFactoryBean还有FeignClientsRegistrar,相当于改了这两个类的源代码。

    要实现的目的是:使用类的全限定名作为 contextId,这样不同类的 contextId 肯定不一样,实现了配置 bean 的名称唯一。同时修改feign配置读取,用name作为key去读取,而不是contextId,也就是能这样配置不同微服务的feign配置:

    feign.client.config.微服务名称.connectTimeout=1000
    

    对于org.springframework.cloud.openfeign.FeignClientFactoryBean,修改configureFeign方法:

    protected void configureFeign(FeignContext context, Feign.Builder builder) {
        FeignClientProperties properties = this.applicationContext
                .getBean(FeignClientProperties.class);
        if (properties != null) {
            if (properties.isDefaultToProperties()) {
                configureUsingConfiguration(context, builder);
                configureUsingProperties(
                        properties.getConfig().get(properties.getDefaultConfig()),
                        builder);
                //使用name,而不用原来的contextId,我们要实现的是多个FeignClient通过contextId指定bean名称实现多个同微服务的FeignClient共存
                //但是配置上,同一个微服务的FeignClient统一配置
                configureUsingProperties(properties.getConfig().get(this.name),
                        builder);
            } else {
                configureUsingProperties(
                        properties.getConfig().get(properties.getDefaultConfig()),
                        builder);
                //使用name,而不用原来的contextId,我们要实现的是多个FeignClient通过contextId指定bean名称实现多个同微服务的FeignClient共存
                //但是配置上,同一个微服务的FeignClient统一配置
                configureUsingProperties(properties.getConfig().get(this.name),
                        builder);
                configureUsingConfiguration(context, builder);
            }
        } else {
            configureUsingConfiguration(context, builder);
        }
    }
    

    对于org.springframework.cloud.openfeign.FeignClientsRegistrar,修改registerFeignClients:

    for (String basePackage : basePackages) {
        Set<BeanDefinition> candidateComponents = scanner
                .findCandidateComponents(basePackage);
        for (BeanDefinition candidateComponent : candidateComponents) {
            if (candidateComponent instanceof AnnotatedBeanDefinition) {
                // verify annotated class is an interface
                AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
                AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
                Assert.isTrue(annotationMetadata.isInterface(),
                        "@FeignClient can only be specified on an interface");
    
                Map<String, Object> attributes = annotationMetadata
                        .getAnnotationAttributes(
                                FeignClient.class.getCanonicalName());
    
                //使用类的名字作为contextId,实现不同类context不同名称
                //这样就不用每个同微服务的feignclient都要填写不同的contextId了
                String contextId = candidateComponent.getBeanClassName();
                registerClientConfiguration(registry, contextId,
                        attributes.get("configuration"));
    
                registerFeignClient(registry, annotationMetadata, attributes, contextId);
            }
        }
    }
    

    详细源码,请参考:

    相关文章

      网友评论

          本文标题:Spring Cloud升级之路 - Hoxton - 2.入口

          本文链接:https://www.haomeiwen.com/subject/mgaaahtx.html