SpringCloudAlibaba-Ribbon负载均衡

一、什么要进行负载均衡

负载均衡就是为避免所有请求都打到一个服务器上面,导致服务器异常儿产生的服务不可问题。

下面我们就来自定负载均衡的策略,我这边实现的只有一下三种,但是实际上根据业务需求的不同会产生各种均衡策略:

  • 根据权重随机选择一个实例
  • 优先选择同集群
  • 优先选择同版本同集群的示例(金丝雀发布—实现同版本同集群优先负载均衡策略)

二、自定义负载均衡策略

这里我先将我的目录放在这里,下面是各种实现

图片加载失败

第一步:自定义一个负载均衡的配置类

这里使用我们自定义的全局配置类

1
2
3
4
5
6
7
8
9
10
11
12
package com.sy.springcloud.config.ribbon;


import com.sy.ribbon.GlobalRibbonConfig;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
import org.springframework.context.annotation.Configuration;

@Configuration
@RibbonClients(defaultConfiguration = GlobalRibbonConfig.class)
public class CustomeRibbonConfig {
}

第二步:在我们的全局配置类中配置我们想要使用的自定义的负载均衡策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.sy.ribbon;

import com.netflix.loadbalancer.IRule;
import com.sy.springcloud.config.ribbon.rule.TheSameClusterPriorityRule;
import com.sy.springcloud.config.ribbon.rule.TheSameClusterPriorityWithVersionRule;
import com.sy.springcloud.config.ribbon.rule.WeightedBalancerRule;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

/**
* 全局负载均衡配
*/
@Component
public class GlobalRibbonConfig {

@Bean
public IRule iRule() {
// 实现带有权重的负载均衡策略
return new WeightedBalancerRule();
// 实现同集群优先的负载均衡策略
//return new TheSameClusterPriorityRule();
// 实现同版本同集群优先的负载均衡策略(必须同版本,可以不同集群)
//return new TheSameClusterPriorityWithVersionRule();
}

}

第三步:自定义我们的负载均衡策略

第1种类型:根据权重随机选择一个实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package com.sy.springcloud.config.ribbon.rule;

import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.cloud.nacos.ribbon.NacosServer;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

/**
* 根据权重随机选择一个实例
*/
@Slf4j
public class WeightedBalancerRule extends AbstractLoadBalancerRule {

@Autowired
private NacosServiceManager nacosServiceManager;
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {

}

@Override
public Server choose(Object o) {
try {
log.info("key:{}",o);
// 调用父类方法,获得当前使用的均衡负载器
BaseLoadBalancer loadBalancer = (BaseLoadBalancer) this.getLoadBalancer();
// 获取目标服务的名称
String serviceName = loadBalancer.getName();
// 获取nacos的服务发现的API
NamingService namingService = nacosServiceManager.getNamingService();

// 根据名称获取服务发现实例,在selectOneHealthyInstance中,nacos实现了权重的负载均衡算法
Instance instance = namingService.selectOneHealthyInstance(serviceName);

return new NacosServer(instance);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

第2种类型:优先选择同集群

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package com.sy.springcloud.config.ribbon.rule;


import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.cloud.nacos.ribbon.NacosServer;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
* 同集群优先调用--负载均衡策略
*/
@Slf4j
public class TheSameClusterPriorityRule extends AbstractLoadBalancerRule {

@Autowired
private NacosServiceManager nacosServiceManager;

@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;


@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {

}

@Override
public Server choose(Object o) {
try {
// 获取服务所在的集群名称
String clusterName = nacosDiscoveryProperties.getClusterName();
log.info("当前集群名称:{}", clusterName);
// 获取当前的负载均衡器
BaseLoadBalancer loadBalancer = (BaseLoadBalancer) this.getLoadBalancer();
// 获取目标服务的实例名称
String serviceName = loadBalancer.getName();
log.info("目标服务的实例名称:{}", serviceName);
// 获取nacos服务注册发现的api
NamingService namingService = nacosServiceManager.getNamingService();
// 通过namimgService获取当前注册的所有实例
List<Instance> allInstance = namingService.getAllInstances(serviceName);

List<Instance> sameClusterInstanceList;
// 过滤筛选同集群下的服务实例
sameClusterInstanceList = allInstance.stream().filter(instance -> instance.getClusterName().equals(clusterName)).collect(Collectors.toList());
log.info("统计群的服务实例:{}", sameClusterInstanceList);
Instance toBeChooseInstance;
// 选择合适的服务实例
if (sameClusterInstanceList == null || sameClusterInstanceList.size() == 0) {
// 从其他集群中随机选择一个服务实例
toBeChooseInstance = WeightedBalancer.chooseInstanceByRandomWeight(allInstance);
log.info("跨集群调用---{}", toBeChooseInstance.getPort());
} else {
// 从本集群中随机选择一个服务实例
toBeChooseInstance = WeightedBalancer.chooseInstanceByRandomWeight(sameClusterInstanceList);
log.info("同集群调用---{}", toBeChooseInstance.getPort());
}
return new NacosServer(toBeChooseInstance);
} catch (Exception e) {
e.printStackTrace();
}

return null;
}
}

这里以及下一种类型会用到一个类,这个类的作用是根据在符合的实例中随机选择一个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.sy.springcloud.config.ribbon.rule;

import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.client.naming.core.Balancer;

import java.util.List;

/**
* 根据权重随机选择一个实例
*/
public class WeightedBalancer extends Balancer {
public static Instance chooseInstanceByRandomWeight(List<Instance> instanceList) {
return getHostByRandomWeight(instanceList);
}
}

第3种类型:同版本同集群优先择负载均衡策略

这个策略也可以叫做金丝雀发布策略:灰度测试的场景下可以使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
package com.sy.springcloud.config.ribbon.rule;


import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.cloud.nacos.ribbon.NacosServer;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.stream.Collectors;

/**
* 同版本同集群优先调用--负载均衡策略(金丝雀/灰度测试)
* 1、优先同版本同集群的服务调用
* 2、没有 条件1的,同版本跨集群的服务调用
* 3、不可以进行不同版本的服务调用
*/
@Slf4j
public class TheSameClusterPriorityWithVersionRule extends AbstractLoadBalancerRule {

@Autowired
private NacosServiceManager nacosServiceManager;

@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;


@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {

}

@Override
public Server choose(Object o) {
try {
// 获取服务所在的集群名称
String clusterName = nacosDiscoveryProperties.getClusterName();
// 获取服务的版本号
String version = nacosDiscoveryProperties.getMetadata().get("version");
log.info("当前集群名称:{},服务版本号:{}", clusterName, version);
// 获取当前的负载均衡器
BaseLoadBalancer loadBalancer = (BaseLoadBalancer) this.getLoadBalancer();
// 获取目标服务的实例名称
String serviceName = loadBalancer.getName();
log.info("目标服务的实例名称:{}", serviceName);
// 获取nacos服务注册发现的api
NamingService namingService = nacosServiceManager.getNamingService();
// 通过namimgService获取当前注册的所有实例
List<Instance> allInstance = namingService.getAllInstances(serviceName);

// 过滤筛选同版本的服务
List<Instance> sameVersionInstance;
sameVersionInstance = allInstance.stream().filter(instance -> instance.getMetadata().get("version").equals(version)).collect(Collectors.toList());


// 过滤筛选同版本下的同集群的服务实例
List<Instance> sameClusterInstanceList;
sameClusterInstanceList = sameVersionInstance.stream().filter(instance -> instance.getClusterName().equals(clusterName)).collect(Collectors.toList());
log.info("统计群的服务实例:{}", sameClusterInstanceList);
Instance toBeChooseInstance;
// 选择合适的服务实例(必须同版本)
if (sameClusterInstanceList == null || sameClusterInstanceList.size() == 0) {
// 从所有同版本的服务实例中选一个
toBeChooseInstance = WeightedBalancer.chooseInstanceByRandomWeight(sameVersionInstance);
log.info("跨集群调用---{}", toBeChooseInstance.getPort());
} else {
// 从本集群中随机选择一个服务实例
toBeChooseInstance = WeightedBalancer.chooseInstanceByRandomWeight(sameClusterInstanceList);
log.info("同集群调用---{}", toBeChooseInstance.getPort());
}
return new NacosServer(toBeChooseInstance);
} catch (Exception e) {
e.printStackTrace();
}

return null;
}
}

三种配置到这里就完事了,接下来就可以在Nacos中配置权重与版本号进行测试了