Ribbon 均衡策略 与 脱离 Eureka 使用、LoadBalancerClient
目录Ribbon 负载均衡策略概述Ribbon 负载均衡策略配置Ribbon 脱离 Eureka 使用没有 Eureka 时已有 Eureka 时Using the Ribbon API Directly(直接使用 Ribbon API)Ribbon 负载均衡策略概述1、如有微服务 mc 下有 3 个节点 A、B、C,当微服务 mk 请求微服务 mc 时,应该使用何...
目录
Using the Ribbon API Directly(直接使用 Ribbon API)
Ribbon 负载均衡策略概述
1、如有微服务 mc 下有 3 个节点 A、B、C,当微服务 mk 请求微服务 mc 时,应该使用何种规则向节点 A、B、C 发起请求呢?于是 Ribbon 有了负载均衡策略。
2、Ribbon 负载均衡继承结构如下图所示,IRule 接口是整个规则/策略的超类/接口:
策略名 | 描述 |
BestAvailableRule | 选择一个最小的并发请求的server。逐个考察 Server,如果 Server 被 tripped(跳闸)了,则忽略,再选择其中 ActiveRequestsCount 最小的 server。 |
AvailabilityFilteringRule | 过滤掉那些因为一直连接失败的被标记为circuit tripped的后端server,并过滤掉那些高并发的的后端server(active connections 超过配置的阈值) |
WeightedResponseTimeRule | 根据响应时间分配一个weight,响应时间越长,weight越小,被选中的可能性越低。 |
RetryRule | 对选定的负载均衡策略机上重试机制。在一个配置时间段内当选择server不成功,则一直尝试使用subRule的方式选择一个可用的server |
RoundRobinRule | 轮询index,选择index对应位置的server |
RandomRule | 随机选择一个server。在index上随机,选择index对应位置的server |
ZoneAvoidanceRule | 复合判断server所在区域的性能和server的可用性选择server |
Ribbon 负载均衡策略配置
1、仍然以 <<netflix ribbon 概述与基本使用、以及 RestTemplate 概述>> 中的 3 个应用作为本文介绍的基础:
eurekaserverchangSha 应用作为 Eureka 服务端, eurekaclientfood、eurekaclient_cat 作为客户端,eurekaclientcat 微服务请求 eurekaclientfood 微服务。
2、eureka 依赖 ribbon,导入了 eureka 客户端组件就同时导入了 ribbon。环境:Java jdk 8 + Spring boot 2.1.3 + spring cloud Greenwich.SR1 + spring 5.1.5。
3、官网 "6.4 Customizing the Ribbon Client by Setting Properties" 有说明:
全局配置文件的选项优先级高于 @RibbonClient(configuration=MyRibbonConfig.class 代码方式,@RibbonClient 代码方式高于默认值。
从 1.2.0 版本开始 Spring Cloud Netflix 支持从全局文件进行配置,支持的配置如下:
<clientName>.ribbon.NFLoadBalancerClassName: Should implement ILoadBalancer
<clientName>.ribbon.NFLoadBalancerRuleClassName: Should implement IRule
<clientName>.ribbon.NFLoadBalancerPingClassName: Should implement IPing
<clientName>.ribbon.NIWSServerListClassName: Should implement ServerList
<clientName>.ribbon.NIWSServerListFilterClassName: Should implement ServerListFilter
其中的 <clientName> 为 Eureka 注册中心注册好的微服务名称,也是微服务应用配置的 spring.applicatoin.name 属性值。
配置的属性值为各个接口实现类的全类名。如下所示 users 为需要请求的微服务名称,属性值为各接口实现类的全类名:
users:
ribbon:
NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule #加权响应时间规则
4、eurekaclientcat 请求 eurekaclientfood,所以修改 eurekaclient_cat 的配置文件如下,其余的内容都无需修改:
server:
port: 9394
spring:
application:
name: eureka-client-cat #微服务名称
eureka:
client:
service-url:
defaultZone: http://localhost:9393/eureka/ #eureka 服务器地址
instance:
prefer-ip-address: true # IP 地址代替主机名注册
instance-id: changSha-cat # 微服务实例id名称
#EUREKA-CLIENT-FOOD 请求的微服务名称,即对方的 spring.application.name 属性值
EUREKA-CLIENT-FOOD:
ribbon:
#随机规则,对 EUREKA-CLIENT-FOOD 微服务下的节点随机访问
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
5、启动 eurekaserverchangSha 、eurekaclient_cat 应用 ,然后使用下面的命令再启动打包好的 eurekaclient_food 3 个实例,使用不同的端口以及实例 id:
java -jar eurekaclient_food-0.0.1-SNAPSHOT.jar --server.port=9396 --eureka.instance.instance-id=changSha-food-9396
java -jar eurekaclient_food-0.0.1-SNAPSHOT.jar --server.port=9397 --eureka.instance.instance-id=changSha-food-9397
java -jar eurekaclient_food-0.0.1-SNAPSHOT.jar --server.port=9398 --eureka.instance.instance-id=changSha-food-9398
此时 eureka 注册中心如下所示:
6、访问 http://localhost:9394/getCatById?id=110 通过 changSha-cat 微服务后台请求 Eureka-client-food 微服务下的3个节点:
如果没有在 eurekaclient_cat 中配置随机访问负载均衡策略,则默认情况下是使用轮询策略的,如上所示,显示现在是随机访问的,负载均衡配置生效。其它的均衡策略也是同理。
Ribbon 脱离 Eureka 使用
Ribbon 脱离 Eureka 使用分为两种情况,一是项目中并没有使用 Eureka,二是项目中已经有 Eureka
没有 Eureka 时
1、之前说过 Eureka 无论是服务端还是客户端都依赖了 Ribbon,所以导入了 Eureka 组件后,同时已经导入了 Ribbon 组件,所以直接编码 Ribbon 即可。现在没有 Eureka 时,需要单独导入 Ribbon 组件,修改 eureka-client-cat 的 pom.xml 文件如下(此时没有 Eureka 组件):
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
</dependencies>
2、官网介绍地址:"How to Use Ribbon Without Eureka" ,当没有 Eureka 时,只需要修改 application.yml 如下:
stores:
ribbon:
listOfServers: example.com,google.com
#stores 是一个自定义的标识符,建议写成请求的微服务名称,即对方的 spring.application.name 属性值。
#listOfServers 为请求的服务域名地址,也可以直接是 Ip:Port 格式,不用带应用名称,而是在代码中写应用名
3、现在继续修改 eureka-client-cat 配置文件如下(此时没有 eureka 的配置了):
server:
port: 9394
#EUREKA-CLIENT-FOOD 纯粹是一个自定义的标识,会在代码中使用类似如下的方式进行识别,根据标识找到服务器地址,然后发起请求
#restTemplate.getForObject("http://EUREKA-CLIENT-FOOD/getHunanCuisine", String.class);#如果对方写提供了应用名称,则也要在URL中加上
EUREKA-CLIENT-FOOD:
ribbon:
#随机规则,对 EUREKA-CLIENT-FOOD 微服务下的节点随机访问
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
listOfServers: 192.168.3.6:9396,192.168.3.6:9397 #请求的服务地址,ip:port,多个时使用 逗号 隔开
#注意不用带应用名称,而是在代码中写应用名
4、eureka-client-cat 其它位置都不需要修改。后台代码使用:
String foodMenu = restTemplate.getForObject("http://EUREKA-CLIENT-FOOD/getHunanCuisine", String.class);
因为 restTemplate 具有负载均衡的能力,所以会根据标识 EUREKA-CLIENT-FOOD 查找配置文件中的服务器实际地址(listOfServers),然后根据策略发起请求。后台代码不变
5、此时因为没有使用 Eureka 客户端,所以 Eureka 注册中心是没有 eureka-client-cat 微服务的,但是并不影响对 EUREKA-CLIENT-FOOD 的访问。
总结:没有使用 Eureka 时,先导入 Ribbon 组件,然后修改配置文件添加服务器列表,最后使用具有负载均衡能力的 RestTemplate 向目标微服务发起 http 请求。
已有 Eureka 时
1、官网文档 Disable Eureka Use in Ribbon,对于项目中已经使用了 Eureka 时,需要 Ribbon 禁用 Eureka ,配置如下:
ribbon:
eureka:
enabled: false
2、ribbon.eureka.enabled=false 是 Ribbon 不再使用 Eureka 发现的服务,而使用自己 stores.ribbon.listOfServers 配置的服务。
所以仅仅是 Ribbon 不再依赖 Eureka,而不是项目中禁用 Eureka。
3、修改 eureka-client-cat 的 pom.xml 文件重新添加 spring-cloud-starter-netflix-eureka-client,然后修改配置文件如下:
server:
port: 9394
spring:
application:
name: eureka-client-cat #微服务名称
eureka:
client:
service-url:
defaultZone: http://localhost:9393/eureka/ #eureka 服务器地址
instance:
prefer-ip-address: true # IP 地址代替主机名注册
instance-id: changSha-cat # 微服务实例id名称
#EUREKA-CLIENT-FOOD :虽然 Ribbon 脱离 Eureka 使用可以自定义标识符,但还是建议写成对方的微服务名称
EUREKA-CLIENT-FOOD:
ribbon:
#随机规则,对 EUREKA-CLIENT-FOOD 微服务下的节点随机访问
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
listOfServers: 192.168.3.6:9396 #请求的服务地址,ip:port,多个时使用 逗号 隔开。不要带应用名称
ribbon:
eureka:
enabled: false #Ribbon 禁用 Eureka,禁用后 Ribbon 自己的 *.ribbon.listOfServers 服务配置才会生效
总结:有 Eureka 与没有 Eureka 相比多了一个 Ribbon 禁用 Eureka 的操作,其余是一样的。
后台代码:String foodMenu = restTemplate.getForObject("http://EUREKA-CLIENT-FOOD/getHunanCuisine", String.class);(如果有应用名称,则在 url 中加上)
原理:Ribbon 没有脱离 Eureka 时,负载均衡请求的服务名称 "EUREKA-CLIENT-FOOD" 会自动从 Eureka 客户端服务发现的服务列表中进行查询解析,然后根据实际地址发起请求。当 Ribbon 脱离了 Eureka 时,显然无法再从 Eureka 发现的服务列表中获取,所以需要在配置文件中使用 *.ribbon.listOfServers 进行服务配置,配置服务实际的 ip 与 端口。
因为 EUREKA-CLIENT-FOOD.ribbon.listOfServers 只配置了一个服务器地址,所以永远都是请求它。
Using the Ribbon API Directly(直接使用 Ribbon API)
1、可以通过 LoadBalancerClient API 来获取请求的服务实例 org.springframework.cloud.client.ServiceInstance。
2、直接 @Autowired、@Resource 从容器中获取 org.springframework.cloud.client.loadbalancer.LoadBalancerClient 使用即可。
3、官网文档 "Using the Ribbon API Directly" 已经写的很详细,这里在依样画葫芦:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.net.URI;
@RestController
public class SystemController {
//获取容器中创建好的 RestTemplate 实例
@Resource
private RestTemplate restTemplate;
//默认已经在容器中创建好了实例,直接获取即可
@Autowired
private LoadBalancerClient loadBalancerClient;
/**
* localhost:9394/loadBalancerClient
*
* @return
*/
@GetMapping("loadBalancerClient")
public String testLoadBalancerClient() {
//choose(String serviceId):服务id,没有脱离 Eureka 时,这里通常就是对方服务名称,即 spring.application 属性值
//当 Ribbon 脱离 Eureka 时,服务id 就是与自己的配置文件 xxx.ribbon.listOfServers 中的 xxx 保持一致
ServiceInstance serviceInstance = loadBalancerClient.choose("EUREKA-CLIENT-FOOD");
String host = serviceInstance.getHost();
int port = serviceInstance.getPort();
String instanceId = serviceInstance.getInstanceId();
String serviceId = serviceInstance.getServiceId();
URI uri = serviceInstance.getUri();
URI storesUri = URI.create(String.format("http://%s:%s", serviceInstance.getHost(), serviceInstance.getPort()));
System.out.println("host:" + host);
System.out.println("port:" + port);
System.out.println("instanceId:" + instanceId);
System.out.println("serviceId:" + serviceId);
System.out.println("uri:" + uri);
System.out.println("storesUri:" + storesUri);
return "";
}
}
//控制台输出如下:
host:192.168.3.6
port:9395
instanceId:192.168.3.6:9395
serviceId:wmx
uri:http://192.168.3.6:9395
storesUri:http://192.168.3.6:9395
演示源码 github 地址:https://github.com/wangmaoxiong/ribbon_study
更多推荐
所有评论(0)