引言

在学习Spring Cloud 中冲 Eureka 服务注册切换到 openFeign 服务注册时,报错有2个服务注册自动配置类,出现在 order_service 的启动报错中, 前面从 eureka 切换到 consul 服务注册,现在又需要切换到 openFeign 的形式,但是还是存在 consul 的注册类

环境

  • Maven 依赖 pom.xml
    <dependencies>
        <!-- spring cloud 整合 openFeign 组件-->
        <dependency>
            <groupId>com.netflix.feign</groupId>
            <artifactId>feign-core</artifactId>
            <version>8.18.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-openfeign-core</artifactId>
            <version>2.1.3.RELEASE</version>
            <scope>compile</scope>
        </dependency>
        
        <dependency>
             <groupId>org.springframework.cloud</groupId>
             <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
         </dependency>
         <!-- https://mvnrepository.com/artifact/org.springframework.retry/spring-retry -->
         <dependency>
             <groupId>org.springframework.retry</groupId>
             <artifactId>spring-retry</artifactId>
             <version>1.3.0</version>
         </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>
    </dependencies>

  • 几个微服务配置文件 application.properties
// 第一个 order_service 的 配置文件
server.port=9002
server.address=127.0.0.1

# 服务
spring.application.name=service-order


# 数据库
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/db_test
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.database=mysql
spring.jpa.show-sql=true
spring.jpa.open-in-view=true


# eureka 配置
eureka.instance.prefer-ip-address=true
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/
eureka.instance.ip-address=${server.address}
eureka.instance.instance-id=${server.address}:${server.port}
# 向注册中心注册服务ID,发送心跳
eureka.instance.lease-renewal-interval-in-seconds=5
# 续约到期时间
eureka.instance.lease-expiration-duration-in-seconds=10

# 配置Ribbon 负载均衡策略
# Ribbon 创建一个Http请求连接的超时时间
service-product.ribbon.ConnectionTimeout=250
# Ribbon 的数据读取超时时间
service-product.ribbon.ReadTimeout=1000
# 是否对所有的操作进行重试
service-product.ribbon.OkToRetryOnAllOperations=true
# 切换实例的重复次数
service-product.ribbon.MaxAutoRetriesNextServer=1
# 对当前实例的重复次数
service-product.ribbon.MaxAutoRetries=1


# Consul 配置
# consul 服务器主机地址
#spring.cloud.consul.host=127.0.0.1
## consul 服务器 Ip 地址
#spring.cloud.consul.port=8500
## 基于服务发现的配置:
## 是否需要注册
#spring.cloud.consul.discovery.register=true
## 注册的实例 id (唯一标识)
#spring.cloud.consul.discovery.instance-id=${spring.application.name}:${server.port}
## 服务的名称
#spring.cloud.consul.discovery.service-name=${spring.application.name}
## 服务的端口
#spring.cloud.consul.discovery.port=${server.port}
## 服务的请求 ip 地址
#spring.cloud.consul.discovery.ip-address=${server.address}
## 指定开启服务 ip 地址注册
#spring.cloud.consul.discovery.prefer-ip-address=true
## 开启 consul 健康心跳检查
#spring.cloud.consul.discovery.heartbeat.enabled=true
  • openFeign Client 接口
//order service 的 openFeign Client 接口
package com.hk.order.feign;

import com.hk.product.entity.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * @author : HK意境
 * @ClassName : ProductFeignClient
 * @date : 2021/7/22 12:46
 * @description : 声明需要调用的微服务名称:
 *                  @FeignClient
 *                      name : 服务提供者名称
 * @Todo :
 * @Bug :
 * @Modified :
 * @Version : 1.0
 */
@FeignClient(name = "service-product")
public interface ProductFeignClient {


    /**
     * 配置需要调用的微服务接口
     */
    @RequestMapping(value = "/product/{id}",method = RequestMethod.GET)
    public Product findById(@PathVariable("id")int id) ;

}

  • 几个微服务主启动类
// OrderApplication 启动类
/**
 * @author : HK意境
 * @ClassName : OrderApplication
 * @date : 2021/7/19 19:35
 * @description :
 * @Todo :
 * @Bug :
 * @Modified :
 * @Version : 1.0
 */
@SpringBootApplication
// 服务发现
//@EnableDiscoveryClient
//激活 Feign
@EnableFeignClients
@EnableEurekaClient
@ImportAutoConfiguration({RibbonAutoConfiguration.class, FeignRibbonClientAutoConfiguration.class, FeignAutoConfiguration.class})
public class OrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class ,args) ;

    }
}

  • 微服务Controller 层面
package com.hk.order.controller;

import com.hk.order.feign.ProductFeignClient;
import com.hk.product.entity.Product;
import com.hk.product.service.ProductService;
import com.hk.product.service.impl.ProductServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.util.List;

/**
 * @author : HK意境
 * @ClassName : OrderController
 * @date : 2021/7/19 19:36
 * @description :
 * @Todo :
 * @Bug :
 * @Modified :
 * @Version : 1.0
 */
@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private ProductFeignClient productFeignClient;

    @Autowired
    private RestTemplate restTemplate ;

    /***
     * 注入 DiscoveryClient
     *     spring cloud 提供的获取元数据的工具类
     *          调用方法获取服务的元数据信息
     */
    @Autowired
    private DiscoveryClient discoveryClient;


    /***
     * 基于 Ribbon 的形式调用远程微服务
     *      1.使用 @LoadBalanced 注解声明 RestTemplate
     *      2.使用服务名替换IP 地址
     *
     * @param id
     * @return
     */
    @RequestMapping("/buy/{id}")
    public Product buy(@PathVariable("id")int id){

        /*//以调用服务名称获取说有元数据
        List<ServiceInstance> serviceInstances = discoveryClient.getInstances("service-product");

        //获取唯一的一个元数据
        ServiceInstance instance = serviceInstances.get(0);
        //根据元数据中的主机地址和端口号拼接请求的微服务URL
        String url = "http://" +instance.getHost() + ":"+instance.getPort() + "/product/1";
        Product product = restTemplate.getForObject(url, Product.class);*/

      /*  String serviceName = "service-product" ;
        String url = "http://" + serviceName + "/product/1" ;
        System.out.println(url);
        Product product = restTemplate.getForObject(url, Product.class);*/


        //通过 openFeign 方式调用
        Product product = productFeignClient.findById(id);

        return product ;
    }




}

异常报错

主要翻译过来就是只需要一个服务注册自动配置类, 但是提供了2个自动的服务注册器,一个是 eureka ,一个是 consul

***************************
APPLICATION FAILED TO START
***************************

Description:

Field registration in org.springframework.cloud.client.serviceregistry.ServiceRegistryAutoConfiguration$ServiceRegistryEndpointConfiguration required a single bean, but 2 were found:
	- eurekaRegistration: defined in BeanDefinition defined in class path resource [org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration$RefreshableEurekaClientConfiguration.class]
	- consulRegistration: defined by method 'consulRegistration' in class path resource [org/springframework/cloud/consul/serviceregistry/ConsulAutoServiceRegistrationAutoConfiguration.class]

异常原因

主要翻译过来就是只需要一个服务注册自动配置类, 但是提供了2个自动的服务注册器,一个是 eureka ,一个是 consul ,
针对这个原因, 推导因该是 以下几个原因

  • 导入的依赖中还剩下了 consul 的依赖没有清除完成
  • 在这个模块中还配置了 consul 的注册服务,及其注解, 编码等
  • 系统缓存, IDEA 缓存原因

解决过程

有了以上原因以及可能情况的分析一个一个进行解决:

  • 第一先查看是否是 consul 的依赖缓存:果然在依赖中还是剩下了 consul 的 jar 包依赖, 尝试去除
    在这里插入图片描述
    果然去除之后,没有再次报错注册中心多个配置类的问题了,变成了另一个异常, 这也算解决了:
    在这里插入图片描述
  • 接下来是第二种尝试方案: 既然说自动装配的类重复了,那就根据spring boot 的自动装配机制, 去除这个重复的自动装配类就好了, 在配置文件中添加如下配置去除 consul 的自动注册装配类的加载
spring.autoconfigure.exclude: org.springframework.cloud.netflix.eureka.ConsulAutoServiceRegistrationAutoConfiguration

总结

总感觉 Spring Cloud 方面的依赖总是存在一堆奇奇怪怪的问题和错误, 依赖老是存在无法导入的问题,要么导入了不能用,报错,导入不齐全等

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐