微服务实战(七)实现服务负载均衡 - SpringCloud GateWay + Nacos + Robin
本章主要讲述内容在上一章节中讲述了集成 配置中心+注册中心+服务网关 的初步使用。本章节主要记录 在服务网关中同一个服务下多个节点的负载均衡问题。由于Nacos里默认集成了 Ribon 负载均衡组件,实际上经过上一章节的操作,我们已经搭建完了负载均衡的功能,所以我们主要是验证一下实际的效果,和一些使用方法以及源码分析。具体搭建可参考:微服务实战(六)集成服务网关 SpringClo...
本章主要讲述内容
在上一章节中讲述了集成 配置中心+注册中心+服务网关 的初步使用。
本章节主要记录 在服务网关中同一个服务下多个节点的负载均衡问题。
由于Nacos里默认集成了 Ribon 负载均衡组件,实际上经过上一章节的操作,我们已经搭建完了负载均衡的功能,所以我们主要是验证一下实际的效果,和一些使用方法以及源码分析。
具体搭建可参考:微服务实战(六)集成服务网关 SpringCloud GateWay (配置中心&注册中心用Nacos)
准备多个服务提供者工程
具体搭建可参考:微服务实战(三)集成服务注册发现中心 SpringCloud Nacos
我们在之前章节中已经搭建过服务提供者工程 (combat-provider)
现在我们先做个小小的劳动改造。
写一个新的接口,用于报告自身的节点编号
package com.zjf.combat.api.nacos;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class LoadBanlanceApi {
@Value("${node:0}")
private String node;
@GetMapping(value = "/loadBanlance/print")
public String print() {
return "my node number is " + node;
}
}
然后在配置文件里面加上一个新的节点编号配置
最后我们要做的把这个combat-provider复制成两份,启动。
【combat-provider】 端口号9999 ,application.yml 中 node : 1
【combat-provider2】端口号9998 , application.yml 中 node : 2
最后的最后,把SpringCloud GateWay 也启动起来。
然后调用我们新写的接口: http://127.0.0.1:9000/nacos-provider/loadBanlance/print
多次刷新 ,观察效果:
经过我长达24小时的跟踪,这个确实是交替负载的,也就是平权轮询的规则。
修改权重
进入nacos后台,找到我们的这个服务,修改里面两个节点各自的权重。
我们先将node1 改为 权重3 。
再次测试下, 嗯,我们可以看到多次刷新,节点1和节点2将是... 额,还是依次出现...
居然这个权重设置是没有用的。
先贴下如何解决吧
在 SpringCloud GateWay 的工程(我的工程:combat-gateway)中,注册一个Bean
@SpringBootApplication
@EnableDiscoveryClient
@ComponentScan
public class NacosApplication {
public static void main(String[] args) {
SpringApplication.run(NacosApplication.class, args);
}
@Bean
@Scope(value="prototype")
public IRule loadBalanceRule(){
return new NacosRule();
}
}
关于Nacos集成Robin并使用自定义负载均衡策略的记录
翻了一下Nacos的源码,发现里面是有一个Robin负载均衡策略的实现的,而Nacos可能没有将它自己重写的负载均衡策略启用
com.alibaba.cloud.nacos.ribbon.NacosRule 中的 choose 实现:
public Server choose(Object key) {
try {
String clusterName = this.nacosDiscoveryProperties.getClusterName();
DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer();
String name = loadBalancer.getName();
NamingService namingService = this.nacosDiscoveryProperties
.namingServiceInstance();
List<Instance> instances = namingService.selectInstances(name, true);
if (CollectionUtils.isEmpty(instances)) {
LOGGER.warn("no instance in service {}", name);
return null;
}
List<Instance> instancesToChoose = instances;
if (StringUtils.isNotBlank(clusterName)) {
List<Instance> sameClusterInstances = instances.stream()
.filter(instance -> Objects.equals(clusterName,
instance.getClusterName()))
.collect(Collectors.toList());
if (!CollectionUtils.isEmpty(sameClusterInstances)) {
instancesToChoose = sameClusterInstances;
}
else {
LOGGER.warn(
"A cross-cluster call occurs锛宯ame = {}, clusterName = {}, instance = {}",
name, clusterName, instances);
}
}
Instance instance = ExtendBalancer.getHostByRandomWeight2(instancesToChoose);
return new NacosServer(instance);
}
catch (Exception e) {
LOGGER.warn("NacosRule error", e);
return null;
}
}
其中,getHostByRandomWeight2 这个方法就是根据Nacos后台设置的权重去获取当前要转发的节点。
接下来再翻到网关项目的 ribbon-loadbalancer-2.3.0.jar 依赖,里面的负载均衡器里使用的默认规则是 RoundRobinRule
public class BaseLoadBalancer extends AbstractLoadBalancer implements
PrimeConnections.PrimeConnectionListener, IClientConfigAware {
private static Logger logger = LoggerFactory
.getLogger(BaseLoadBalancer.class);
private final static IRule DEFAULT_RULE = new RoundRobinRule();
com.netflix.loadbalancer.RoundRobinRule 的 choose 方法实现:
RoundRobinRule 是在节点列表中轮询的策略实现
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
log.warn("no load balancer");
return null;
}
Server server = null;
int count = 0;
while (server == null && count++ < 10) {
List<Server> reachableServers = lb.getReachableServers();
List<Server> allServers = lb.getAllServers();
int upCount = reachableServers.size();
int serverCount = allServers.size();
if ((upCount == 0) || (serverCount == 0)) {
log.warn("No up servers available from load balancer: " + lb);
return null;
}
int nextServerIndex = incrementAndGetModulo(serverCount);
server = allServers.get(nextServerIndex);
if (server == null) {
/* Transient. */
Thread.yield();
continue;
}
if (server.isAlive() && (server.isReadyToServe())) {
return (server);
}
// Next.
server = null;
}
if (count >= 10) {
log.warn("No available alive servers after 10 tries from load balancer: "
+ lb);
}
return server;
}
int nextServerIndex = incrementAndGetModulo(serverCount);
server = allServers.get(nextServerIndex);
//根据当前节点总数 递增取余,从而逐次轮询所有节点
private int incrementAndGetModulo(int modulo) {
for (;;) {
int current = nextServerCyclicCounter.get();
int next = (current + 1) % modulo;
if (nextServerCyclicCounter.compareAndSet(current, next))
return next;
}
}
//获取到上一次的轮询索引
nextServerCyclicCounter.get()
在Robin中,AbstractLoadBalancerRule 它的负载均衡策略有如下实现类,其实我们如果想使用结合Nacos后台权重设置的策略,只需要将 NacosRule 注册成为Bean,替换默认的 Rule即可。
所以在 SpringCloud GateWay 的工程中,注册一个Bean
@SpringBootApplication
@EnableDiscoveryClient
@ComponentScan
public class NacosApplication {
public static void main(String[] args) {
SpringApplication.run(NacosApplication.class, args);
}
@Bean
@Scope(value="prototype")
public IRule loadBalanceRule(){
return new NacosRule();
}
}
更多推荐
所有评论(0)