Spring Cloud体系实现标签路由
如果你正在使用Spring Cloud体系,在实际使用过程中正遇到以下问题,可以阅读本文章的内容作为后续你解决这些问题的参考,文章内容不保证无错,请务必仔细思考之后再进行实践。
问题:
1,本地连上开发或测试环境的集群连调,正常测试请求可能会请求到本地,被自己的debug阻塞。
2,测试环境维护时,多项目并发提测,维护多个相同的集群进行测试是否必要,是否有更好的方案。
一般,我们在使用Spring Cloud全家桶的时候,会选择zuul作为网关,Ribbon作为负载均衡器,Feign作为远程服务调用模版。使用过Spring Cloud的同学对这些组件的作用必然非常熟悉。这里就拿这些组件组合成的微服务集群来实现标签路由的功能。
实现的效果如图所示,在头上带上标签的请求会在经过网关和各个应用时进行标签判断流量应该打到哪一个去,而每一个应用自己本身的标签是通过eureka上的matedate实现的。

如下图可以构想动态修改标签控制应用所能承接的请求,这里暂时不描述mq部分的功能:

答案:
实现一个ZoneAvoidanceRule的继承类,重写getPredicate方法:
@Override
public AbstractServerPredicate getPredicate() {
OfflineEnvMetadataAwarePredicate offlineEnvMetadataAwarePredicate = new OfflineEnvMetadataAwarePredicate();
offlineEnvMetadataAwarePredicate.setEnv(env);
return offlineEnvMetadataAwarePredicate;
}
Predicate的实现屏蔽了开发测试环境中非这个环境网段启动的应用,并且比对请求的标签和本地的标签,来控制路由给哪一个服务器。
/**
* 线下环境路由策略具体逻辑
*/
public class OfflineEnvMetadataAwarePredicate extends AbstractServerPredicate {
private String env;
public void setEnv(String env) {
this.env = env;
}
@Override
public boolean apply(PredicateKey predicateKey) {
if(predicateKey == null || !(predicateKey.getServer() instanceof DiscoveryEnabledServer)){
return true;
}
DiscoveryEnabledServer server = (DiscoveryEnabledServer) predicateKey.getServer();
String serverZone = server.getInstanceInfo().getMetadata().get("zone");
String requestZone = RequestZoneLabelContext.getRequestZone();
// dev || sit 环境 本地不允许直接连调
if(env.equals("sit") || env.equals("dev")){
if(StringUtils.isBlank(requestZone) && !server.getHost().startsWith("10.0")){
return false;
}
}
if(StringUtils.isNotBlank(serverZone)) {
return serverZone.equals(requestZone);
}else if(StringUtils.isNotBlank(requestZone)){
return requestZone.equals(serverZone);
}
return true;
}
}
那么我们注意到请求头上的标签要在初始时就拿到,所以需要一个ServletRequestListener,将拿到的zone放入RequestZoneLabelContext。我们知道在一个请求中如果是一个io线程执行到底,我们只需要利用threadlocal来存储线程变量,可是如果一个请求中会产生不定的子线程完成,数据在线程间的传递就成为问题,这里使用了InheritableThreadLocal来决解,在RequestZoneLabelContext中可以看到。
public class RequestZoneLabelContextListener implements ServletRequestListener {
private static final String ZONE_LABEL_NAME = "zone";
@Override
public void requestDestroyed(ServletRequestEvent sre) {
RequestZoneLabelContext.remove();
}
@Override
public void requestInitialized(ServletRequestEvent requestEvent) {
HttpServletRequest request = (HttpServletRequest)requestEvent.getServletRequest();
String lbZone = request.getHeader(ZONE_LABEL_NAME);
if(StringUtils.isNotBlank(lbZone)){
RequestZoneLabelContext.setZone(lbZone);
}
}
}
/**
* 从request header上传递label到feign请求
*/
public class RequestZoneLabelContext {
private static InheritableThreadLocal<String> zoneLabelThreadLocal = new InheritableThreadLocal<>();
public static void setZone(String zone){
zoneLabelThreadLocal.set(zone);
}
public static String getRequestZone(){
return zoneLabelThreadLocal.get();
}
public static void remove(){
zoneLabelThreadLocal.remove();
}
}
那么在应用之间调用的feign中我们是需要继续把这个zone通过header传递下去的,所以又扩展了RequestInterceptor:
public class FeignZoneHeaderInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
String requestZone = RequestZoneLabelContext.getRequestZone();
if(StringUtils.isNotBlank(requestZone)){
template.header("zone", requestZone);
}
}
}
至此就基本实现了最初的想法。
这个实现方式仅供参考,如有跟好的方式,多多指教哈~
Spring Cloud体系实现标签路由的更多相关文章
- Spring Cloud体系介绍
上图只是Spring Cloud体系的一部分,Spring Cloud共集成了19个子项目,里面都包含一个或者多个第三方的组件或者框架! Spring Cloud 工具框架 1.Spring Clou ...
- Spring Cloud Gateway的动态路由怎样做?集成Nacos实现很简单
一.说明 网关的核心概念就是路由配置和路由规则,而作为所有请求流量的入口,在实际生产环境中为了保证高可靠和高可用,是尽量要避免重启的,所以实现动态路由是非常有必要的:本文主要介绍 Spring Clo ...
- Python写的微服务如何融入Spring Cloud体系?
前言 在今天的文章中小码哥将会给大家分享一个目前工作中遇到的一个比较有趣的案例,就是如何将Python写的微服务融入到以Java技术栈为主的Spring Cloud微服务体系中?也许有朋友会有疑问,到 ...
- Spring Cloud (13) 服务网关-路由配置
传统路由配置 所谓传统路由配置方式就是在不依赖于服务发现机制情况下,通过在配置文件中具体制定每个路由表达式与服务实例的映射关系来实现API网关对外部请求的路由.没有Eureka服务治理框架帮助的时候, ...
- Spring Cloud之Zuul网关路由
前端请求先通过nginx走到zuul网关服务,zuul负责路由转发.请求过滤等网关接入层的功能,默认和ribbon整合实现了负载均衡 比如说你有20个服务,暴露出去,你的调用方,如果要跟20个服务打交 ...
- Spring Cloud(5):服务路由(Zuul)
Zuul简介 所有微服务之间的调用,都应该通过服务网关进行路由,服务网关充当服务与服务之间的中介.服务网关像交通警察一样指挥交通,将用户引导到目标微服务实例.服务网关还充当着应用程序内所有微服务调用的 ...
- Spring cloud gateway 如何在路由时进行负载均衡
本文为博主原创,转载请注明出处: 1.spring cloud gateway 配置路由 在网关模块的配置文件中配置路由: spring: cloud: gateway: routes: - id: ...
- spring cloud学习(四) 动态路由
Zuul的主要功能是路由和过滤器.路由功能是微服务的一部分,zuul实现了负载均衡. 1.1 新建模块zuul pom.xml <?xml version="1.0" enc ...
- 6.Spring Cloud初相识-------Zool路由
前言: 在生产环境中,我们不可能将每个服务的真实信息暴漏出去,因为这样太不安全. 我们会选择使用路由代理真实的服务信息,由它负责转发给真实的服务. 新建一个Zool: 1.添加依赖 <?xml ...
随机推荐
- mysql提升效率
1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索 ...
- Jquery+ashx实现Ajax
一 Ajax的实现方式 1.使用一般的webform,在页面用jQuery ajax调用,再从取得的html数据中取得<body>内的内容,写入DOM 优点:不用改变现有的asp.net开 ...
- vscode配置python环境
修改 tasks.json 配置文件 找到.vscode文件夹下的tasks.json配置文件,拖进 Visual Studio Code 中进行修改. 也可以直接按Ctrl + Shift + p后 ...
- AngularJS小练习20170508
首先可能需要安装npm,并且配置环境. 1.打开Dos(命令提示符).按Windows徽标键+R组合键,输入cmd然后按回车键进入Dos. 2.安装Yeoman.在Dos下输入npm install ...
- jsencrypt加解密 &&cryptico
npm install --save jsencrypt import {JSEncrypt} from 'jsencrypt'; //导入公钥if ( publicKey.indexOf('---- ...
- zabbix4.2学习笔记--新建用户组和用户
新建用户组 zabbix中管理机器是以用户组划分,这里新建一个只读用户群组和只读用户 新建用户组 点击 管理-用户组-创建用户群组,如下图 点击创建之后,有三列设置,分别是用户群组.权限和标签过滤器, ...
- 小程序input自动聚焦拉起键盘
微信官方提供了两种自动聚焦的方法 1,auto-focus 接受boolean值:默认为false:只需设置为true即可 自动聚焦,拉起键盘:不过官方的提示即将废弃,所以能不用还是不要用 2,foc ...
- nodejs初探
var http= require('http');var server= http.createServer(function(req,res){ res.writeHead(200,{" ...
- 笔试算法题(01):字符串倒置 & 八皇后问题
出题:将字符串“ABCD1234efgh”进行前后对调: 分析: 常见的考查指针使用的案例,知道字符串长度之后,依次交换位置i以及位置(length-1-i)上的内容,直到重叠: 注意不能直接修改指针 ...
- <Redis> 入门三 事务
Redis事务是什么 1.可以一次执行多个命令,本质是一组命令的集合. 2.一个事务中的所有命令都会被序列化,按顺序串行化执行而不会被其他命令插入,不许加塞. 意味着redis在事务执行的过程中,不允 ...