Netflix Feign 集成 Ribbon

Ribbon 是 Netflix 发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将 Netflix 的中间层服务连接在一起。Ribbon 客户端组件提供一系列完善的配置项,如:连接超时,重试等。

简单的说,就是在配置文件中列出 Load Balancer 后面所有的机器,Ribbon 会自动的帮助你基于某种策略(例如:简单轮询,随即连接等等)去调用这些主机。而且,我们可以很容易使用 Ribbon 实现自定义的负载均衡算法,点击查看“Netflix Ribbon 教程”。

集成 Ribbon

在 Netflix Feign 中,RibbonModule 模块覆盖了 Feign 客户端的 URL 解析,添加了由 Ribbon 提供的智能路由和弹性能力。

RibbonModule 模块包含了一个 Feign 的 Target(LoadBalancingTarget) 和 Client(RibbonClient) 适配器,以利用 Ribbon 负载均衡能力。

注意:Ribbon 集成依赖于 Feign 的 Target.url() 被编码为类似于“https://myAppProd”的形式,其中“myAppProd”是 Ribbon 客户端或负载均衡器名称,并且设置了“myAppProd.ribbon.listOfServers”配置。

RibbonClient 类

添加 RibbonClient 会覆盖 Feign 客户端的 URL 解析,增加由 Ribbon 提供的智能路由和弹性能力。

使用下面代码:

MyService api = Feign.builder()
    .client(RibbonClient.create())
    .target(MyService.class, "https://myAppProd");

代替

MyService api = Feign.builder()
    .target(MyService.class, "https://myAppProd-1234567890.us-east-1.elb.amazonaws.com");

LoadBalancingTarget 类

使用或扩展 LoadBalancingTarget 将通过 Ribbon 实现动态 URL 发现,包括增加服务器请求计数。

使用下面代码:

MyService api = Feign.builder()
    .target(LoadBalancingTarget.create(MyService.class, "https://myAppProd"));

代替

MyService api = Feign.builder()
    .target(MyService.class, "https://myAppProd-1234567890.us-east-1.elb.amazonaws.com");

官网地址:https://github.com/OpenFeign/feign/tree/master/ribbon

示例代码

下面将演示如何将 Netflix Feign 和 Ribbon 进行集成,假如我们启动了两个 ServiceDemoApplication 服务,分别位于 8090 和 8091 端口,如下图:

8a7465743e366070ad08f952255367b7_1730790285279-41ce5862-2a8b-43bb-b938-881bb6410d35.png

集成详细步骤如下:

(1)添加 Ribbon 的 Maven 依赖

<dependency>
  <groupId>io.github.openfeign</groupId>
  <artifactId>feign-ribbon</artifactId>
  <version>11.8</version>
</dependency>
<dependency>
  <groupId>com.netflix.ribbon</groupId>
  <artifactId>ribbon-httpclient</artifactId>
  <version>2.7.18</version>
</dependency>

(2)编写一个简单的 hello 服务,返回服务的名称和端口,如下:

package com.hxstrive.service_demo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ServiceDemoApplication {

    @Value("${spring.application.name}")
    private String appName;

    @Value("${server.port}")
    private String appPort;

    @GetMapping("/hello")
    public String hello() {
        return "Hello World! appName=" + appName + ", appPort=" + appPort + "!";
    }

}

(3)添加 Ribbon 配置文件 ribbon.properties,配置 Ribbon 服务列表等信息,如下:

# 最大重试次数
user.ribbon.MaxAutoRetries=1

# (不包括第一个服务器)要重试的下一个服务器的最大数量
user.ribbon.MaxAutoRetriesNextServer=1

# 对于这个客户端,是否所有操作都可以重试
user.ribbon.OkToRetryOnAllOperations=true

# 从源刷新服务器列表的时间间隔
user.ribbon.ServerListRefreshInterval=2000

# Apache HttpClient 使用的连接超时时间
user.ribbon.ConnectTimeout=3000

# Apache HttpClient 使用的读取超时时间
user.ribbon.ReadTimeout=3000

# 初始服务器列表,可在运行时通过 Archaius 动态属性进行更改
user.ribbon.listOfServers=http://localhost:8090,http://localhost:8091
user.ribbon.EnablePrimeConnections=true

(4)编写 Feign 客户端,初始化 Ribbon,如下图:

package com.hxstrive.demo_netflix_feign.feign;

import com.netflix.config.ConfigurationManager;
import feign.Contract;
import feign.Feign;
import feign.RequestLine;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.ribbon.RibbonClient;

public interface RibbonFeign {

    // 定义获取用户信息的方法
    @RequestLine("GET /hello")
    String hello();

    // 创建Feign客户端实例的静态方法
    static RibbonFeign create() {
        try {
            ConfigurationManager.loadPropertiesFromResources("ribbon.properties");
            System.out.println(ConfigurationManager.getConfigInstance().getProperty("user.ribbon.listOfServers"));
        } catch (Exception e) {
            e.printStackTrace();
        }

        return Feign.builder()
                .client(RibbonClient.create())
                .encoder(new Encoder.Default())
                .decoder(new Decoder.Default())
                .contract(new Contract.Default())
                .target(RibbonFeign.class, "http://user");
    }

}

(5)访问接口,多刷新几次,你会发现以轮训的方式调用目标服务,如下图:

faa0dc7d6a95ab0551e9d55c0acfcb0a_1730790372925-9d9c2a45-4058-4f1a-9730-8f92bc07871c.gif

注意,除了使用 RibbonClient 实现 Ribbon,还可以使用 LoadBalancingTarget 实现相同的效果,如下:

package com.hxstrive.demo_netflix_feign.feign;

import com.netflix.config.ConfigurationManager;
import feign.Contract;
import feign.Feign;
import feign.RequestLine;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.ribbon.LoadBalancingTarget;
import feign.ribbon.RibbonClient;

public interface RibbonFeign {

    // 定义获取用户信息的方法
    @RequestLine("GET /hello")
    String hello();

    // 创建Feign客户端实例的静态方法
    static RibbonFeign create() {
        try {
            ConfigurationManager.loadPropertiesFromResources("ribbon.properties");
            System.out.println(ConfigurationManager.getConfigInstance().getProperty("user.ribbon.listOfServers"));
        } catch (Exception e) {
            e.printStackTrace();
        }

        return Feign.builder()
                .encoder(new Encoder.Default())
                .decoder(new Decoder.Default())
                .contract(new Contract.Default())
                // LoadBalancingTarget 
                .target(LoadBalancingTarget.create(RibbonFeign.class, "http://user"));
    }

}

注意,在生产环境中,可以将初始化 Ribbon 的代码放到 Spring Boot 的启动完成监听器,如下:

package com.hxstrive.demo_netflix_feign.listener;

import com.netflix.config.ConfigurationManager;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;

/**
 * APP 启动完成监听器
 */
public class AppReadyListener {

    @EventListener
    public void onApplicationReady(ApplicationReadyEvent event) {
        System.out.println("## 应用已经完全准备好,可以接收请求啦!");

        // 可以在这里添加检查服务是否可用、预热缓存等逻辑
        // ribbon 配置文件加载
        try {
            ConfigurationManager.loadPropertiesFromResources("ribbon.properties");
            System.out.println(ConfigurationManager.getConfigInstance().getProperty("user.ribbon.listOfServers"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

添加 Spring Boot 配置文件,如下:

package com.hxstrive.demo_netflix_feign.config;

import com.hxstrive.demo_netflix_feign.listener.AppReadyListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * APP 配置
 */
@Configuration
public class AppConfig {

    @Bean
    public AppReadyListener appReadyListener() {
        return new AppReadyListener();
    }

}

效果和上面一样。

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号