Modify the entry class annotation
In previous projects, we might have annotated our entry class with @SpringCloudApplication. This note includes:
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public @interface SpringCloudApplication {
}Copy the code
The @enableDiscoveryClient will start the service discovery client. We continue to use Eureka, but EurekaClient does not need this annotation, just add the spring-cloud-starter-Eureka-client dependency, It starts EurekaClient. The @enablecircuitbreaker annotation is more troublesome. We introduced the Spring-cloud-starter-Netflix-Eureka-client dependency, which included the Hystrix dependency, causing hystrix to be automatically enabled to implement the CircuitBreaker interface, which we didn’t want to enable. In addition, using SpringCloud’s CircuitBreaker abstract interface cannot fully use all the functions of Resilience4J. The resilience4J starter function maintained by the Spring-Cloud community is not as applicable as that maintained by Resilience4J itself.
So, ** we’ll use @SpringBootApplication as the entry class annotation ** instead
The open – feign compatible
After the upgrade, multiple FeignClient classes share the same microservice, resulting in the feign configuration bean name duplication. When there are multiple FeignClient classes using the same microservice name in your project:
@FeignClient(value = "service-provider")
public interface UserService {
}
@FeignClient(value = "service-provider")
public interface RetailerService {
}Copy the code
After the upgrade, an error will be reported:
Now that Spring Boot 2.1.x does not support ancient beans by default, we need to add spring.main. allow-bean-meant-Overriding =true. However, this is a very dangerous configuration. Bean override is enabled, and if you define duplicate beans without your knowledge, this can cause you to be unsure which bean you are using and cause business problems. Therefore, this function is not recommended.
Another solution is to see: Support Multiple Clients Using The Same Service
Using the contextId configuration in FeignClient:
@FeignClient(value = "service-provider", contextId = "UserService")
public interface UserService {
}
@FeignClient(value = "service-provider", contextId = "RetailerService")
public interface RetailerService {
}Copy the code
But if you have a lot of feignClients like this, it is also more troublesome to change. We can consider using the fully qualified name of the class as contextId here. Consider creating two classes in your project that have the same name and path as the code in the framework. Respectively is org. Springframework. Cloud. Openfeign. FeignClientFactoryBean FeignClientsRegistrar, equivalent to a changed these two classes of source code.
ContextId (); contextId (); contextId (); contextId (); To configure the feIGN configuration for different microservices, use name as the key instead of contextId.
Feign.client.config. Micro service name connectTimeout=1000Copy the code
For org. Springframework. Cloud. Openfeign. FeignClientFactoryBean, modify configureFeign method:
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); // If the bean name is contextId, then the FeignClient is contextId. If the bean name is contextId, the FeignClient is contextId. ConfigureUsingProperties (properties.getConfig().get(this.name), Builder); }else{ configureUsingProperties( properties.getConfig().get(properties.getDefaultConfig()), builder); // If the bean name is contextId, then the FeignClient is contextId. If the bean name is contextId, the FeignClient is contextId. ConfigureUsingProperties (properties.getConfig().get(this.name), Builder); configureUsingConfiguration(context, builder); }}else{ configureUsingConfiguration(context, builder); }}Copy the code
For org. Springframework. Cloud. Openfeign. FeignClientsRegistrar, modify 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()); // Use the class name as contextId, Not achieve similar context so I wouldn't have different name / / every feignclient with micro service must fill out different contextId String contextId = candidateComponent. GetBeanClassName (); registerClientConfiguration(registry, contextId, attributes.get("configuration")); registerFeignClient(registry, annotationMetadata, attributes, contextId); }}}Copy the code
For detailed source code, please refer to:
- FeignClientFactoryBean.java
- FeignClientsRegistrar.java