如果你正在使用Spring Cloud体系,在实际使用过程中正遇到以下问题,可以阅读本文章的内容作为后续你解决这些问题的参考,文章内容不保证无错,请务必仔细思考之后再进行实践。

问题:

1,本地连上开发或测试环境的集群连调,正常测试请求可能会请求到本地,被自己的debug阻塞。

2,测试环境维护时,多项目并发提测,维护多个相同的集群进行测试是否必要,是否有更好的方案。

一般,我们在使用Spring Cloud全家桶的时候,会选择zuul作为网关,Ribbon作为负载均衡器,Feign作为远程服务调用模版。使用过Spring Cloud的同学对这些组件的作用必然非常熟悉。这里就拿这些组件组合成的微服务集群来实现标签路由的功能。

实现的效果如图所示,在头上带上标签的请求会在经过网关和各个应用时进行标签判断流量应该打到哪一个去,而每一个应用自己本身的标签是通过eureka上的matedate实现的。

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

答案:

实现一个ZoneAvoidanceRule的继承类,重写getPredicate方法:

  1. @Override
  2. public AbstractServerPredicate getPredicate() {
  3. OfflineEnvMetadataAwarePredicate offlineEnvMetadataAwarePredicate = new OfflineEnvMetadataAwarePredicate();
  4. offlineEnvMetadataAwarePredicate.setEnv(env);
  5. return offlineEnvMetadataAwarePredicate;
  6. }

Predicate的实现屏蔽了开发测试环境中非这个环境网段启动的应用,并且比对请求的标签和本地的标签,来控制路由给哪一个服务器。


  1. /**
  2. * 线下环境路由策略具体逻辑
  3. */
  4. public class OfflineEnvMetadataAwarePredicate extends AbstractServerPredicate {
  5. private String env;
  6. public void setEnv(String env) {
  7. this.env = env;
  8. }
  9. @Override
  10. public boolean apply(PredicateKey predicateKey) {
  11. if(predicateKey == null || !(predicateKey.getServer() instanceof DiscoveryEnabledServer)){
  12. return true;
  13. }
  14. DiscoveryEnabledServer server = (DiscoveryEnabledServer) predicateKey.getServer();
  15. String serverZone = server.getInstanceInfo().getMetadata().get("zone");
  16. String requestZone = RequestZoneLabelContext.getRequestZone();
  17. // dev || sit 环境 本地不允许直接连调
  18. if(env.equals("sit") || env.equals("dev")){
  19. if(StringUtils.isBlank(requestZone) && !server.getHost().startsWith("10.0")){
  20. return false;
  21. }
  22. }
  23. if(StringUtils.isNotBlank(serverZone)) {
  24. return serverZone.equals(requestZone);
  25. }else if(StringUtils.isNotBlank(requestZone)){
  26. return requestZone.equals(serverZone);
  27. }
  28. return true;
  29. }
  30. }

那么我们注意到请求头上的标签要在初始时就拿到,所以需要一个ServletRequestListener,将拿到的zone放入RequestZoneLabelContext。我们知道在一个请求中如果是一个io线程执行到底,我们只需要利用threadlocal来存储线程变量,可是如果一个请求中会产生不定的子线程完成,数据在线程间的传递就成为问题,这里使用了InheritableThreadLocal来决解,在RequestZoneLabelContext中可以看到。

  1. public class RequestZoneLabelContextListener implements ServletRequestListener {
  2. private static final String ZONE_LABEL_NAME = "zone";
  3. @Override
  4. public void requestDestroyed(ServletRequestEvent sre) {
  5. RequestZoneLabelContext.remove();
  6. }
  7. @Override
  8. public void requestInitialized(ServletRequestEvent requestEvent) {
  9. HttpServletRequest request = (HttpServletRequest)requestEvent.getServletRequest();
  10. String lbZone = request.getHeader(ZONE_LABEL_NAME);
  11. if(StringUtils.isNotBlank(lbZone)){
  12. RequestZoneLabelContext.setZone(lbZone);
  13. }
  14. }
  15. }
  1. /**
  2. * 从request header上传递label到feign请求
  3. */
  4. public class RequestZoneLabelContext {
  5. private static InheritableThreadLocal<String> zoneLabelThreadLocal = new InheritableThreadLocal<>();
  6. public static void setZone(String zone){
  7. zoneLabelThreadLocal.set(zone);
  8. }
  9. public static String getRequestZone(){
  10. return zoneLabelThreadLocal.get();
  11. }
  12. public static void remove(){
  13. zoneLabelThreadLocal.remove();
  14. }
  15. }

那么在应用之间调用的feign中我们是需要继续把这个zone通过header传递下去的,所以又扩展了RequestInterceptor:

  1. public class FeignZoneHeaderInterceptor implements RequestInterceptor {
  2. @Override
  3. public void apply(RequestTemplate template) {
  4. String requestZone = RequestZoneLabelContext.getRequestZone();
  5. if(StringUtils.isNotBlank(requestZone)){
  6. template.header("zone", requestZone);
  7. }
  8. }
  9. }

至此就基本实现了最初的想法。

这个实现方式仅供参考,如有跟好的方式,多多指教哈~

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. 浏览器和ES5的介绍

    浏览器的组成 : shell(浏览器的外壳).内核(渲染引擎.js引擎)主流浏览器及其内核: IE                    tritent(IE9及以下,IE10及以上用webkit) ...

  2. Scala基础篇-函数式编程的重要特性

    1.纯函数 表示函数无副作用(状态变化). 2.引用透明性 表示对相同输入,总是得到相同输出. 3.函数是一等公民 函数与变量.对象.类是同一等级.表示可以把函数当做参数传入另一个函数,或者作为函数的 ...

  3. Win7系统32位和64位的区别

    Win7系统32位和64位的区别已经是一个老话题了,可是还是有很多朋友不明白.这两者到底有什么区别呢?下面本文与大家通俗的介绍下Win7系统32位和64位的区别,其他一些深入的理论讲述,大家可以看看文 ...

  4. Asp.Net控件的客户端命名

    我们在用ASP.NET写出来的网页,用浏览器来查看生成的客户端代码的时候经常看到这样的代码:GridView1_ctl101_WebUserControl1_webuserControlButton, ...

  5. c#中out参数的作用

    给你个简单的解释说法吧.虽然不完全对.但是我可以让你理解OUT有什么作用.呵呵 举个例子.每个方法只能有一个返回值.但是你想有多个返回值,呵呵.OUT就起作用了啊.比如分页,不光返回数据,还要返回总记 ...

  6. Node.js——重定向

  7. sh NonUniqueObjectException

    话题引入: 使用hibernate进行更新操作时,首先调用了findById方法获取要修改的对象,此时session没有被关闭,接着重新创建一个对象,将要修改的属性值赋值给这个对象.调用修改方法抛出如 ...

  8. D2. Toy Train

    D2. Toy Train time limit per test 2 seconds memory limit per test 256 megabytes input standard input ...

  9. Spring Cloud和Dubbo的对比

  10. 第2节 mapreduce深入学习:4, 5

    第2节 mapreduce深入学习:4.mapreduce的序列化以及自定义排序 序列化(Serialization)是指把结构化对象转化为字节流. 反序列化(Deserialization)是序列化 ...