Nacos实现服务间的负载均衡

一点背景

Nacos支持权重配置,是比较实用的功能。例如可以把好的机器权重升高,让硬件资源好的服务器享受更高的优先级;在某个服务器出现异常的时候可以降低这个服务器的权重或者暂时停止这个服务器的流量。

Nacos是自带Ribbon的。Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用。

Nacos配置负载均衡Demo

继续采用之前的项目进行改进来跑负载均衡。之前的项目:Nacos安装(docker),运行,持久化(单机MySQL),Eureka迁移至Nacos

总体结构

首先是两个服务provider:

ProviderApplication.java

@SpringBootApplication
@EnableDiscoveryClient
public class ProviderApplication {

	public static void main(String[] args) {
		SpringApplication.run(ProviderApplication.class, args);
	}

	@RestController
	class EchoController {
		@Value("${server.port}")
		private String port;

		@RequestMapping(value = "/echo/{string}", method = RequestMethod.GET)
		public String echo(@PathVariable String string) {
			System.out.println(string);

			return "Hello Nacos Discovery00 " + "server port : " + port + string;
		}
	}
}

Controller返回一行字符串+当前服务的端口。

配置文件:

application.properties:

server.port=8080
spring.application.name=service-provider

spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

另外一个服务Provider01提供相同的服务,端口在8081。

定义一个消费者Consumer:

ConsumerApplication.java

@SpringBootApplication
@EnableDiscoveryClient
public class ConsumerApplication {

	@LoadBalanced
	@Bean
	public RestTemplate restTemplate() {
		return new RestTemplate();
	}

	public static void main(String[] args) {
		SpringApplication.run(ConsumerApplication.class, args);
	}

	@RestController
	public class TestController {
		@Autowired
		private RestTemplate restTemplate;

		@RequestMapping(value = "/echos", method = RequestMethod.GET)
		public String echo() {
//			System.out.println("str:"+str);
			return restTemplate.getForObject("http://service-provider/echo/mxb", String.class);
		}
	}
}

他的作用是调用服务service-provider。

配置文件:

application.properties:

server.port=8070
spring.application.name=service-consumer

spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

他运行在8070。

这样就可以运行起来服务了,效果如下:

把他们全部run起来:

我们访问 http://localhost:8070/echos,可以看到:

刷新一下可以看到:

默认方式下是轮询,所以每次刷新都会换一个服务。

在Nacos控制台上可以看到两个服务,其中provider的实例数是两个。

实现在Nacos控制台上配置服务权重

Nacos默认是轮询。此时在控制台上修改服务的权重是无效的,仍然是轮询。

可以在消费者Consumer的代码上面加上:

ConsumerApplication.java

@Bean
@Scope(value = "prototype")
public IRule loadBalanceRule()
{
	return new NacosRule();
}

这样就可以支持在控制台上手动配置权重了。

参考了博客:微服务实战(七)实现服务负载均衡 - SpringCloud GateWay + Nacos + Robin

那么为什么这样可以实现呢?怎样实现这种人工赋权的呢?

阅读一下Nacos的源码

在package com.alibaba.cloud.nacos.ribbon下,有个NacosRule类继承自AbstractLoadBalancerRule,内容如下:

/**
 * Supports preferentially calling the ribbon load balancing rules of the same cluster
 * instance.
 *
 * @author itmuch.com
 */
public class NacosRule extends AbstractLoadBalancerRule {

	private static final Logger LOGGER = LoggerFactory.getLogger(NacosRule.class);

	@Autowired
	private NacosDiscoveryProperties nacosDiscoveryProperties;

	@Autowired
	private NacosServiceManager nacosServiceManager;

	@Override
	public Server choose(Object key) {
		try {
			String clusterName = this.nacosDiscoveryProperties.getClusterName();
			String group = this.nacosDiscoveryProperties.getGroup();
			DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer();
			String name = loadBalancer.getName();

			NamingService namingService = nacosServiceManager
					.getNamingService(nacosDiscoveryProperties.getNacosProperties());
			List<Instance> instances = namingService.selectInstances(name, group, 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,name = {}, clusterName = {}, instance = {}",
							name, clusterName, instances);
				}
			}

			Instance instance = ExtendBalancer.getHostByRandomWeight2(instancesToChoose);

			return new NacosServer(instance);
		}
		catch (Exception e) {
			LOGGER.warn("NacosRule error", e);
			return null;
		}
	}

	@Override
	public void initWithNiwsConfig(IClientConfig iClientConfig) {
	}

}

其中,重载的choose函数实现了对Server的选择。

NacosRule.choose(Object key)调用了
ExtendBalancer.getHostByRandomWeight2(List<Instance> instances)调用了
Balancer.getHostByRandomWeight(List<Instance> hosts)调用了
Chooser<K, T>.randomWithWeight()

在randomWithWeight里面,先生成了一个double类型的随机数random,然后对所有的权重weight数组double weight[]进行二分查找Arrays.binarySearch(weights,random)。

这个二分查找的解释是:

     * Searches the specified array of doubles for the specified value using
     * the binary search algorithm.  The array must be sorted
     * (as by the {@link #sort(double[])} method) prior to making this call.
     * If it is not sorted, the results are undefined.  If the array contains
     * multiple elements with the specified value, there is no guarantee which
     * one will be found.  This method considers all NaN values to be
     * equivalent and equal.

那么这个weight里面到底是什么呢?

在class Ref<T>里面,有个refresh()函数,可以看出来这个weight数组存的是累计占比。

获取的大致原理学习了博客:java中根据权重随机获取数据根据权重随机选取指定条数记录的简单算法实现(C#)

Ribbon默认的rule是轮询,因此在没有设置的情况下默认规则是轮询。如果想使用结合Nacos后台权重设置的策略,只需要将 NacosRule 注册成为Bean,替换默认的 Rule即可。

参考博客

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

java中根据权重随机获取数据

根据权重随机选取指定条数记录的简单算法实现(C#)

  • 8
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值