同步操作将从 Gitee 极速下载/Nepxion-Discovery 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
Nepxion Discovery是一款对Spring Cloud的服务注册发现的增强中间件,其功能包括多版本灰度发布,黑/白名单的IP地址过滤,限制注册等,支持Eureka、Consul和Zookeeper,支持Spring Cloud Api Gateway(F版)、Zuul网关和微服务的灰度发布,支持用户自定义和编程灰度路由策略,支持Nacos和Redis为远程配置中心,支持Spring Cloud E版和F版。现有的Spring Cloud微服务可以方便引入该插件,代码零侵入
使用者只需要做如下简单的事情:
现有Spring Cloud的痛点
简单描述一下,本系统的核心模块“基于版本控制的灰度发布”,从网关(Zuul)开始的灰度发布操作过程
架构图
版本兼容情况
中间件兼容情况
<dependency>
<groupId>com.nepxion</groupId>
<artifactId>discovery</artifactId>
<version>${discovery.plugin.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
微服务端引入
[必须引入] 三个服务注册发现的中间件的增强插件,请任选一个引入
<dependency>
<groupId>com.nepxion</groupId>
<artifactId>discovery-plugin-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>com.nepxion</groupId>
<artifactId>discovery-plugin-starter-consul</artifactId>
</dependency>
<dependency>
<groupId>com.nepxion</groupId>
<artifactId>discovery-plugin-starter-zookeeper</artifactId>
</dependency>
[选择引入] 两个远程配置中心的中间件的扩展插件,如需要,请任选一个引入
<dependency>
<groupId>com.nepxion</groupId>
<artifactId>discovery-plugin-config-center-extension-nacos</artifactId>
</dependency>
<dependency>
<groupId>com.nepxion</groupId>
<artifactId>discovery-plugin-config-center-extension-nacos</artifactId>
</dependency>
[选择引入] 用户自定义和编程灰度路由,如需要,请引入
<dependency>
<groupId>com.nepxion</groupId>
<artifactId>discovery-plugin-strategy-extension-service</artifactId>
</dependency>
网关Zuul端引入
[选择引入] 用户自定义和编程灰度路由,如需要,请引入
<dependency>
<groupId>com.nepxion</groupId>
<artifactId>discovery-plugin-strategy-extension-zuul</artifactId>
</dependency>
网关Spring Cloud Api Gateway(F版)端引入
[选择引入] 用户自定义和编程灰度路由,如需要,请引入
<dependency>
<groupId>com.nepxion</groupId>
<artifactId>discovery-plugin-strategy-extension-gatewway</artifactId>
</dependency>
独立控制台引入
[必须引入]
<dependency>
<groupId>com.nepxion</groupId>
<artifactId>discovery-console-starter</artifactId>
</dependency>
[选择引入] 两个远程配置中心的中间件的扩展插件,如需要,请任选一个引入
<dependency>
<groupId>com.nepxion</groupId>
<artifactId>discovery-console-extension-nacos</artifactId>
</dependency>
<dependency>
<groupId>com.nepxion</groupId>
<artifactId>discovery-console-extension-redis</artifactId>
</dependency>
特别注意:中间件的引入一定要在所有层面保持一致,绝不允许出现类似如下情况,这也是常识
工程名 | 描述 |
---|---|
discovery-common-nacos | 封装Nacos通用操作逻辑 |
discovery-common-redis | 封装Redis通用操作逻辑 |
discovery-plugin-framework | 核心框架 |
discovery-plugin-framework-eureka | 核心框架服务注册发现的Eureka实现 |
discovery-plugin-framework-consul | 核心框架服务注册发现的Consul实现 |
discovery-plugin-framework-zookeeper | 核心框架服务注册发现的Zookeeper实现 |
discovery-plugin-config-center | 配置中心实现 |
discovery-plugin-config-center-extension-nacos | 配置中心的Nacos扩展 |
discovery-plugin-config-center-extension-redis | 配置中心的Redis扩展 |
discovery-plugin-admin-center | 管理中心实现 |
discovery-plugin-starter-eureka | Eureka Starter |
discovery-plugin-starter-consul | Consul Starter |
discovery-plugin-starter-zookeeper | Zookeeper Starter |
discovery-plugin-strategy | 用户自定义和编程灰度路由策略 |
discovery-plugin-strategy-extension-service | 基于服务的用户自定义和编程灰度路由策略扩展 |
discovery-plugin-strategy-extension-zuul | 基于Zuul的用户自定义和编程灰度路由策略扩展 |
discovery-plugin-strategy-extension-gateway | 基于Spring Cloud Api Gateway(F版)的用户自定义和编程灰度路由策略扩展 |
discovery-console | 独立控制台,提供给UI |
discovery-console-extension-nacos | 独立控制台的Nacos扩展 |
discovery-console-extension-redis | 独立控制台的Redis扩展 |
discovery-console-starter | Console Starter |
discovery-console-desktop | 图形化灰度发布等桌面程序 |
discovery-springcloud-example-console | 独立控制台示例 |
discovery-springcloud-example-eureka | Eureka服务器示例 |
discovery-springcloud-example-service | 用于灰度发布的微服务示例 |
discovery-springcloud-example-zuul | 用于灰度发布的Zuul示例 |
discovery-springcloud-example-gateway | 用于灰度发布的Spring Cloud Api Gateway(F版)示例 |
请不要被吓到,我只是把注释写的很详细而已,里面配置没几行,下面的内容也可以通过Json来描述,这里不做描述,见discovery-springcloud-example-service下的rule.json
<?xml version="1.0" encoding="UTF-8"?>
<rule>
<!-- 如果不想开启相关功能,只需要把相关节点删除即可,例如不想要黑名单功能,把blacklist节点删除 -->
<register>
<!-- 服务注册的黑/白名单注册过滤,只在服务启动的时候生效。白名单表示只允许指定IP地址前缀注册,黑名单表示不允许指定IP地址前缀注册。每个服务只能同时开启要么白名单,要么黑名单 -->
<!-- filter-type,可选值blacklist/whitelist,表示白名单或者黑名单 -->
<!-- service-name,表示服务名 -->
<!-- filter-value,表示黑/白名单的IP地址列表。IP地址一般用前缀来表示,如果多个用“;”分隔,不允许出现空格 -->
<!-- 表示下面所有服务,不允许10.10和11.11为前缀的IP地址注册(全局过滤) -->
<blacklist filter-value="10.10;11.11">
<!-- 表示下面服务,不允许172.16和10.10和11.11为前缀的IP地址注册 -->
<service service-name="discovery-springcloud-example-a" filter-value="172.16"/>
</blacklist>
<!-- <whitelist filter-value="">
<service service-name="" filter-value=""/>
</whitelist> -->
<!-- 服务注册的数目限制注册过滤,只在服务启动的时候生效。当某个服务的实例注册达到指定数目时候,更多的实例将无法注册 -->
<!-- service-name,表示服务名 -->
<!-- filter-value,表示最大实例注册数 -->
<!-- 表示下面所有服务,最大实例注册数为10000(全局配置) -->
<count filter-value="10000">
<!-- 表示下面服务,最大实例注册数为5000,全局配置值10000将不起作用,以局部配置值为准 -->
<service service-name="discovery-springcloud-example-a" filter-value="5000"/>
</count>
</register>
<discovery>
<!-- 服务发现的黑/白名单发现过滤,使用方式跟“服务注册的黑/白名单过滤”一致 -->
<!-- 表示下面所有服务,不允许10.10和11.11为前缀的IP地址被发现(全局过滤) -->
<blacklist filter-value="10.10;11.11">
<!-- 表示下面服务,不允许172.16和10.10和11.11为前缀的IP地址被发现 -->
<service service-name="discovery-springcloud-example-b" filter-value="172.16"/>
</blacklist>
<!-- 服务发现的多版本灰度访问控制 -->
<!-- service-name,表示服务名 -->
<!-- version-value,表示可供访问的版本,如果多个用“;”分隔,不允许出现空格 -->
<version>
<!-- 表示消费端服务a的1.0,允许访问提供端服务b的1.0和1.1版本 -->
<service consumer-service-name="discovery-springcloud-example-a" provider-service-name="discovery-springcloud-example-b" consumer-version-value="1.0" provider-version-value="1.0;1.1"/>
<!-- 表示消费端服务b的1.0,允许访问提供端服务c的1.0和1.1版本 -->
<service consumer-service-name="discovery-springcloud-example-b" provider-service-name="discovery-springcloud-example-c" consumer-version-value="1.0" provider-version-value="1.0;1.1"/>
<!-- 表示消费端服务b的1.1,允许访问提供端服务c的1.2版本 -->
<service consumer-service-name="discovery-springcloud-example-b" provider-service-name="discovery-springcloud-example-c" consumer-version-value="1.1" provider-version-value="1.2"/>
</version>
</discovery>
</rule>
版本策略介绍
1. 标准配置,举例如下
<service consumer-service-name="a" provider-service-name="b" consumer-version-value="1.0" provider-version-value="1.0,1.1"/> 表示消费端1.0版本,允许访问提供端1.0和1.1版本
2. 版本值不配置,举例如下
<service consumer-service-name="a" provider-service-name="b" provider-version-value="1.0,1.1"/> 表示消费端任何版本,允许访问提供端1.0和1.1版本
<service consumer-service-name="a" provider-service-name="b" consumer-version-value="1.0"/> 表示消费端1.0版本,允许访问提供端任何版本
<service consumer-service-name="a" provider-service-name="b"/> 表示消费端任何版本,允许访问提供端任何版本
3. 版本值空字符串,举例如下
<service consumer-service-name="a" provider-service-name="b" consumer-version-value="" provider-version-value="1.0,1.1"/> 表示消费端任何版本,允许访问提供端1.0和1.1版本
<service consumer-service-name="a" provider-service-name="b" consumer-version-value="1.0" provider-version-value=""/> 表示消费端1.0版本,允许访问提供端任何版本
<service consumer-service-name="a" provider-service-name="b" consumer-version-value="" provider-version-value=""/> 表示消费端任何版本,允许访问提供端任何版本
4. 版本对应关系未定义,默认消费端任何版本,允许访问提供端任何版本
特殊情况处理,在使用上需要极力避免该情况发生
1. 消费端的application.properties未定义版本号,则该消费端可以访问提供端任何版本
2. 提供端的application.properties未定义版本号,当消费端在xml里不做任何版本配置,才可以访问该提供端
微服务启动的时候,由于规则(例如:rule.xml)已经配置在本地,使用者希望改变一下规则,而不重启微服务,达到规则的改变
微服务启动的时候,由于版本已经写死在application.properties里,使用者希望改变一下版本,而不重启微服务,达到访问版本的路径改变
微服务启动的时候,禁止指定的IP地址注册到服务注册发现中心。支持黑/白名单,白名单表示只允许指定IP地址前缀注册,黑名单表示不允许指定IP地址前缀注册
微服务启动的时候,一旦微服务集群下注册的实例数目已经达到上限(可配置),将禁止后续的微服务进行注册
微服务启动的时候,禁止指定的IP地址被服务发现。它使用的方式和“黑/白名单的IP地址注册的过滤”一致
使用者可以实现跟业务有关的路由策略,根据业务参数的不同,负载均衡到不同的服务器
使用者可以继承如下类
集成了健康检查的Consul控制台
不同的服务注册发现组件对应的版本配置值
# Eureka config
eureka.instance.metadataMap.version=1.0
eureka.instance.metadataMap.group=xxx-service-group
# 奇葩的Consul配置(参考https://springcloud.cc/spring-cloud-consul.html - 元数据和Consul标签)
# Consul config(多个值用“,”分隔,例如version=1.0,value=abc)
spring.cloud.consul.discovery.tags=version=1.0,group=xxx-service-group
# Zookeeper config
spring.cloud.zookeeper.discovery.metadata.version=1.0
spring.cloud.zookeeper.discovery.metadata.group=xxx-service-group
请注意,如下很多配置项,如果使用者不想做特色化的处理,为避免繁琐,使用者绝大多数可以去掉
# Plugin config
# 开启和关闭服务注册层面的控制。一旦关闭,服务注册的黑/白名单过滤功能将失效,最大注册数的限制过滤功能将失效。缺失则默认为true
spring.application.register.control.enabled=true
# 开启和关闭服务发现层面的控制。一旦关闭,服务多版本调用的控制功能将失效,动态屏蔽指定IP地址的服务实例被发现的功能将失效。缺失则默认为true
spring.application.discovery.control.enabled=true
# 开启和关闭通过Rest方式对规则配置的控制和推送。一旦关闭,只能通过远程配置中心来控制和推送。缺失则默认为true
spring.application.config.rest.control.enabled=true
# 规则文件的格式,支持xml和json。缺失则默认为xml
spring.application.config.format=xml
# spring.application.config.format=json
# 本地规则文件的路径,支持两种方式:classpath:rule.xml(rule.json) - 规则文件放在resources目录下,便于打包进jar;file:rule.xml(rule.json) - 规则文件放在工程根目录下,放置在外部便于修改。缺失则默认为不装载本地规则
spring.application.config.path=classpath:rule.xml
# spring.application.config.path=classpath:rule.json
# 为微服务归类的Key,一般通过group字段来归类,例如eureka.instance.metadataMap.group=xxx-group或者eureka.instance.metadataMap.application=xxx-application。缺失则默认为group
# spring.application.group.key=group
# spring.application.group.key=application
# Plugin strategy config
# 开启和关闭用户自定义和编程灰度路由策略的控制,例如用户根据业务参数的不同,负载均衡到不同的服务器。缺失则默认为true
spring.application.strategy.control.enabled=true
# 用户自定义和编程灰度路由策略的时候,需要指定对业务Controller类的扫描路径,以便传递上下文对象。该项配置只对服务有效,对网关无效
spring.application.strategy.scan.packages=com.nepxion.discovery.plugin.example.service.feign
PORT端口号为server.port或者management.port都可以(management.port开放只支持3.x.x版本)
参考Swagger界面,如下图
为UI提供相关接口,包括
PORT端口号为server.port或者management.port都可以((注意:管理端口不支持F版)
参考Swagger界面,如下图
本例将模拟一个较为复杂的场景,如下图
用规则来表述上述关系
<?xml version="1.0" encoding="UTF-8"?>
<rule>
<discovery>
<version>
<!-- 表示网关z的1.0,允许访问提供端服务a的1.0版本 -->
<service consumer-service-name="discovery-springcloud-example-zuul" provider-service-name="discovery-springcloud-example-a" consumer-version-value="1.0" provider-version-value="1.0"/>
<!-- 表示网关z的1.1,允许访问提供端服务a的1.1版本 -->
<service consumer-service-name="discovery-springcloud-example-zuul" provider-service-name="discovery-springcloud-example-a" consumer-version-value="1.1" provider-version-value="1.1"/>
<!-- 表示消费端服务a的1.0,允许访问提供端服务b的1.0版本 -->
<service consumer-service-name="discovery-springcloud-example-a" provider-service-name="discovery-springcloud-example-b" consumer-version-value="1.0" provider-version-value="1.0"/>
<!-- 表示消费端服务a的1.1,允许访问提供端服务b的1.1版本 -->
<service consumer-service-name="discovery-springcloud-example-a" provider-service-name="discovery-springcloud-example-b" consumer-version-value="1.1" provider-version-value="1.1"/>
<!-- 表示消费端服务b的1.0,允许访问提供端服务c的1.0和1.1版本 -->
<service consumer-service-name="discovery-springcloud-example-b" provider-service-name="discovery-springcloud-example-c" consumer-version-value="1.0" provider-version-value="1.0;1.1"/>
<!-- 表示消费端服务b的1.1,允许访问提供端服务c的1.2版本 -->
<service consumer-service-name="discovery-springcloud-example-b" provider-service-name="discovery-springcloud-example-c" consumer-version-value="1.1" provider-version-value="1.2"/>
</version>
</discovery>
</rule>
上述微服务分别见discovery-springcloud-example-service、discovery-springcloud-example-zuul和discovery-springcloud-example-gateway三个工程。相应的服务名、端口和版本见下表
微服务 | 服务端口 | 管理端口 | 版本 |
---|---|---|---|
A1 | 1100 | 5100 | 1.0 |
A2 | 1101 | 5101 | 1.1 |
B1 | 1200 | 5200 | 1.0 |
B2 | 1201 | 5201 | 1.1 |
C1 | 1300 | 5300 | 1.0 |
C2 | 1301 | 5301 | 1.1 |
C3 | 1302 | 5302 | 1.2 |
Zuul | 1400 | 5400 | 1.0 |
Gateway | 1500 | 5500 | 1.0 |
独立控制台见discovery-springcloud-example-console,对应的版本和端口号如下表
服务端口 | 管理端口 |
---|---|
2222 | 3333 |
<dependency>
<groupId>com.nepxion</groupId>
<artifactId>discovery-plugin-starter-eureka</artifactId>
<!-- <artifactId>discovery-plugin-starter-consul</artifactId> -->
<!-- <artifactId>discovery-plugin-starter-zookeeper</artifactId> -->
<version>${discovery.plugin.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<!-- <artifactId>spring-cloud-starter-consul-discovery</artifactId> -->
<!-- <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId> -->
</dependency>
黑/白名单的IP地址注册的过滤
最大注册数的限制的过滤
黑/白名单的IP地址发现的过滤
基于服务的操作过程和效果
新XML规则
<?xml version="1.0" encoding="UTF-8"?>
<rule>
<discovery>
<version>
<service consumer-service-name="discovery-springcloud-example-b" provider-service-name="discovery-springcloud-example-c" consumer-version-value="" provider-version-value="3.0"/>
</version>
</discovery>
</rule>
图1
图2
图3
图4
图5
基于网关的操作过程和效果
图6
图7
public class MyDiscoveryEnabledAdapter implements DiscoveryEnabledAdapter {
private static final Logger LOG = LoggerFactory.getLogger(MyDiscoveryEnabledAdapter.class);
@Override
public boolean apply(Server server, Map<String, String> metadata) {
RequestContext context = RequestContext.getCurrentContext();
String token = context.getRequest().getHeader("token");
// String value = context.getRequest().getParameter("value");
String serviceId = server.getMetaInfo().getAppName().toLowerCase();
LOG.info("Zuul端负载均衡用户定制触发:serviceId={}, host={}, metadata={}, context={}", serviceId, server.toString(), metadata, context);
String filterToken = "abc";
if (StringUtils.isNotEmpty(token) && token.contains(filterToken)) {
LOG.info("过滤条件:当Token含有'{}'的时候,不能被Ribbon负载均衡到", filterToken);
return false;
}
return true;
}
}
图8
public class MyDiscoveryEnabledAdapter implements DiscoveryEnabledAdapter {
private static final Logger LOG = LoggerFactory.getLogger(MyDiscoveryEnabledAdapter.class);
@SuppressWarnings("unchecked")
@Override
public boolean apply(Server server, Map<String, String> metadata) {
ServiceStrategyContext context = ServiceStrategyContext.getCurrentContext();
Map<String, Object> attributes = context.getAttributes();
String serviceId = server.getMetaInfo().getAppName().toLowerCase();
String version = metadata.get(PluginConstant.VERSION);
LOG.info("Serivice端负载均衡用户定制触发:serviceId={}, host={}, metadata={}, context={}", serviceId, server.toString(), metadata, context);
String filterServiceId = "discovery-springcloud-example-c";
String filterVersion = "1.0";
String filterBusinessValue = "abc";
if (StringUtils.equals(serviceId, filterServiceId) && StringUtils.equals(version, filterVersion)) {
if (attributes.containsKey(ServiceStrategyConstant.PARAMETER_MAP)) {
Map<String, Object> parameterMap = (Map<String, Object>) attributes.get(ServiceStrategyConstant.PARAMETER_MAP);
String value = parameterMap.get("value").toString();
if (StringUtils.isNotEmpty(value) && value.contains(filterBusinessValue)) {
LOG.info("过滤条件:当serviceId={} && version={} && 业务参数含有'{}'的时候,不能被Ribbon负载均衡到", filterServiceId, filterVersion, filterBusinessValue);
return false;
}
}
}
return true;
}
}
图9
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。