如果你正在使用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体系实现标签路由的更多相关文章

  1. Spring Cloud体系介绍

    上图只是Spring Cloud体系的一部分,Spring Cloud共集成了19个子项目,里面都包含一个或者多个第三方的组件或者框架! Spring Cloud 工具框架 1.Spring Clou ...

  2. Spring Cloud Gateway的动态路由怎样做?集成Nacos实现很简单

    一.说明 网关的核心概念就是路由配置和路由规则,而作为所有请求流量的入口,在实际生产环境中为了保证高可靠和高可用,是尽量要避免重启的,所以实现动态路由是非常有必要的:本文主要介绍 Spring Clo ...

  3. Python写的微服务如何融入Spring Cloud体系?

    前言 在今天的文章中小码哥将会给大家分享一个目前工作中遇到的一个比较有趣的案例,就是如何将Python写的微服务融入到以Java技术栈为主的Spring Cloud微服务体系中?也许有朋友会有疑问,到 ...

  4. Spring Cloud (13) 服务网关-路由配置

    传统路由配置 所谓传统路由配置方式就是在不依赖于服务发现机制情况下,通过在配置文件中具体制定每个路由表达式与服务实例的映射关系来实现API网关对外部请求的路由.没有Eureka服务治理框架帮助的时候, ...

  5. Spring Cloud之Zuul网关路由

    前端请求先通过nginx走到zuul网关服务,zuul负责路由转发.请求过滤等网关接入层的功能,默认和ribbon整合实现了负载均衡 比如说你有20个服务,暴露出去,你的调用方,如果要跟20个服务打交 ...

  6. Spring Cloud(5):服务路由(Zuul)

    Zuul简介 所有微服务之间的调用,都应该通过服务网关进行路由,服务网关充当服务与服务之间的中介.服务网关像交通警察一样指挥交通,将用户引导到目标微服务实例.服务网关还充当着应用程序内所有微服务调用的 ...

  7. Spring cloud gateway 如何在路由时进行负载均衡

    本文为博主原创,转载请注明出处: 1.spring cloud gateway 配置路由 在网关模块的配置文件中配置路由: spring: cloud: gateway: routes: - id: ...

  8. spring cloud学习(四) 动态路由

    Zuul的主要功能是路由和过滤器.路由功能是微服务的一部分,zuul实现了负载均衡. 1.1 新建模块zuul pom.xml <?xml version="1.0" enc ...

  9. 6.Spring Cloud初相识-------Zool路由

    前言: 在生产环境中,我们不可能将每个服务的真实信息暴漏出去,因为这样太不安全. 我们会选择使用路由代理真实的服务信息,由它负责转发给真实的服务. 新建一个Zool: 1.添加依赖 <?xml ...

随机推荐

  1. 数据库时间类型是 datetime 类型的处理

    当数据库时间类型是datetime类型时,获取前台时间是要这样处理 ,因为数据库是把时间转换为字符类型来比较的 sb.Append(" and Date <=convert(varch ...

  2. 动态生成表格呈现还是将表格直接绑定gridview等控件呈现的开发方式选择依据

    动态生成表格呈现还是将表格直接绑定gridview等控件呈现的开发方式选择依据:由存储过程决定,如果编写的存储过程可以生成需要呈现的表格则直接绑定,否则要动态生成表格

  3. Vue.js——打包之后资源路径产生问题

    https://blog.csdn.net/qq_30632003/article/details/79353035 https://www.cnblogs.com/diantao/p/7776523 ...

  4. 关于Farseer.net轻量级ORM开源框架 V1.0 概念版本开发的消息

    V0.2版的开源距离今天(05年03月)已有近3年的时间.可以说这个版本已经有点落伍的感觉了,呵呵. V0.2版至今一直处于BUG的修复及一些细小功能的增加,所以版本号上一直没有变化. 其实在这1.2 ...

  5. win+r 快速命令

    control keymgr.dll   打开凭据管理器 secpol.msc   本地安全策略 mstsc   远程 msconfig   启动选项 %temp%   临时文件夹 \\192.168 ...

  6. 【译】x86程序员手册32-9.4 中断描述符表

    9.4 Interrupt Descriptor Table 中断描述符表 The interrupt descriptor table (IDT) associates each interrupt ...

  7. badblocks - 查询设备的坏区块

    语法(SYNPSIS) badblocks [ -svwnf ] [ -b block-size ] [ -c blocks_at_once ] [ -i input_file ] [ -o outp ...

  8. Git中文书籍

    Git中文书籍: http://git-scm.com/book/zh/v1

  9. vue 封装自定义组件

    组件结构 sjld >index.js >sjid.vue 最好单独放一个文件夹,有依赖的话装依赖 Sjld.vue 内容 <template id="sjld" ...

  10. [LUOGU] P1316 丢瓶盖

    题目描述 陶陶是个贪玩的孩子,他在地上丢了A个瓶盖,为了简化问题,我们可以当作这A个瓶盖丢在一条直线上,现在他想从这些瓶盖里找出B个,使得距离最近的2个距离最大,他想知道,最大可以到多少呢? 输入输出 ...