微服务实战(七)实现服务负载均衡 SpringCloud GateWay + Nacos + Robin

本章主要讲述内容

在上一章节中讲述了集成 配置中心+注册中心+服务网关 的初步使用。

本章节主要记录 在服务网关中同一个服务下多个节点的负载均衡问题。

由于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
        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
        public IRule loadBalanceRule(){
           return new NacosRule();
        }
}
发布了30 篇原创文章 · 获赞 123 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/u011177064/article/details/104223055