踩坑记录 -微服务中生产者的集群环境下使用eureka+feign的坑(负载均衡失效报错com.netflix.client.ClientException)

今天分享的是关于微服务 eureka集群 + 微服务生产者集群 + feign服务调用 的一个坑(报异常com.netflix.client.ClientException: Load balancer does not have available server for client: CLOUD-PROVIDER-PAYMENT)。

1.环境

先说一下我的环境。

大概如下图所示:

在这里插入图片描述

各个模块的端口号:

在这里插入图片描述

两台eureka分别在7001、7002端口。
两台服务提供者分别在8001、8002端口。
服务消费者在80端口。

2.核心代码

这里说明一下,所有代码都是用来学习spring cloud相关组件的demo级代码,没有加一些健壮性的判断,学会spring cloud组件即可。

服务提供者

核心配置:

server:
  port: 8001
spring:
  application:
    name: cloud-provider-payment
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource      #当前数据源操作类型
    driver-class-name: org.gjt.mm.mysql.Driver        #mysql驱动包
    url: jdbc:mysql://localhost:3306/study_springcloud?useUnicode=true&characterEncoding-utr-8&useSSL=false
    username: root
    password: root

mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: cn.skywalker.springcloud.entity       #所有Entity别名类所在包

eureka:
  client:
    #表示是否将自己注册进EurekaServer默认为true
    register-with-eureka: true 
    fetch-registry: true
    service-url:
      #defaultZone: http://localhost:7001/eureka   # 单机版
      defaultZone: http://localhost:7001/eureka,http://localhost:7002/eureka  # 集群版
  instance:
    instance-id: payment8001
    prefer-ip-address: true

controller

@RestController
@RequestMapping("/payment")
public class PaymentController { 
    @Resource
    private PaymentService paymentService;

    @Value("${server.port}")
    private String serverPort; // 从配置文件中获取端口号

	// 根据主键获取Payment
    @GetMapping("/selectOne/{id}")
    public R selectOne(@PathVariable("id") Long id) {
        return R.ok().data(this.paymentService.queryById(id)).addMessage("server.port = " + serverPort);
    }
}

Feign Client的接口代码

@FeignClient(value = "CLOUD-PROVIDER-PAYMENT")
@RequestMapping("/payment")
public interface PaymentClient {
    //通过主键查询单条数据
    @GetMapping("/selectOne/{id}")
    public R selectOne(@PathVariable("id") Long id); 
} 

服务消费者

核心配置:

server:
  port: 80
spring:
  application:
    name: cloud-consumer-order
eureka:
  client:
    #表示是否将自己注册进EurekaServer默认为true
    register-with-eureka: true
    fetch-registry: false
    service-url:
      #defaultZone: http://localhost:7001/eureka   # 单机版
      defaultZone: http://localhost:7001/eureka,http://localhost:7002/eureka   # 集群版

controller

@RestController
@RequestMapping("/consumer")
@Slf4j
public class OrderController {
    @Resource
    private PaymentClient paymentClient;


    @GetMapping("/payment/selectOne/{id}")
    public R selectOne(@PathVariable("id") Long id) {
        return R.ok().data(paymentClient.selectOne(id));
    } 
    
    //----------------这是一条华丽的分割线----------------

    @Resource
    private DiscoveryClient discoveryClient;

    @GetMapping("/payment/discovery")
    public R discovery() {
        return R.ok().data(discoveryClient);
    } 
} 

3.坑

我们满心欢喜的写完了代码,然后把集群环境启动了起来,没有报错,很开心,然后我们打开了eureka的控制台,一切都正常,依然很开心。。

在这里插入图片描述

再然后我们输入了url测试接口,快乐就定格在了敲击enter键的那一瞬间。。。
在这里插入图片描述
oh,amazing…

我去度娘看了看,大多数的答案都是添加一行配置:

ribbon:
  eureka: 
  	enabled: true

但是,我发现人家的默认值本来就是true(我的eureka版本是2.2.1.RELEASSE)。
在这里插入图片描述

这就引发了我的思考,找不到可用的服务?eureka上面明明全部的服务都注册上去了。

我就在服务消费者controller里加上了discovery client的方法。
进行调用后,结果如下:
在这里插入图片描述
eureka中明明全都有啊,为什么服务列表services是个空的呢。。。
在这里插入图片描述

这个问题困扰了我一个小时,不过我已经有一种感觉,答案已经快要揭晓了

4.解决

终于,在我无意中找到了一个关键的地方:

eureka:
  client:
    #表示是否将自己注册进EurekaServer默认为true
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。
    #单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetch-registry: false
    service-url:
      #defaultZone: http://localhost:7001/eureka   # 单机版
      defaultZone: http://localhost:7001/eureka,http://localhost:7002/eureka   # 集群版

关键的地方我已经加上了注释,相信各位也都看懂了吧。
没错,就是eureka.client.fetch-registry这个配置项,在服务提供者是集群的环境下(即同一个服务名下有多台服务器提供服务),必须设置为true才可以配合ribbon进行负载均衡。

他本身的作用就是从eureka中抓取已有的注册信息。

我们可以思考一下,如果不从eureka中获取注册的服务实例信息,ribbon怎么帮我们负载均衡?

ribbon:你**一台服务实例都没,还让我用各种算法去给你负载均衡?做梦!直接送你一句:
com.netflix.client.ClientException: Load balancer does not have available server for client: CLOUD-PROVIDER-PAYMENT

说到这里,问题已经解决了。。我们只需要把eureka.client.fetch-registry改为true就搞定了。
在这里插入图片描述
在这里插入图片描述

可以看到,已经可以正常访问到我们的接口,并且有ribbon默认实现的轮询策略。

nice,打完收工。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值