当前位置:首页 > 编程教程 > java技术文章 > SpringCloud Zuul实现动态路由

SpringCloud Zuul实现动态路由的方法

  • 发布时间:
  • 作者:码农之家
  • 点击:92

这篇文章主要知识点是关于SpringCloud、Zuul、动态路由、springcloud Zuul动态路由的实现 的内容,如果大家想对相关知识点有系统深入的学习,可以参阅以下电子书

RxJava 2.x 实战
RxJava 2.x 实战完整清晰版
  • 类型:RxJava大小:149 MB格式:PDF作者:沈哲
立即下载

SpringCloud Zuul实现动态路由

前言

Zuul 是在Spring Cloud Netflix平台上提供动态路由,监控,弹性,安全等边缘服务的框架,是Netflix基于jvm的路由器和服务器端负载均衡器,相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门。本文基于上篇(SpringCloud系列——Ribbon 负载均衡)实现Zuul动态路由

GitHub地址:https://github.com/Netflix/zuul

官方文档:https://cloud.spring.io/spring-cloud-static/spring-cloud-netflix/2.1.0.RC2/single/spring-cloud-netflix.html

代码编写

首先我们在springCloud下面新建一个springboot项目:zuul-server,pom继承parent,并且在Eureka上面注册(还不会服务注册与发现的,请戳:SpringCloud系列——Eureka 服务注册与发现)

SpringCloud Zuul实现动态路由

maven引入Zuul

 <!-- Zuul -->
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
    </dependency>

配置文件

server.port=10010
spring.application.name=zuul-server
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
#健康检查(需要spring-boot-starter-actuator依赖)
eureka.client.healthcheck.enabled=true
# 续约更新时间间隔(默认30秒)
eureka.instance.lease-renewal-interval-in-seconds=10
# 续约到期时间(默认90秒)
eureka.instance.lease-expiration-duration-in-seconds=10

#zuul代理配置 zuul.routes.服务名.path,服务名要与注册的一致
#应用名映射
zuul.routes.myspringboot.path=/myspringboot/**
zuul.routes.myspringboot.service-id=myspringboot

#URL映射
#zuul.routes.myspringboot.path=/myspringboot/**
#zuul.routes.myspringboot-url.url=http://localhost:10087/

自定义Zuul过滤器

更多的检查规则后续慢慢健全

/**
 * Zuul过滤器,实现了路由检查
 */
public class AccessFilter extends ZuulFilter {

  /**
   * 通过int值来定义过滤器的执行顺序
   */
  @Override
  public int filterOrder() {
    // PreDecoration之前运行
    return PRE_DECORATION_FILTER_ORDER - 1;
  }

  /**
   * 过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型:
   *   public static final String ERROR_TYPE = "error";
   *   public static final String POST_TYPE = "post";
   *   public static final String PRE_TYPE = "pre";
   *   public static final String ROUTE_TYPE = "route";
   */
  @Override
  public String filterType() {
    return PRE_TYPE;
  }

  /**
   * 过滤器的具体逻辑
   */
  @Override
  public Object run() {
    RequestContext ctx = RequestContext.getCurrentContext();
    HttpServletRequest request = ctx.getRequest();
    System.out.println(String.format("%s AccessFilter request to %s", request.getMethod(),request.getRequestURL().toString()));
    String accessToken = request.getParameter("accessToken");
    //有权限令牌
    if (!StringUtils.isEmpty(accessToken)) {
      ctx.setSendZuulResponse(true);
      ctx.setResponseStatusCode(200);
      //可以设置一些值
      ctx.set("isSuccess", true);
      return null;
    } else {
      ctx.setSendZuulResponse(false);
      ctx.setResponseStatusCode(401);
      ctx.setResponseBody("{\"result\":\"accessToken is not correct!\"}");
      //可以设置一些值
      ctx.set("isSuccess", false);
      return null;
    }
  }

  /**
   * 返回一个boolean类型来判断该过滤器是否要执行
   */
  @Override
  public boolean shouldFilter() {
    return true;
  }
}

启动类

添加@EnableZuulProxy注解并使用自定义过滤器

@EnableZuulProxy
@SpringBootApplication
public class ZuulServerApplication {

  public static void main(String[] args) {
    SpringApplication.run(ZuulServerApplication.class, args);
  }

  @Bean
  public AccessFilter accessFilter() {
    return new AccessFilter();
  }
}

效果演示

启动所有项目,我们在Eureka上注册了四个服务,相比上篇(SpringCloud系列——Ribbon 负载均衡)多了一个Zuul

SpringCloud Zuul实现动态路由

浏览器访问 http://localhost:10010/myspringboot/feign/ribbon、http://localhost:10010/myspringboot/feign/ribbon?accessToken=123456

http://localhost:10010/ 这个端口对外暴露,相对于总入口,后面接不同的路径由,Zuul路由到对应的服务上

1、没有accessToken是,无法通过检查

2、携带accessToken时,可正常路由,并且Feign调用、Ribbon负载均衡

SpringCloud Zuul实现动态路由

后记

我们为什么要使用Zuul呢?

1、请求校验、路由转发,接口校验与业务逻辑分离

2、隐藏诸多服务路径,只暴露统一入口,安全

更多Zuul配置,请看官方文档

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持码农之家。

springcloud Zuul动态路由的实现

前言

Zuul 是Netflix 提供的一个开源组件,致力于在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。也有很多公司使用它来作为网关的重要组成部分,碰巧今年公司的架构组决定自研一个网关产品,集动态路由,动态权限,限流配额等功能为一体,为其他部门的项目提供统一的外网调用管理,最终形成产品(这方面阿里其实已经有成熟的网关产品了,但是不太适用于个性化的配置,也没有集成权限和限流降级)。

不过这里并不想介绍整个网关的架构,而是想着重于讨论其中的一个关键点,并且也是经常在交流群中听人说起的:动态路由怎么做?

再阐释什么是动态路由之前,需要介绍一下架构的设计。

传统互联网架构图

springcloud Zuul动态路由的实现 

上图是没有网关参与的一个最典型的互联网架构(本文中统一使用book代表应用实例,即真正提供服务的一个业务系统)

加入eureka的架构图

springcloud Zuul动态路由的实现 

book注册到eureka注册中心中,zuul本身也连接着同一个eureka,可以拉取book众多实例的列表。服务中心的注册发现一直是值得推崇的一种方式,但是不适用与网关产品。因为我们的网关是面向众多的其他部门的已有或是异构架构的系统,不应该强求其他系统都使用eureka,这样是有侵入性的设计。

最终架构图

springcloud Zuul动态路由的实现 

要强调的一点是,gateway最终也会部署多个实例,达到分布式的效果,在架构图中没有画出,请大家自行脑补。

本博客的示例使用最后一章架构图为例,带来动态路由的实现方式,会有具体的代码。

动态路由

动态路由需要达到可持久化配置,动态刷新的效果。如架构图所示,不仅要能满足从spring的配置文件properties加载路由信息,还需要从数据库加载我们的配置。另外一点是,路由信息在容器启动时就已经加载进入了内存,我们希望配置完成后,实施发布,动态刷新内存中的路由信息,达到不停机维护路由信息的效果。

zuul–HelloWorldDemo

项目结构

 <groupId>com.sinosoft</groupId>
 <artifactId>zuul-gateway-demo</artifactId>
 <packaging>pom</packaging>
 <version>1.0</version>

 <parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>1.5.2.RELEASE</version>
 </parent>

 <modules>
  <module>gateway</module>
  <module>book</module>
 </modules>

 <dependencyManagement>
  <dependencies>
   <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-dependencies</artifactId>
    <version>Camden.SR6</version>
    <type>pom</type>
    <scope>import</scope>
   </dependency>
  </dependencies>
 </dependencyManagement>

tip:springboot-1.5.2对应的springcloud的版本需要使用Camden.SR6,一开始想专门写这个demo时,只替换了springboot的版本1.4.0->1.5.2,结果启动就报错了,最后发现是版本不兼容的锅。

gateway项目:

启动类:GatewayApplication.java

@EnableZuulProxy
@SpringBootApplication
public class GatewayApplication {

 public static void main(String[] args) {
  SpringApplication.run(GatewayApplication.class, args);
 }

}

配置:application.properties

#配置在配置文件中的路由信息
zuul.routes.books.url=http://localhost:8090
zuul.routes.books.path=/books/**
#不使用注册中心,会带来侵入性
ribbon.eureka.enabled=false
#网关端口
server.port=8080

book项目:

启动类:BookApplication.java

@RestController
@SpringBootApplication
public class BookApplication {

 @RequestMapping(value = "/available")
 public String available() {
  System.out.println("Spring in Action");
  return "Spring in Action";
 }

 @RequestMapping(value = "/checked-out")
 public String checkedOut() {
  return "Spring Boot in Action";
 }

 public static void main(String[] args) {
  SpringApplication.run(BookApplication.class, args);
 }
}

配置类:application.properties

server.port=8090

测试访问:http://localhost:8080/books/available

上述demo是一个简单的静态路由,简单看下源码,zuul是怎么做到转发,路由的。

@Configuration
@EnableConfigurationProperties({ ZuulProperties.class })
@ConditionalOnClass(ZuulServlet.class)
@Import(ServerPropertiesAutoConfiguration.class)
public class ZuulConfiguration {

 @Autowired
 //zuul的配置文件,对应了application.properties中的配置信息
 protected ZuulProperties zuulProperties;

 @Autowired
 protected ServerProperties server;

 @Autowired(required = false)
 private ErrorController errorController;

 @Bean
 public HasFeatures zuulFeature() {
  return HasFeatures.namedFeature("Zuul (Simple)", ZuulConfiguration.class);
 }

 //核心类,路由定位器,最最重要
 @Bean
 @ConditionalOnMissingBean(RouteLocator.class)
 public RouteLocator routeLocator() {
  //默认配置的实现是SimpleRouteLocator.class
  return new SimpleRouteLocator(this.server.getServletPrefix(),
    this.zuulProperties);
 }

 //zuul的控制器,负责处理链路调用
 @Bean
 public ZuulController zuulController() {
  return new ZuulController();
 }

 //MVC HandlerMapping that maps incoming request paths to remote services.
 @Bean
 public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes) {
  ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController());
  mapping.setErrorController(this.errorController);
  return mapping;
 }

 //注册了一个路由刷新监听器,默认实现是ZuulRefreshListener.class,这个是我们动态路由的关键
 @Bean
 public ApplicationListener<ApplicationEvent> zuulRefreshRoutesListener() {
  return new ZuulRefreshListener();
 }

 @Bean
 @ConditionalOnMissingBean(name = "zuulServlet")
 public ServletRegistrationBean zuulServlet() {
  ServletRegistrationBean servlet = new ServletRegistrationBean(new ZuulServlet(),
    this.zuulProperties.getServletPattern());
  // The whole point of exposing this servlet is to provide a route that doesn't
  // buffer requests.
  servlet.addInitParameter("buffer-requests", "false");
  return servlet;
 }

 // pre filters

 @Bean
 public ServletDetectionFilter servletDetectionFilter() {
  return new ServletDetectionFilter();
 }

 @Bean
 public FormBodyWrapperFilter formBodyWrapperFilter() {
  return new FormBodyWrapperFilter();
 }

 @Bean
 public DebugFilter debugFilter() {
  return new DebugFilter();
 }

 @Bean
 public Servlet30WrapperFilter servlet30WrapperFilter() {
  return new Servlet30WrapperFilter();
 }

 // post filters

 @Bean
 public SendResponseFilter sendResponseFilter() {
  return new SendResponseFilter();
 }

 @Bean
 public SendErrorFilter sendErrorFilter() {
  return new SendErrorFilter();
 }

 @Bean
 public SendForwardFilter sendForwardFilter() {
  return new SendForwardFilter();
 }

 @Configuration
 protected static class ZuulFilterConfiguration {

  @Autowired
  private Map<String, ZuulFilter> filters;

  @Bean
  public ZuulFilterInitializer zuulFilterInitializer() {
   return new ZuulFilterInitializer(this.filters);
  }

 }

 //上面提到的路由刷新监听器
 private static class ZuulRefreshListener
   implements ApplicationListener<ApplicationEvent> {

  @Autowired
  private ZuulHandlerMapping zuulHandlerMapping;

  private HeartbeatMonitor heartbeatMonitor = new HeartbeatMonitor();

  @Override
  public void onApplicationEvent(ApplicationEvent event) {
   if (event instanceof ContextRefreshedEvent
     || event instanceof RefreshScopeRefreshedEvent
     || event instanceof RoutesRefreshedEvent) {
    //设置为脏,下一次匹配到路径时,如果发现为脏,则会去刷新路由信息
    this.zuulHandlerMapping.setDirty(true);
   }
   else if (event instanceof HeartbeatEvent) {
    if (this.heartbeatMonitor.update(((HeartbeatEvent) event).getValue())) {
     this.zuulHandlerMapping.setDirty(true);
    }
   }
  }

 }

}

我们要解决动态路由的难题,第一步就得理解路由定位器的作用。

springcloud Zuul动态路由的实现 

很失望,因为从接口关系来看,spring考虑到了路由刷新的需求,但是默认实现的SimpleRouteLocator没有实现RefreshableRouteLocator接口,看来我们只能借鉴DiscoveryClientRouteLocator去改造SimpleRouteLocator使其具备刷新能力。

public interface RefreshableRouteLocator extends RouteLocator {
 void refresh();
}

DiscoveryClientRouteLocator比SimpleRouteLocator多了两个功能,第一是从DiscoveryClient(如Eureka)发现路由信息,之前的架构图已经给大家解释清楚了,我们不想使用eureka这种侵入式的网关模块,所以忽略它,第二是实现了RefreshableRouteLocator接口,能够实现动态刷新。

对SimpleRouteLocator.class的源码加一些注释,方便大家阅读:

public class SimpleRouteLocator implements RouteLocator {

 //配置文件中的路由信息配置
 private ZuulProperties properties;
 //路径正则配置器,即作用于path:/books/**
 private PathMatcher pathMatcher = new AntPathMatcher();

 private String dispatcherServletPath = "/";
 private String zuulServletPath;

 private AtomicReference<Map<String, ZuulRoute>> routes = new AtomicReference<>();

 public SimpleRouteLocator(String servletPath, ZuulProperties properties) {
  this.properties = properties;
  if (servletPath != null && StringUtils.hasText(servletPath)) {
   this.dispatcherServletPath = servletPath;
  }

  this.zuulServletPath = properties.getServletPath();
 }

 //路由定位器和其他组件的交互,是最终把定位的Routes以list的方式提供出去,核心实现
 @Override
 public List<Route> getRoutes() {
  if (this.routes.get() == null) {
   this.routes.set(locateRoutes());
  }
  List<Route> values = new ArrayList<>();
  for (String url : this.routes.get().keySet()) {
   ZuulRoute route = this.routes.get().get(url);
   String path = route.getPath();
   values.add(getRoute(route, path));
  }
  return values;
 }

 @Override
 public Collection<String> getIgnoredPaths() {
  return this.properties.getIgnoredPatterns();
 }

 //这个方法在网关产品中也很重要,可以根据实际路径匹配到Route来进行业务逻辑的操作,进行一些加工
 @Override
 public Route getMatchingRoute(final String path) {

  if (log.isDebugEnabled()) {
   log.debug("Finding route for path: " + path);
  }

  if (this.routes.get() == null) {
   this.routes.set(locateRoutes());
  }

  if (log.isDebugEnabled()) {
   log.debug("servletPath=" + this.dispatcherServletPath);
   log.debug("zuulServletPath=" + this.zuulServletPath);
   log.debug("RequestUtils.isDispatcherServletRequest()="
     + RequestUtils.isDispatcherServletRequest());
   log.debug("RequestUtils.isZuulServletRequest()="
     + RequestUtils.isZuulServletRequest());
  }

  String adjustedPath = adjustPath(path);

  ZuulRoute route = null;
  if (!matchesIgnoredPatterns(adjustedPath)) {
   for (Entry<String, ZuulRoute> entry : this.routes.get().entrySet()) {
    String pattern = entry.getKey();
    log.debug("Matching pattern:" + pattern);
    if (this.pathMatcher.match(pattern, adjustedPath)) {
     route = entry.getValue();
     break;
    }
   }
  }
  if (log.isDebugEnabled()) {
   log.debug("route matched=" + route);
  }

  return getRoute(route, adjustedPath);

 }

 private Route getRoute(ZuulRoute route, String path) {
  if (route == null) {
   return null;
  }
  String targetPath = path;
  String prefix = this.properties.getPrefix();
  if (path.startsWith(prefix) && this.properties.isStripPrefix()) {
   targetPath = path.substring(prefix.length());
  }
  if (route.isStripPrefix()) {
   int index = route.getPath().indexOf("*") - 1;
   if (index > 0) {
    String routePrefix = route.getPath().substring(0, index);
    targetPath = targetPath.replaceFirst(routePrefix, "");
    prefix = prefix + routePrefix;
   }
  }
  Boolean retryable = this.properties.getRetryable();
  if (route.getRetryable() != null) {
   retryable = route.getRetryable();
  }
  return new Route(route.getId(), targetPath, route.getLocation(), prefix,
    retryable,
    route.isCustomSensitiveHeaders() ? route.getSensitiveHeaders() : null);
 }

 //注意这个类并没有实现refresh接口,但是却提供了一个protected级别的方法,旨在让子类不需要重复维护一个private AtomicReference<Map<String, ZuulRoute>> routes = new AtomicReference<>();也可以达到刷新的效果
 protected void doRefresh() {
  this.routes.set(locateRoutes());
 }


 //具体就是在这儿定位路由信息的,我们之后从数据库加载路由信息,主要也是从这儿改写
 /**
  * Compute a map of path pattern to route. The default is just a static map from the
  * {@link ZuulProperties}, but subclasses can add dynamic calculations.
  */
 protected Map<String, ZuulRoute> locateRoutes() {
  LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<String, ZuulRoute>();
  for (ZuulRoute route : this.properties.getRoutes().values()) {
   routesMap.put(route.getPath(), route);
  }
  return routesMap;
 }

 protected boolean matchesIgnoredPatterns(String path) {
  for (String pattern : this.properties.getIgnoredPatterns()) {
   log.debug("Matching ignored pattern:" + pattern);
   if (this.pathMatcher.match(pattern, path)) {
    log.debug("Path " + path + " matches ignored pattern " + pattern);
    return true;
   }
  }
  return false;
 }

 private String adjustPath(final String path) {
  String adjustedPath = path;

  if (RequestUtils.isDispatcherServletRequest()
    && StringUtils.hasText(this.dispatcherServletPath)) {
   if (!this.dispatcherServletPath.equals("/")) {
    adjustedPath = path.substring(this.dispatcherServletPath.length());
    log.debug("Stripped dispatcherServletPath");
   }
  }
  else if (RequestUtils.isZuulServletRequest()) {
   if (StringUtils.hasText(this.zuulServletPath)
     && !this.zuulServletPath.equals("/")) {
    adjustedPath = path.substring(this.zuulServletPath.length());
    log.debug("Stripped zuulServletPath");
   }
  }
  else {
   // do nothing
  }

  log.debug("adjustedPath=" + path);
  return adjustedPath;
 }

}

重写过后的自定义路由定位器如下:

public class CustomRouteLocator extends SimpleRouteLocator implements RefreshableRouteLocator{

 public final static Logger logger = LoggerFactory.getLogger(CustomRouteLocator.class);

 private JdbcTemplate jdbcTemplate;

 private ZuulProperties properties;

 public void setJdbcTemplate(JdbcTemplate jdbcTemplate){
  this.jdbcTemplate = jdbcTemplate;
 }

 public CustomRouteLocator(String servletPath, ZuulProperties properties) {
  super(servletPath, properties);
  this.properties = properties;
  logger.info("servletPath:{}",servletPath);
 }

 //父类已经提供了这个方法,这里写出来只是为了说明这一个方法很重要!!!
// @Override
// protected void doRefresh() {
//  super.doRefresh();
// }


 @Override
 public void refresh() {
  doRefresh();
 }

 @Override
 protected Map<String, ZuulRoute> locateRoutes() {
  LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<String, ZuulRoute>();
  //从application.properties中加载路由信息
  routesMap.putAll(super.locateRoutes());
  //从db中加载路由信息
  routesMap.putAll(locateRoutesFromDB());
  //优化一下配置
  LinkedHashMap<String, ZuulRoute> values = new LinkedHashMap<>();
  for (Map.Entry<String, ZuulRoute> entry : routesMap.entrySet()) {
   String path = entry.getKey();
   // Prepend with slash if not already present.
   if (!path.startsWith("/")) {
    path = "/" + path;
   }
   if (StringUtils.hasText(this.properties.getPrefix())) {
    path = this.properties.getPrefix() + path;
    if (!path.startsWith("/")) {
     path = "/" + path;
    }
   }
   values.put(path, entry.getValue());
  }
  return values;
 }

 private Map<String, ZuulRoute> locateRoutesFromDB(){
  Map<String, ZuulRoute> routes = new LinkedHashMap<>();
  List<ZuulRouteVO> results = jdbcTemplate.query("select * from gateway_api_define where enabled = true ",new BeanPropertyRowMapper<>(ZuulRouteVO.class));
  for (ZuulRouteVO result : results) {
   if(org.apache.commons.lang3.StringUtils.isBlank(result.getPath()) || org.apache.commons.lang3.StringUtils.isBlank(result.getUrl()) ){
    continue;
   }
   ZuulRoute zuulRoute = new ZuulRoute();
   try {
    org.springframework.beans.BeanUtils.copyProperties(result,zuulRoute);
   } catch (Exception e) {
    logger.error("=============load zuul route info from db with error==============",e);
   }
   routes.put(zuulRoute.getPath(),zuulRoute);
  }
  return routes;
 }

 public static class ZuulRouteVO {

  /**
   * The ID of the route (the same as its map key by default).
   */
  private String id;

  /**
   * The path (pattern) for the route, e.g. /foo/**.
   */
  private String path;

  /**
   * The service ID (if any) to map to this route. You can specify a physical URL or
   * a service, but not both.
   */
  private String serviceId;

  /**
   * A full physical URL to map to the route. An alternative is to use a service ID
   * and service discovery to find the physical address.
   */
  private String url;

  /**
   * Flag to determine whether the prefix for this route (the path, minus pattern
   * patcher) should be stripped before forwarding.
   */
  private boolean stripPrefix = true;

  /**
   * Flag to indicate that this route should be retryable (if supported). Generally
   * retry requires a service ID and ribbon.
   */
  private Boolean retryable;

  private Boolean enabled;

  public String getId() {
   return id;
  }

  public void setId(String id) {
   this.id = id;
  }

  public String getPath() {
   return path;
  }

  public void setPath(String path) {
   this.path = path;
  }

  public String getServiceId() {
   return serviceId;
  }

  public void setServiceId(String serviceId) {
   this.serviceId = serviceId;
  }

  public String getUrl() {
   return url;
  }

  public void setUrl(String url) {
   this.url = url;
  }

  public boolean isStripPrefix() {
   return stripPrefix;
  }

  public void setStripPrefix(boolean stripPrefix) {
   this.stripPrefix = stripPrefix;
  }

  public Boolean getRetryable() {
   return retryable;
  }

  public void setRetryable(Boolean retryable) {
   this.retryable = retryable;
  }

  public Boolean getEnabled() {
   return enabled;
  }

  public void setEnabled(Boolean enabled) {
   this.enabled = enabled;
  }
 }
}

配置这个自定义的路由定位器:

@Configuration
public class CustomZuulConfig {

 @Autowired
 ZuulProperties zuulProperties;
 @Autowired
 ServerProperties server;
 @Autowired
 JdbcTemplate jdbcTemplate;

 @Bean
 public CustomRouteLocator routeLocator() {
  CustomRouteLocator routeLocator = new CustomRouteLocator(this.server.getServletPrefix(), this.zuulProperties);
  routeLocator.setJdbcTemplate(jdbcTemplate);
  return routeLocator;
 }

}

现在容器启动时,就可以从数据库和配置文件中一起加载路由信息了,离动态路由还差最后一步,就是实时刷新,前面已经说过了,默认的ZuulConfigure已经配置了事件监听器,我们只需要发送一个事件就可以实现刷新了。

public class RefreshRouteService {

 @Autowired
 ApplicationEventPublisher publisher;

 @Autowired
 RouteLocator routeLocator;

 public void refreshRoute() {
  RoutesRefreshedEvent routesRefreshedEvent = new RoutesRefreshedEvent(routeLocator);
  publisher.publishEvent(routesRefreshedEvent);
 }

}

具体的刷新流程其实就是从数据库重新加载了一遍,有人可能会问,为什么不自己是手动重新加载Locator.dorefresh?非要用事件去刷新。这牵扯到内部的zuul内部组件的工作流程,不仅仅是Locator本身的一个变量,具体想要了解的还得去看源码。

到这儿我们就实现了动态路由了,所以的实例代码和建表语句我会放到github上,下载的时候记得给我star QAQ !!!

链接:https://github.com/lexburner/zuul-gateway-demo

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持码农之家。

以上就是本次给大家分享的关于java的全部知识点内容总结,大家还可以在下方相关文章里找到相关文章进一步学习,感谢大家的阅读和支持。

您可能感兴趣的文章:

  • SpringCloud中Ribbon的使用方法
  • SpringCloud实现简单的微服务架构方法
  • SpringCloud实现SSO单点登录的用法及实例
  • SpringCloud Zuul实现动态路由 相关电子书
    学习笔记
    网友NO.944581

    详解SpringCloud Zuul过滤器返回值拦截

    Zuul作为网关服务,是其他各服务对外中转站,通过Zuul进行请求转发。这就涉及到部分数据是不能原封返回的,比如服务之间通信的凭证,用户的加密信息等等。 举个例子,用户服务提供一个登录接口,用户名密码正确后返回一个Token,此Token作为用户服务的通行证,那么用户登录成功后返回的Token就需要进行加密或者防止篡改处理。在到达用户服务其他接口前,就需要对Token进行校验,非法的Token就不需要转发到用户服务中了,直接在网关层返回信息即可。 要修改服务返回的信息,需要使用的是Zuul的过滤器。使用时只需要继承ZuulFilter,实现必要的方法即可。 Zuul提供默认的四种过滤器类型,通过filterType方法进行标识 pre:可以在请求被路由之前调用 route:在路由请求时候被调用 post:在route和error过滤器之后被调用 error:处理请求时发生错误时被调用……

    网友NO.730591

    springcloud 中 zuul 修改请求参数信息的方法

    Zuul是Netflix出品的一个基于JVM路由和服务端的负载均衡器. Zuul功能: 认证 压力测试 金丝雀测试 动态路由 负载削减 安全 静态响应处理 主动/主动交换管理 Zuul的规则引擎允许通过任何JVM语言来编写规则和过滤器, 支持基于Java和Groovy的构建。 配置属性 zuul.max.host.connections 已经被两个新的配置属性替代, zuul.host.maxTotalConnections (总连接数)和 zuul.host.maxPerRouteConnections,(每个路由连接数) 默认值分别是200和20. 一. 为什么要用到这个 在基于 springcloud 构建的微服务系统中,通常使用网关zuul来进行一些用户验证等过滤的操作,比如 用户在 header 或者 url 参数中存放了 token ,网关层需要 用该 token 查出用户 的 userId ,并存放于 request 中,以便后续微服务可以直接使用而避免再去用 token 查询。 二.基础知识 在 zuul 中最大的用法的除了路由之外,就是过滤器了,自……

    网友NO.919620

    SpringCloud实战之Zuul网关服务

    为什么需要网关呢? 我们知道我们要进入一个服务本身,很明显我们没有特别好的办法,直接输入IP地址+端口号,我们知道这样的做法很糟糕的,这样的做法大有问题,首先暴露了我们实体机器的IP地址,别人一看你的IP地址就知道服务部署在哪里,让别人很方便的进行攻击操作。 第二,我们这么多服务,我们是不是要挨个调用它呀,我们这里假设做了个权限认证,我们每一个客户访问的都是跑在不同机器上的不同的JVM上的服务程序,我们每一个服务都需要一个服务认证,这样做烦不烦呀,明显是很烦的。 那么我们这时候面临着这两个及其总要的问题,这时我们就需要一个办法解决它们。首先,我们看IP地址的暴露和IP地址写死后带来的单点问题,我是不是对这么服务本身我也要动态的维护它服务的列表呀,我需要调用这服务本身,是不是也要一个负……

    网友NO.431527

    SpringCloud Zuul在何种情况下使用Hystrix及问题小结

    首先,引入 spring-cloud-starter-zuul 之后会间接引入: hystrix依赖已经引入,那么何种情况下使用hystrix呢? 在Zuul的自动配置类ZuulServerAutoConfiguration和ZuulProxyAutoConfiguration中总共会向Spring容器注入3个Zuul的RouteFilter,分别是 •SimpleHostRoutingFilter 简单路由,通过HttpClient向预定的URL发送请求 生效条件: RequestContext.getCurrentContext().getRouteHost() != null ​ RequestContext.getCurrentContext().sendZuulResponse() 1、RequestContext中的routeHost不为空,routeHost就是URL,即使用URL直连 2、RequestContext中的sendZuulResponse为true,即是否将response发送给客户端,默认为true •RibbonRoutingFilter 使用Ribbon、Hystrix和可插入的http客户端发送请求 生效条件: (RequestContext.getRouteHost() == null RequestContext.get(SERVICE_ID_KEY) != null ​ RequestContext.sendZuulResponse()) 1、RequestContext中的routeHost为空,即URL为空 2、RequestContext中的servi……

    <
    1
    >

    Copyright 2018-2020 www.xz577.com 码农之家

    版权投诉 / 书籍推广 / 赞助:520161757@qq.com