文章目录
前言
(软件定制开发供应商又名金丝雀发布)软件定制开发供应商是指在黑与白之间,软件定制开发供应商能够平滑过渡的一种发布方式。软件定制开发供应商在其上可以进行A/B testing,软件定制开发供应商即让一部分用户继续用产品特性A,一部分用户开始用产品特性B,如果用户对B没有什么反对意见,那么逐步扩大范围,把所有用户都迁移到B上面来。灰度发布可以保证整体系统的稳定,在初始灰度的时候就可以发现、调整问题,以保证其影响度 —— 百度百科
📒 下面把上面这段表述抽象成程序设计模型:
比如现在有2个服务,user服务和order服务,user服务通过在注册中心拉取order服务的地址来消费order服务,灰度发布其实就是让v1版本的user去消费v1版本的order,让v2版本的user去消费v2版本的order。
Ribbon是一个Netflix公司开发的的负载均衡组件,通过自定义实现它的负载均衡策略,可以实现我们的需求。
1.配置负载均衡策略
Nacos中有实现一个优先访问同一ClusterName的Service的负载均衡策略NacosRule,我们可以参考其源码实现。
先上GrayReleasedRule的代码:
public class GrayReleasedRule extends AbstractLoadBalancerRule { private static final Logger LOGGER = LoggerFactory.getLogger(GrayReleasedRule.class); @Autowired private NacosDiscoveryProperties nacosDiscoveryProperties; @Override public Server choose(Object key) { try { String version = this.nacosDiscoveryProperties.getMetadata().get("version"); DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer(); String name = loadBalancer.getName(); NamingService namingService = 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(version)) { List<Instance> sameClusterInstances = instances.stream() .filter(instance -> Objects.equals(version, instance.getMetadata().get("version"))) .collect(Collectors.toList()); if (!CollectionUtils.isEmpty(sameClusterInstances)) { instancesToChoose = sameClusterInstances; } else { LOGGER.warn( "A version-service scall occurs,name = {}, version = {}, instance = {}", name, version, instances); } } Instance instance = ExtendBalancer.getHostByRandomWeight2(instancesToChoose); return new NacosServer(instance); } catch (Exception e) { LOGGER.warn("GrayReleasedRule error", e); return null; } } @Override public void initWithNiwsConfig(IClientConfig iClientConfig) { }}
- 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
首先我们需要继承一个AbstractLoadBalancerRule
抽象类,它实现了IRule
接口,我们需要实现它的choose(Object key)方法,关键部分的代码我把它摘出来:
if (StringUtils.isNotBlank(version)) { List<Instance> sameClusterInstances = instances.stream() .filter(instance -> Objects.equals(version, instance.getMetadata().get("version"))) .collect(Collectors.toList()); if (!CollectionUtils.isEmpty(sameClusterInstances)) { instancesToChoose = sameClusterInstances; } else { LOGGER.warn( "A version-service scall occurs,name = {}, version = {}, instance = {}", name, version, instances); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
我选用的注册中心是Nacos,整合到SpringCloud使用,所以我在
在yml配置文件上的元数据metadata
字段中配上version
字段
spring: cloud: nacos: discovery: server-addr: 127.0.0.1:8888 metadata: version: v1
- 1
- 2
- 3
- 4
- 5
- 6
- 7
首先获取到我们配置的版本号version
String version = this.nacosDiscoveryProperties.getMetadata().get("version");
- 1
判断从注册中心获取的version
不为空的时候,判断是否和当前服务的版本号相同,相同的话就去访问同一版本号的服务。
总结,只需要建立多套服务实例,配置不同的版本号,选择目标用户群体,让请求分散到不同版本号的入口服务上,就能实现不同版本服务的隔离。
2.指定负载均衡策略
通过SpringBean生成策略返回一个IRule接口类型的实例交给SpringContainer管理,覆盖掉Ribbon的默认生成策略
@Configurationpublic class RibbonGrayReleasedConfig { public IRule ribbonRule() { return new GrayReleasedRule(); }}
- 1
- 2
- 3
- 4
- 5
- 6
补充:
1.RandomRule: 随机选择一个Server。
2.RetryRule: 对选定的负载均衡策略机上重试机制,在一个配置时间段内当选择Server不成功,则一直尝试使用subRule的方式选择一个可用的server。
3.RoundRobinRule: 轮询选择, 轮询index,选择index对应位置的Server。
4.AvailabilityFilteringRule: 过滤掉一直连接失败的被标记为circuit tripped的后端Server,并过滤掉那些高并发的后端Server或者使用一个AvailabilityPredicate来包含过滤server的逻辑,其实就是检查status里记录的各个Server的运行状态。
5.BestAvailableRule: 选择一个最小的并发请求的Server,逐个考察Server,如果Server被tripped了,则跳过。
6.WeightedResponseTimeRule: 根据响应时间加权,响应时间越长,权重越小,被选中的可能性越低。
7.ZoneAvoidanceRule: 默认的负载均衡策略,即复合判断Server所在区域的性能和Server的可用性选择Server,在没有区域的环境下,类似于轮询(RandomRule)
8.NacosRule(Nacos的自定义实现): 同集群优先调用
有兴趣的可以自行测试