现在我们简单地来定制二个 ServiceInstanceListSupplier, 都是zone-preference的变种.

为了方便, 我重新调整了一下项目的结构, 把一些公用的类移动到hello-pubtool 模块, 这样网关项目和Feign项目就能复用一样的类了.

A. main和beta互不相通, 绝对隔离 (资源相对充裕)

回到最开始的目的, 我们先实现这个A方案

package com.cnscud.betazone.pub.samezone;

import com.cnscud.betazone.pub.LogUtils;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.config.LoadBalancerZoneConfig;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import reactor.core.publisher.Flux; import java.util.ArrayList;
import java.util.List;
import java.util.Map; /**
* 只返回统一区域的实例. (和网上的代码略有不同)
* @see org.springframework.cloud.loadbalancer.core.ZonePreferenceServiceInstanceListSupplier
* @see org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplierBuilder
*
*/
public class SameZoneOnlyServiceInstanceListSupplier implements ServiceInstanceListSupplier { private final String ZONE = "zone"; private final ServiceInstanceListSupplier delegate; private final LoadBalancerZoneConfig zoneConfig; private String zone; public SameZoneOnlyServiceInstanceListSupplier(ServiceInstanceListSupplier delegate,
LoadBalancerZoneConfig zoneConfig) {
this.delegate = delegate;
this.zoneConfig = zoneConfig;
} @Override
public String getServiceId() {
return delegate.getServiceId();
} @Override
public Flux<List<ServiceInstance>> get() {
return delegate.get().map(this::filteredByZone);
} private List<ServiceInstance> filteredByZone(List<ServiceInstance> serviceInstances) {
if (zone == null) {
zone = zoneConfig.getZone();
} if (zone != null) {
List<ServiceInstance> filteredInstances = new ArrayList<>();
for (ServiceInstance serviceInstance : serviceInstances) {
String instanceZone = getZone(serviceInstance);
if (zone.equalsIgnoreCase(instanceZone)) {
filteredInstances.add(serviceInstance);
}
}
//如果没找到就返回空列表,绝不返回其他集群的实例
LogUtils.warn("find instances size: " + filteredInstances.size());
return filteredInstances;
} //如果没有zone设置, 则返回所有实例
return serviceInstances;
} private String getZone(ServiceInstance serviceInstance) {
Map<String, String> metadata = serviceInstance.getMetadata();
if (metadata != null) {
return metadata.get(ZONE);
}
return null;
} }

很简单, 不过要注意这个实现如果没有zone设置, 则返回所有可用实例.

对应的配置声明:



/**
* 自定义 Instance List Supplier: 根据默认Zone划分, 并且zone互相隔离.
*/
public class SameZoneOnlyCustomLoadBalancerConfiguration { @Bean
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
DiscoveryClient discoveryClient, Environment env,
LoadBalancerZoneConfig zoneConfig,
ApplicationContext context) { ServiceInstanceListSupplier delegate = new SameZoneOnlyServiceInstanceListSupplier(
new DiscoveryClientServiceInstanceListSupplier(discoveryClient, env),
zoneConfig
);
ObjectProvider<LoadBalancerCacheManager> cacheManagerProvider = context
.getBeanProvider(LoadBalancerCacheManager.class);
if (cacheManagerProvider.getIfAvailable() != null) {
return new CachingServiceInstanceListSupplier(
delegate,
cacheManagerProvider.getIfAvailable()
);
}
return delegate; } }

这里使用了缓存, 如果需要更多特性, 就去 LoadBalancerClientConfiguration 的源码里去参悟吧, 还有什么 health-check, same-instance-preference很多东西可以参考.

C. beta绝对隔离, main在发布过程中可以切换到beta (main区域只有一台机器, 资源比较紧张)

这个也比较简单, 仅有几行代码的差异

package com.cnscud.betazone.pub.samezone;

import com.cnscud.betazone.pub.LogUtils;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.config.LoadBalancerZoneConfig;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import reactor.core.publisher.Flux; import java.util.ArrayList;
import java.util.List;
import java.util.Map; /**
* Beta只返回统一区域的实例, 其他区域如果为空则返回所有实例.
* @see org.springframework.cloud.loadbalancer.core.ZonePreferenceServiceInstanceListSupplier
* @see org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplierBuilder
*
*/
public class SameZoneSpecialBetaServiceInstanceListSupplier implements ServiceInstanceListSupplier { private final String ZONE = "zone";
private String ZONE_BETA = "beta"; private final ServiceInstanceListSupplier delegate; private final LoadBalancerZoneConfig zoneConfig; private String zone; public SameZoneSpecialBetaServiceInstanceListSupplier(ServiceInstanceListSupplier delegate,
LoadBalancerZoneConfig zoneConfig) {
this.delegate = delegate;
this.zoneConfig = zoneConfig;
} @Override
public String getServiceId() {
return delegate.getServiceId();
} @Override
public Flux<List<ServiceInstance>> get() {
return delegate.get().map(this::filteredByZone);
} private List<ServiceInstance> filteredByZone(List<ServiceInstance> serviceInstances) {
if (zone == null) {
zone = zoneConfig.getZone();
} if (zone != null) {
List<ServiceInstance> filteredInstances = new ArrayList<>();
for (ServiceInstance serviceInstance : serviceInstances) {
String instanceZone = getZone(serviceInstance);
if (zone.equalsIgnoreCase(instanceZone)) {
filteredInstances.add(serviceInstance);
}
}
//如果没找到就返回空列表,绝不返回其他集群的实例
LogUtils.warn("find instances size: " + filteredInstances.size());
if(filteredInstances.size()>0) {
return filteredInstances;
}
else {
//如果是beta, 则返回空
if (zone.equalsIgnoreCase(ZONE_BETA)){
return filteredInstances;
}
}
} //如果没有zone设置, 则返回所有实例
return serviceInstances;
} private String getZone(ServiceInstance serviceInstance) {
Map<String, String> metadata = serviceInstance.getMetadata();
if (metadata != null) {
return metadata.get(ZONE);
}
return null;
} }

这个也没啥特殊的.

D. 发布前标注一套区域为beta的服务, 测试通过后修改beta服务的区域为main, 多个实例都可上线服务 (资源非常紧张, 操作相对复杂)

这个就是个附加操作, 可以使用A,B,C任意一个方案, 搭配脚本就可以使用.

脚本已经准备好了:

本文源码: https://github.com/cnscud/javaroom/tree/main/betazone2/hello-pubtool{:target="_blank"}

比较

灰度发布肯定还有很多方案, 但是对于作者来说, 根据zone来做分区灰度发布, 可能这是最简单的一种方式了, 实现简单, 通过Nginx做简单的设置分流到2组网关上, 就可以实现2组实例了.

当然Nginx也可以根据header, cookie, URL来做分流,就看自己的需要了.

代码简单, 容易理解, 大道至简!

后面还会实践通过Header来分流的方法, 不过比较而言, zone-preference 还是最简单的, 后面的实践起来服务直接传递数据就是头疼...

Spring Cloud分区发布实践(5)--定制ServiceInstanceListSupplier的更多相关文章

  1. Spring Cloud分区发布实践(1) 环境准备

    最近研究了一下Spring Cloud里面的灰度发布, 看到各种各样的使用方式, 真是纷繁复杂, 眼花缭乱, 不同的场景需要不同的解决思路. 那我们也来实践一下最简单的场景: 区域划分: 服务分为be ...

  2. Spring Cloud分区发布实践(6)--灰度服务-根据Header选择实例区域

    此文是一个完整的例子, 包含可运行起来的源码. 此例子包含以下部分: 网关层实现自定义LoadBalancer, 根据Header选取实例 服务中的Feign使用拦截器, 读取Header Feign ...

  3. Spring Cloud分区发布实践(3) 网关和负载均衡

    注意: 因为涉及到配置测试切换, 中间环节需按此文章操作体验, 代码仓库里面的只有最后一步的代码 准备好了微服务, 那我们就来看看网关+负载均衡如何一起工作 新建一个模块hello-gateway, ...

  4. Spring Cloud分区发布实践(4) FeignClient

    上面看到直接通过网关访问微服务是可以实现按区域调用的, 那么微服务之间调用是否也能按区域划分哪? 下面我们使用FeignClient来调用微服务, 就可以配合LoadBalancer实现按区域调用. ...

  5. Spring Cloud分区发布实践(2) 微服务

    我们准备一下用于查询姓名的微服务. 首先定义一下服务的接口, 新建一个空的Maven模块hello-remotename-core, 里面新建一个类: public interface RemoteN ...

  6. Spring Cloud Alibaba发布第二个版本,Spring 发来贺电

    还是熟悉的面孔,还是熟悉的味道,不同的是,这次的配方升级了. 今年10月底,Spring Cloud联合创始人Spencer Gibb在Spring官网的博客页面宣布:阿里巴巴开源 Spring Cl ...

  7. spring cloud微服务实践二

    在上一篇,我们已经搭建了spring cloud微服务中的注册中心.但只有一个注册中心还远远不够. 接下来我们就来尝试提供服务. 注:这一个系列的开发环境版本为 java1.8, spring boo ...

  8. 厉害了,Spring Cloud Alibaba 发布 GA 版本!

    ? 小马哥 & Josh Long ? 喜欢写一首诗一般的代码,更喜欢和你共同 code review,英雄的相惜,犹如时间沉淀下来的对话,历久方弥新. 相见如故,@杭州. 4 月 18 日, ...

  9. Spring Boot 2.x 已经发布了很久,现在 Spring Cloud 也发布了 基于 Spring Boot 2.x 的 Finchley 版本,现在一起为项目做一次整体框架升级。

    升级前 => 升级后 Spring Boot 1.5.x => Spring Boot 2.0.2 Spring Cloud Edgware SR4 => Spring Cloud ...

随机推荐

  1. 测试开发之网络篇-IP地址

    IP地址是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异.这里介绍一下目前广泛使用的IPv4版本. IP地址使用一种统一的格式,为互联 ...

  2. 第三天编程学习Hello,World!

    真正意义上迈入编程的大门--Hello,World! 新建一个文件夹(最好在桌面),方便存放代码 新建一个文件(如:Hello.txt) 改文件后缀名为.java 扩展文件得到Hello.java 编 ...

  3. CS 面试题目总结(问题+答案)

    开源了一个新的github仓库CS 面试题目总结(问题+答案),主要总结一些CS大厂常见的面试问题,所有的问题与答案参考了网络上的许多博客和github仓库,也希望各位读者能够对这个仓库进行补充,毕竟 ...

  4. Restful规范-APIView源码分析

    目录 一.Restful规范 十条规范 二.drf的简单使用 三.APIView源码分析 CBV源码分析 APIView源码分析 一.Restful规范 Restful规范是一种web API接口的设 ...

  5. 处理python中的信号

    什么是信号 信号(signal)-- 进程间通讯的一种方式,也可作为一种软件中断的方法.一个进程一旦接收到信号就会打断原来的程序执行来按照信号进行处理. 简化术语,信号是一个事件,用于中断运行功能的执 ...

  6. XAML代码格式化神器扩展:XAML Styler,从安装到放弃

    背景 平时,我们写XAML的时候,写着写着就多了,乱了,听说这个神器扩展可以一键格式化. XAML Styler -- VS格式化扩展 XAML Styler From MarketPlace 安装 ...

  7. 前端BootstrapTable组件不同使用方法的效率各有差异

    本人需要解决的问题(#需求) 设备端批量发送数据过来,数据已按照特定字段进行排序,现在本人需要按照传过来的数据动态更新表格,表格的显示区域有限制 因为一些原因,最终确定使用 Bootstrap Tab ...

  8. CentOS-Docker搭建Kafka(单点,含:zookeeper、kafka-manager)

    Docker搭建Kafka(单点,含:zookeeper.kafka-manager) 下载相关容器 $ docker pull wurstmeister/zookeeper $ docker pul ...

  9. 暑假自学java第五天

    关于测试类的问题: 单独创建一个包存放测试类,如com.test 首先要构建路径添加测试类的相关类库,方法是项目右键,buld path->config buld path->librar ...

  10. 消息队列——kafka

    原文:再过半小时,你就能明白kafka的工作原理了 会出现什么情况呢? 1.为了这个女朋友,我请假回去拿(老板不批). 2.小哥一直在你楼下等(小哥还有其他的快递要送). 3.周末再送(显然等不及). ...