基于Zuul、Redis和REST API实现动态路由的持久化及容错能力

JackieTang SpringForAll社区 今天

原文链接:https://dzone.com/articles/persistent-and-fault-tolerant-dynamic-routes-using

作者: Vikas Anand

译者:helloworldtang

学习如何使用REST API在Zuul服务器上注册动态路由,并借助Redis使您的动态路由具备容错能力。

目标

我们将使用Zuul、Spring boot Actuator、Redis创建一个应用程序,它提供REST API来创建动态路由,查看动态路由,删除不需要的路由,从缓存和数据库中恢复以前创建的所有动态路由的功能。虽然这个应用程序展示了更多关于动态路由的信息,但是它也展示了使用spring boot-starer-redis与Redis进行交互的方式。假设Redis服务器在本地的6379端口上运行。此外,它还展示了Spring boot actuator公开的对这个应用程序很有帮助的一些URL。

步骤

我们将创建一个基于maven的Spring Boot项目。

项目结构:

项目中的文件如下所示:

  • pom.xml

 
  1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

  2.    <modelVersion>4.0.0</modelVersion>

  3.    <groupId>com.cfeindia.examples.zuul</groupId>

  4.    <artifactId>zuul-route-redis</artifactId>

  5.    <version>0.0.1-SNAPSHOT</version>

  6.    <parent>

  7.        <groupId>org.springframework.boot</groupId>

  8.        <artifactId>spring-boot-starter-parent</artifactId>

  9.        <version>1.5.13.RELEASE</version>

  10.        <relativePath />

  11.        <!-- lookup parent from repository -->

  12.    </parent>

  13.    <properties>

  14.        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

  15.        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

  16.        <java.version>1.8</java.version>

  17.        <spring-cloud.version>Edgware.SR3</spring-cloud.version>

  18.        <maven.compiler.source>1.8</maven.compiler.source>

  19.        <maven.compiler.target>1.8</maven.compiler.target>

  20.    </properties>

  21.    <repositories>

  22.        <repository>

  23.            <id>spy</id>

  24.            <name>Spy Repository</name>

  25.            <layout>default</layout>

  26.            <url>http://files.couchbase.com/maven2/</url>

  27.            <snapshots>

  28.                <enabled>false</enabled>

  29.            </snapshots>

  30.        </repository>

  31.    </repositories>

  32.    <dependencyManagement>

  33.        <dependencies>

  34.            <dependency>

  35.                <groupId>org.springframework.cloud</groupId>

  36.                <artifactId>spring-cloud-dependencies</artifactId>

  37.                <version>${spring-cloud.version}</version>

  38.                <type>pom</type>

  39.                <scope>import</scope>

  40.            </dependency>

  41.        </dependencies>

  42.    </dependencyManagement>

  43.    <dependencies>

  44.        <dependency>

  45.            <groupId>org.springframework.cloud</groupId>

  46.            <artifactId>spring-cloud-starter-zuul</artifactId>

  47.        </dependency>

  48.        <dependency>

  49.            <artifactId>spring-boot-starter-actuator</artifactId>

  50.            <exclusions>

  51.                <exclusion>

  52.                    <groupId>org.springframework.boot</groupId>

  53.                    <artifactId>spring-boot-starter-logging</artifactId>

  54.                </exclusion>

  55.            </exclusions>

  56.        </dependency>

  57.        <dependency>

  58.            <groupId>org.springframework.boot</groupId>

  59.            <artifactId>spring-boot-starter-log4j2</artifactId>

  60.        </dependency>

  61.        <dependency>

  62.            <groupId>org.springframework.boot</groupId>

  63.            <artifactId>spring-boot-starter-data-redis</artifactId>

  64.        </dependency>

  65.        <dependency>

  66.            <groupId>org.springframework.boot</groupId>

  67.            <artifactId>spring-boot-starter-web</artifactId>

  68.        </dependency>

  69.    </dependencies>

  70.    <build>

  71.        <plugins>

  72.            <plugin>

  73.                <artifactId>maven-compiler-plugin</artifactId>

  74.                <configuration>

  75.                    <source>1.8</source>

  76.                    <target>1.8</target>

  77.                </configuration>

  78.            </plugin>

  79.            <plugin>

  80.                <artifactId>maven-shade-plugin</artifactId>

  81.                <executions>

  82.                    <execution>

  83.                        <phase>package</phase>

  84.                        <goals>

  85.                            <goal>shade</goal>

  86.                        </goals>

  87.                        <configuration>

  88. <finalName>${project.artifactId}-${project.version}</finalName>

  89.                            <transformers>

  90.                                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

  91.                                    <resource>META-INF/spring.handlers</resource>

  92.                                </transformer>

  93.                                <transformer implementation="org.springframework.boot.maven.PropertiesMergingResourceTransformer">

  94.                                    <resource>META-INF/spring.factories</resource>

  95.                                </transformer>

  96.                                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

  97.                                    <resource>META-INF/spring.schemas</resource>

  98.                                </transformer>

  99.                                <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />

  100.                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">

  101.                                    <mainClass>com.mettl.gatewayservice.application.SpringBootWebApplication</mainClass>

  102.                                </transformer>

  103.                                <transformer implementation="com.github.edwgiz.mavenShadePlugin.log4j2CacheTransformer.PluginsCacheFileTransformer" />

  104.                            </transformers>

  105.                        </configuration>

  106.                    </execution>

  107.                </executions>

  108.                <dependencies>

  109.                    <dependency>

  110.                        <groupId>com.github.edwgiz</groupId>

  111.                        <artifactId>maven-shade-plugin.log4j2-cachefile-transformer</artifactId>

  112.                        <version>2.6.1</version>

  113.                    </dependency>

  114.                </dependencies>

  115.            </plugin>

  116.        </plugins>

  117.    </build>

  118. </project>

  • Spring Boot应用程序启动类,添加了Zuul、Redis等特性必需的注解:

 
  1. package com.cfeindia.examples.zuul.application;

  2. import org.springframework.boot.autoconfigure.SpringBootApplication;

  3. import org.springframework.boot.SpringApplication;

  4. import org.springframework.boot.autoconfigure.EnableAutoConfiguration;

  5. import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;

  6. import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

  7. import org.springframework.context.annotation.ComponentScan;

  8. import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;

  9. @SpringBootApplication

  10. @EnableAutoConfiguration(exclude = {

  11. RabbitAutoConfiguration.class

  12. })

  13. @EnableZuulProxy

  14. @EnableRedisRepositories("com.cfeindia.examples.zuul")

  15. @ComponentScan("com.cfeindia.examples.zuul")

  16. public class SpringBootWebApplication {

  17. public static void main(String[] args) {

  18.  SpringApplication.run(SpringBootWebApplication.class, args);

  19. }

  20. }

  • 应用程序配置类,定义bean

 
  1. package com.cfeindia.examples.zuul.config;

  2. import org.springframework.context.annotation.Bean;

  3.  

  4. import org.springframework.context.annotation.Configuration;

  5. import org.springframework.data.redis.connection.RedisConnectionFactory;

  6. import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;

  7. import org.springframework.data.redis.core.RedisTemplate;

  8. import com.cfeindia.examples.zuul.model.DynamicRoute;

  9. import redis.clients.jedis.JedisPoolConfig;

  10. @Configuration

  11. public class AppConfig {

  12. private static final int REDIS_PORT = 6379;

  13. private static final String REDIS_HOST_NAME = "localhost";

  14.  

  15. @Bean

  16. public RedisConnectionFactory redisConnectionFactory() {

  17.  JedisPoolConfig poolConfig = new JedisPoolConfig();

  18.  //Change the configuration as per requirement

  19.  poolConfig.setMaxTotal(128);

  20.  poolConfig.setTestOnBorrow(true);

  21.  poolConfig.setTestOnReturn(true);

  22.  poolConfig.setMinIdle(5);

  23.  poolConfig.setMaxIdle(128);

  24.  JedisConnectionFactory connectionFactory = new JedisConnectionFactory(poolConfig);

  25.  connectionFactory.setUsePool(true);

  26.  connectionFactory.setHostName(REDIS_HOST_NAME);

  27.  connectionFactory.setPort(REDIS_PORT);

  28.  return connectionFactory;

  29. }

  30.  

  31. @Bean

  32. public RedisTemplate < String, DynamicRoute > redisTemplate() {

  33.  RedisTemplate < String, DynamicRoute > redisTemplate = new RedisTemplate < > ();

  34.  redisTemplate.setConnectionFactory(redisConnectionFactory());

  35.  redisTemplate.setEnableTransactionSupport(false);

  36.  return redisTemplate;

  37. }

  38. }

  • controller类,提供了动态路由注册和删除功能的API:

 
  1. package com.cfeindia.examples.zuul.controller;

  2. import org.slf4j.Logger;

  3. import org.slf4j.LoggerFactory;

  4. import org.springframework.beans.factory.annotation.Autowired;

  5. import org.springframework.web.bind.annotation.RequestBody;

  6. import org.springframework.web.bind.annotation.RequestMapping;

  7. import org.springframework.web.bind.annotation.RequestMethod;

  8. import org.springframework.web.bind.annotation.ResponseBody;

  9. import org.springframework.web.bind.annotation.RestController;

  10. import com.cfeindia.examples.zuul.model.DeleteRouteRequest;

  11. import com.cfeindia.examples.zuul.model.DynamicRoute;

  12. import com.cfeindia.examples.zuul.model.DynamicRouteResponse;

  13. import com.cfeindia.examples.zuul.service.ZuulDynamicRoutingService;

  14.  

  15. @RestController

  16. public class DynamicRouteController {

  17.  

  18. private static final Logger logger = LoggerFactory.getLogger(DynamicRouteController.class);

  19.  

  20. @Autowired

  21. ZuulDynamicRoutingService zuulDynamicRoutingService;

  22.  

  23. @RequestMapping(value = "/proxyurl", method = RequestMethod.POST)

  24. public @ResponseBody DynamicRouteResponse getProxyURL(@RequestBody DynamicRoute dynamicRoute) {

  25.  logger.debug("request received to add {}", dynamicRoute);

  26.  DynamicRouteResponse dynamicRouteResponse = zuulDynamicRoutingService.addDynamicRoute(dynamicRoute);

  27.  logger.debug("response sent {}", dynamicRouteResponse);

  28.  return dynamicRouteResponse;

  29. }

  30.  

  31. @RequestMapping(value = "/proxyurl", method = RequestMethod.DELETE)

  32. public @ResponseBody Boolean deleteProxyURL(@RequestBody DeleteRouteRequest deleteRouteRequest) {

  33.  logger.debug("request received to delete {}", deleteRouteRequest);

  34.  Boolean response = zuulDynamicRoutingService.removeDynamicRoute(deleteRouteRequest.getRequestURIUniqueKey());

  35.  logger.debug("response sent for delete {}", response);

  36.  return response;

  37. }

  38. }

  • Rest API请求和响应需要的POJO

DynamicRoute.java:

存储到Redis时用到的动态路由请求类。检查@RedisHash和@Id注解,这是保存、检索和删除动态路由所必需的。 它也被用于Rest API请求中,用来将传入的Json转换成动态路由对象。

 
  1. package com.cfeindia.examples.zuul.model;

  2. import java.io.Serializable;

  3. import org.springframework.data.annotation.Id;

  4. import org.springframework.data.redis.core.RedisHash;

  5.  

  6. /**

  7. * Dynamic route request object saved in Redis

  8. * It is also used in Rest API request to convert Json in Object

  9. * @author vikasanand

  10. *

  11. */

  12. @RedisHash("Gateway_Service_Dynamic_Route")

  13. public class DynamicRoute implements Serializable {

  14.  

  15. private static final long serialVersionUID = -6719150427066586659 L;

  16.  

  17. /**

  18.  * This can be a unique key different for each route registration. It should be different

  19.  * for each requestURI to be forwarded.

  20.  * i.e. asad121-sadas-hjjhhd

  21.  */

  22. @Id

  23. public String requestURIUniqueKey;

  24. /**

  25.  * This can be of format "/api1"

  26.  * It should be sub-path URI which needs to forwarded to different proxy

  27.  */

  28. private String requestURI;

  29.  

  30. /**

  31.  * Target Host name or IP

  32.  * i.e. https://adomain.com

  33.  */

  34. private String targetURLHost;

  35.  

  36. /**

  37.  * Target Port to forward

  38.  * i.e. 80

  39.  */

  40. private int targetURLPort;

  41.  

  42. /**

  43.  * Target URI to forward

  44.  * /proxy-api1

  45.  */

  46. private String targetURIPath;

  47.  

  48. public String getRequestURIUniqueKey() {

  49.  return requestURIUniqueKey;

  50. }

  51.  

  52. public void setRequestURIUniqueKey(String requestURIUniqueKey) {

  53.  this.requestURIUniqueKey = requestURIUniqueKey;

  54. }

  55.  

  56. public String getRequestURI() {

  57.  return requestURI;

  58. }

  59.  

  60. public void setRequestURI(String requestURI) {

  61.  this.requestURI = requestURI;

  62. }

  63.  

  64. public String getTargetURLHost() {

  65.  return targetURLHost;

  66. }

  67.  

  68. public void setTargetURLHost(String targetURLHost) {

  69.  this.targetURLHost = targetURLHost;

  70. }

  71.  

  72. public int getTargetURLPort() {

  73.  return targetURLPort;

  74. }

  75.  

  76. public void setTargetURLPort(int targetURLPort) {

  77.  this.targetURLPort = targetURLPort;

  78. }

  79.  

  80. public String getTargetURIPath() {

  81.  return targetURIPath;

  82. }

  83.  

  84. public void setTargetURIPath(String targetURIPath) {

  85.  this.targetURIPath = targetURIPath;

  86. }

  87.  

  88. @Override

  89. public String toString() {

  90.  StringBuilder builder = new StringBuilder();

  91.  builder.append("DynamicRoute [requestURIUniqueKey=");

  92.  builder.append(requestURIUniqueKey);

  93.  builder.append(", requestURI=");

  94.  builder.append(requestURI);

  95.  builder.append(", targetURLHost=");

  96.  builder.append(targetURLHost);

  97.  builder.append(", targetURLPort=");

  98.  builder.append(targetURLPort);

  99.  builder.append(", targetURIPath=");

  100.  builder.append(targetURIPath);

  101.  builder.append("]");

  102.  return builder.toString();

  103. }

  104. }

其它POJO:

 
  1. package com.cfeindia.examples.zuul.model;

  2. /**

  3. * Response of dynamic route creation and saving

  4. * @author vikasanand

  5. *

  6. */

  7. public class DynamicRouteResponse {

  8. /**

  9.  * statusCode is 0 for success.

  10.  * if statusCode is not 0 then check for errorMessage for details.

  11.  */

  12. private int statusCode;

  13.  

  14. private String errorMessage;

  15.  

  16. public DynamicRouteResponse() {

  17.  super();

  18. }

  19.  

  20. public DynamicRouteResponse(int statusCode, String errorMessage) {

  21.  super();

  22.  this.statusCode = statusCode;

  23.  this.errorMessage = errorMessage;

  24. }

  25.  

  26. public int getStatusCode() {

  27.  return statusCode;

  28. }

  29.  

  30. public String getErrorMessage() {

  31.  return errorMessage;

  32. }

  33.  

  34. @Override

  35. public String toString() {

  36.  StringBuilder builder = new StringBuilder();

  37.  builder.append("DynamicRouteResponse [statusCode=");

  38.  builder.append(statusCode);

  39.  builder.append(", errorMessage=");

  40.  builder.append(errorMessage);

  41.  builder.append("]");

  42.  return builder.toString();

  43. }

  44. }

 
  1. package com.cfeindia.examples.zuul.model;

  2. public class DeleteRouteRequest {

  3.  

  4. public String requestURIUniqueKey;

  5.  

  6. public String getRequestURIUniqueKey() {

  7.  return requestURIUniqueKey;

  8. }

  9.  

  10. public void setRequestURIUniqueKey(String requestURIUniqueKey) {

  11.  this.requestURIUniqueKey = requestURIUniqueKey;

  12. }

  13. }

  • 与Redis服务器交互的DAO或repository类。使用Spring Data Redis库使得CRUD操作变得非常简单。我们只需要创建一个接口来扩展接口CrudRepository,并添加@Repository注解。请设置DynamicRouteRedisRepository所继承父类CrudRepository的泛型为DynamicRoute和String,第一个是值的类型,第二个是键的类型。

 
  1. package com.cfeindia.examples.zuul.dao;

  2.  

  3. import org.springframework.data.repository.CrudRepository;

  4. import org.springframework.stereotype.Repository;

  5. import com.cfeindia.examples.zuul.model.DynamicRoute;

  6.  

  7. @Repository

  8. public interface DynamicRouteRedisRepository extends CrudRepository < DynamicRoute, String > {}

  • Zuul使用的Filter类。这些类都是默认类。

 
  1. package com.cfeindia.examples.zuul.filter;

  2.  

  3. import javax.servlet.http.HttpServletRequest;

  4. import org.slf4j.Logger;

  5. import org.slf4j.LoggerFactory;

  6. import org.springframework.stereotype.Component;

  7. import org.springframework.util.StringUtils;

  8. import org.springframework.web.util.UrlPathHelper;

  9. import com.netflix.zuul.ZuulFilter;

  10. import com.netflix.zuul.context.RequestContext;

  11. import com.netflix.zuul.http.HttpServletRequestWrapper;

  12.  

  13. @Component

  14. public class PreFilter extends ZuulFilter {

  15.  

  16. private static Logger log = LoggerFactory.getLogger(PreFilter.class);

  17. private UrlPathHelper urlPathHelper = new UrlPathHelper();

  18.  

  19. @Override

  20. public String filterType() {

  21.  return "pre";

  22. }

  23.  

  24. @Override

  25. public int filterOrder() {

  26.  return 1;

  27. }

  28.  

  29. @Override

  30. public boolean shouldFilter() {

  31.  RequestContext ctx = RequestContext.getCurrentContext();

  32.  String requestURL = ctx.getRequest().getRequestURL().toString();

  33.  return !(requestURL.contains("proxyurl") || requestURL.contains("/admin/"));

  34. }

  35.  

  36. @Override

  37. public Object run() {

  38.  RequestContext ctx = RequestContext.getCurrentContext();

  39.  HttpServletRequest request = ctx.getRequest();

  40.  log.info("PreFilter: " +

  41.   String.format("%s request to %s", request.getMethod(), request.getRequestURL().toString()));

  42.  return null;

  43. }

  44. }

 
  1. package com.cfeindia.examples.zuul.filter;

  2.  

  3. import javax.servlet.http.HttpServletRequest;

  4. import org.slf4j.Logger;

  5. import org.slf4j.LoggerFactory;

  6. import org.springframework.stereotype.Component;

  7. import com.netflix.zuul.ZuulFilter;

  8. import com.netflix.zuul.context.RequestContext;

  9.  

  10. @Component

  11. public class RouteFilter extends ZuulFilter {

  12.  

  13. private static Logger log = LoggerFactory.getLogger(RouteFilter.class);

  14.  

  15. @Override

  16. public String filterType() {

  17.  return "route";

  18. }

  19.  

  20. @Override

  21. public int filterOrder() {

  22.  return 1;

  23. }

  24.  

  25. @Override

  26. public boolean shouldFilter() {

  27.  RequestContext ctx = RequestContext.getCurrentContext();

  28.  String requestURL = ctx.getRequest().getRequestURL().toString();

  29.  return !(requestURL.contains("proxyurl") || requestURL.contains("/admin/"));

  30. }

  31.  

  32. @Override

  33. public Object run() {

  34.  RequestContext ctx = RequestContext.getCurrentContext();

  35.  HttpServletRequest request = ctx.getRequest();

  36.  log.info("RouteFilter: " + String.format("%s request to %s", request.getMethod(), request.getRequestURL().toString()));

  37.  return null;

  38. }

  39.  

  40. }

 
  1. package com.cfeindia.examples.zuul.filter;

  2.  

  3. import javax.servlet.http.HttpServletResponse;

  4. import org.slf4j.Logger;

  5. import org.slf4j.LoggerFactory;

  6. import org.springframework.stereotype.Component;

  7. import com.netflix.zuul.ZuulFilter;

  8. import com.netflix.zuul.context.RequestContext;

  9.  

  10. @Component

  11. public class PostFilter extends ZuulFilter {

  12.  

  13. private static Logger log = LoggerFactory.getLogger(PostFilter.class);

  14.  

  15. @Override

  16. public String filterType() {

  17.  return "post";

  18. }

  19.  

  20. @Override

  21. public int filterOrder() {

  22.  return 1;

  23. }

  24.  

  25. @Override

  26. public boolean shouldFilter() {

  27.  RequestContext ctx = RequestContext.getCurrentContext();

  28.  String requestURL = ctx.getRequest().getRequestURL().toString();

  29.  return !(requestURL.contains("proxyurl") || requestURL.contains("/admin/"));

  30. }

  31.  

  32. @Override

  33. public Object run() {

  34.  HttpServletResponse response = RequestContext.getCurrentContext().getResponse();

  35.  log.info("PostFilter: " + String.format("response is %s", response));

  36.  return null;

  37. }

  38. }

 
  1. package com.cfeindia.examples.zuul.filter;

  2.  

  3. import javax.servlet.http.HttpServletResponse;

  4. import org.slf4j.Logger;

  5. import org.slf4j.LoggerFactory;

  6. import org.springframework.stereotype.Component;

  7. import com.netflix.zuul.ZuulFilter;

  8. import com.netflix.zuul.context.RequestContext;

  9.  

  10. @Component

  11. public class ErrorFilter extends ZuulFilter {

  12.  

  13. private static Logger log = LoggerFactory.getLogger(PostFilter.class);

  14.  

  15. @Override

  16. public String filterType() {

  17.  return "error";

  18. }

  19.  

  20. @Override

  21. public int filterOrder() {

  22.  return 1;

  23. }

  24.  

  25. @Override

  26. public boolean shouldFilter() {

  27.  RequestContext ctx = RequestContext.getCurrentContext();

  28.  String requestURL = ctx.getRequest().getRequestURL().toString();

  29.  return !(requestURL.contains("proxyurl") || requestURL.contains("/admin/"));

  30. }

  31.  

  32. @Override

  33. public Object run() {

  34.  HttpServletResponse response = RequestContext.getCurrentContext().getResponse();

  35.  log.info("ErrorFilter: " + String.format("response is %s", response));

  36.  return null;

  37. }

  38. }

在Spring Boot的application.yml文件中配置Zuul和actuator:

 
  1. server:

  2.  port: ${appPort:8071}

  3. # Actuator endpoint path (/admin/info, /admin/health, ...)

  4. server.servlet-path: /

  5. management.context-path: /admin

  6. management.security.enabled: false

  7. endpoints.health.sensitive: false

  8. # ribbon.eureka.enabled: false

  9. zuul:

  10.  ignoredPatterns: /**/admin/**, /proxyurl

  11.  routes:

  12.    zuulDemo1:

  13.      path: /**

  14.      url: http://localhost/admin/health

  15.      # stripPrefix set to true if context path is set to /

  16.      stripPrefix: true

现在,Service对于不同的功能有不同的方法。创建ZuulDynamicRoutingService类并引入必要依赖,如下所示:

 
  1. package com.cfeindia.examples.zuul.service;

  2.  

  3. import java.util.HashSet;

  4. import javax.annotation.PostConstruct;

  5. import org.slf4j.Logger;

  6. import org.slf4j.LoggerFactory;

  7. import org.springframework.beans.factory.annotation.Autowired;

  8. import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;

  9. import org.springframework.cloud.netflix.zuul.filters.ZuulProperties.ZuulRoute;

  10. import org.springframework.cloud.netflix.zuul.web.ZuulHandlerMapping;

  11. import org.springframework.stereotype.Service;

  12. import org.springframework.util.StringUtils;

  13. import com.cfeindia.examples.zuul.dao.DynamicRouteRedisRepository;

  14. import com.cfeindia.examples.zuul.model.DynamicRoute;

  15. import com.cfeindia.examples.zuul.model.DynamicRouteResponse;

  16.  

  17. @Service

  18. public class ZuulDynamicRoutingService {

  19.  

  20. private static final Logger logger = LoggerFactory.getLogger(ZuulDynamicRoutingService.class);

  21.  

  22. private static final String HTTP_PROTOCOL = "http://";

  23.  

  24. private final ZuulProperties zuulProperties;

  25. private final ZuulHandlerMapping zuulHandlerMapping;

  26. private final DynamicRouteRedisRepository dynamicRouteRedisRepository;

  27.  

  28. @Autowired

  29. public ZuulDynamicRoutingService(final ZuulProperties zuulProperties, final ZuulHandlerMapping zuulHandlerMapping,

  30.   final DynamicRouteRedisRepository dynamicRouteRedisRepository) {

  31.   this.zuulProperties = zuulProperties;

  32.   this.zuulHandlerMapping = zuulHandlerMapping;

  33.   this.dynamicRouteRedisRepository = dynamicRouteRedisRepository;

  34.  }

  35.  .......

添加创建动态路由的方法。下面是请求JSON示例:

 
  1. {

  2. "requestURIUniqueKey" : "api1UniqueKey",

  3. "requestURI": "/api1",

  4. "targetURLHost": "localhost",

  5. "targetURLPort": "8081",

  6. "targetURIPath": "/proxy-api1"

  7. }

ZuulRoute映射需要一个独一无二的key往map中添加route,因此API客户端应该总是为不同的请求URI和路由信息发送不同的唯一key;否则,新路由将覆盖已经存在的路由。

dynamicRoute.requestURIUniqueKey在每个请求中应该有所不同。它可以是任何字符串。请求URI和目标URI路径应该从“/”开始,以使route正常工作。

在ZuulDynamicRoutingService中添加一条Route的相关操作

在添加一条route时,这几行代码很重要:

 
  1. zuulProperties.getRoutes().put(dynamicRoute.getRequestURIUniqueKey(),

  2. new ZuulRoute(

  3. dynamicRoute.getRequestURIUniqueKey(),

  4. dynamicRoute.getRequestURI() + "/**",

  5. null, url, true, false, new HashSet<>())

  6. );

  7.  

  8. zuulHandlerMapping.setDirty(true);

 
  1. public DynamicRouteResponse addDynamicRoute(DynamicRoute dynamicRoute) {

  2.  

  3. logger.debug("request received in service to add {}", dynamicRoute);

  4. addDynamicRouteInZuul(dynamicRoute);

  5. logger.debug("going to add in cache {}", dynamicRoute);

  6. addToCache(dynamicRoute);

  7. logger.debug("added in cache {}", dynamicRoute);

  8. zuulHandlerMapping.setDirty(true);

  9. DynamicRouteResponse dynamicRouteResponse = new DynamicRouteResponse();

  10. logger.debug("response sent {}", dynamicRouteResponse);

  11. return dynamicRouteResponse;

  12. }

  13.  

  14. private void addDynamicRouteInZuul(DynamicRoute dynamicRoute) {

  15. String url = createTargetURL(dynamicRoute);

  16. zuulProperties.getRoutes().put(dynamicRoute.getRequestURIUniqueKey(),

  17.  new ZuulRoute(dynamicRoute.getRequestURIUniqueKey(), dynamicRoute.getRequestURI() + "/**",

  18.   null, url, true, false, new HashSet < > ()));

  19. }

  20.  

  21. private String createTargetURL(DynamicRoute dynamicRoute) {

  22. StringBuilder sb = new StringBuilder(HTTP_PROTOCOL);

  23. sb.append(dynamicRoute.getTargetURLHost()).append(":").append(dynamicRoute.getTargetURLPort());

  24. if (StringUtils.isEmpty(dynamicRoute.getTargetURIPath())) {

  25.  sb.append("");

  26. } else {

  27.  sb.append(dynamicRoute.getTargetURIPath());

  28. }

  29. return sb.toString();

  30. }

此外,我们还需要在Redis缓存中添加路由信息:

 
  1. private void addToCache(final DynamicRoute dynamicRoute) {

  2. DynamicRoute dynamicRouteSaved = dynamicRouteRedisRepository.save(dynamicRoute);

  3. logger.debug("Added in cache {}", dynamicRouteSaved);

  4. }

按如下步骤操作,可以从Zuul Mapping和Redis缓存中删除route。

在移除一个route时,这几行代码比较重要。

 
  1. ZuulRoute zuulRoute = zuulProperties.getRoutes().remove(requestURIUniqueKey);

  2. zuulHandlerMapping.setDirty(true)

完整的代码:

 
  1. public Boolean removeDynamicRoute(final String requestURIUniqueKey) {

  2.  

  3. DynamicRoute dynamicRoute = new DynamicRoute();

  4. //Removal from redis will be done from unique key. No need for other params. So create object

  5. //with just unique key

  6. dynamicRoute.setRequestURIUniqueKey(requestURIUniqueKey);

  7. if (zuulProperties.getRoutes().containsKey(requestURIUniqueKey)) {

  8.  ZuulRoute zuulRoute = zuulProperties.getRoutes().remove(requestURIUniqueKey);

  9.  logger.debug("removed the zuul route {}", zuulRoute);

  10.  //Removal from redis will be done from unique key. No need for other params

  11.  removeFromCache(dynamicRoute);

  12.  zuulHandlerMapping.setDirty(true);

  13.  return Boolean.TRUE;

  14. }

  15. return Boolean.FALSE;

  16. }

  17.  

  18. private void removeFromCache(final DynamicRoute dynamicRoute) {

  19. logger.debug("removing the dynamic route {}", dynamicRoute);

  20. //Removal from redis will be done from unique key. No need for other params

  21. dynamicRouteRedisRepository.delete(dynamicRoute);

  22. }

在服务器启动时,从Redis缓存恢复路由可以这样做:

 
  1. /**

  2. * Load all routes from redis cache to restore the existing routes while restarting the zuul server

  3. */

  4. @PostConstruct

  5. public void initialize() {

  6. try {

  7.  dynamicRouteRedisRepository.findAll().forEach(dynamicRoute -> {

  8.   addDynamicRouteInZuul(dynamicRoute);

  9.  });

  10.  zuulHandlerMapping.setDirty(true);

  11. } catch (Exception e) {

  12.  logger.error("Exception in loading any previous route while restarting zuul routes.", e);

  13. }

  14. }

Demo

下面展示了不同操作及其对服务器数据影响的快照列表。Spring Boot公开了URL /admin/routes来检查Zuul路由。

本地服务器下的完整URL是 http://localhost:8071/admin/routes

增加路由:

查看已经添加的路由:

增加另一个路由:

再次显示已经添加的路由。查看已经添加的路由数量:

删除一个路由:

 

在删除一个路由后,再查看一下路由:

待办事项

通过添加一些API来停止Zuul服务器。重新启动服务器并使用 http://localhost:8071/admin/routes查看路由,如果它们是从Redis加载的。

总结

本文解释了在JVM运行时中使用REST API在Zuul服务器上注册动态路由。它在Redis缓存中保存路由信息。

我们展示了如何使它具有容错功能,以及如何在重新启动Zuul服务器时从Redis缓存中恢复以前的路由。

本例包含了使用Redis保存和检索数据的功能,还演示了在Spring boot/Spring MVC项目中如何在服务器启动时加载数据。

此外,像Mongo这样的数据库可以代替Redis,以更好的方式确保路由不丢失。

从GitHub下载这个项目https://github.com/vikasanandgit/zuul-route-redis。

这是另一篇关于使用Zuul的文章 ,其中一个子域的请求被路由到子路径,并且子域到子路径的路由可以动态注册。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值