Ribbon 是 Netflix 发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将 Netflix 的中间层服务连接在一起。Ribbon 客户端组件提供一系列完善的配置项,如:连接超时,重试等。
简单的说,就是在配置文件中列出 Load Balancer 后面所有的机器,Ribbon 会自动的帮助你基于某种策略(例如:简单轮询,随即连接等等)去调用这些主机。而且,我们可以很容易使用 Ribbon 实现自定义的负载均衡算法,点击查看“Netflix 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 会覆盖 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 将通过 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 端口,如下图:
集成详细步骤如下:
(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)访问接口,多刷新几次,你会发现以轮训的方式调用目标服务,如下图:
注意,除了使用 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(); } }
效果和上面一样。