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
-
<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">
-
<modelVersion>4.0.0</modelVersion>
-
<groupId>com.cfeindia.examples.zuul</groupId>
-
<artifactId>zuul-route-redis</artifactId>
-
<version>0.0.1-SNAPSHOT</version>
-
<parent>
-
<groupId>org.springframework.boot</groupId>
-
<artifactId>spring-boot-starter-parent</artifactId>
-
<version>1.5.13.RELEASE</version>
-
<relativePath />
-
<!-- lookup parent from repository -->
-
</parent>
-
<properties>
-
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
-
<java.version>1.8</java.version>
-
<spring-cloud.version>Edgware.SR3</spring-cloud.version>
-
<maven.compiler.source>1.8</maven.compiler.source>
-
<maven.compiler.target>1.8</maven.compiler.target>
-
</properties>
-
<repositories>
-
<repository>
-
<id>spy</id>
-
<name>Spy Repository</name>
-
<layout>default</layout>
-
<url>http://files.couchbase.com/maven2/</url>
-
<snapshots>
-
<enabled>false</enabled>
-
</snapshots>
-
</repository>
-
</repositories>
-
<dependencyManagement>
-
<dependencies>
-
<dependency>
-
<groupId>org.springframework.cloud</groupId>
-
<artifactId>spring-cloud-dependencies</artifactId>
-
<version>${spring-cloud.version}</version>
-
<type>pom</type>
-
<scope>import</scope>
-
</dependency>
-
</dependencies>
-
</dependencyManagement>
-
<dependencies>
-
<dependency>
-
<groupId>org.springframework.cloud</groupId>
-
<artifactId>spring-cloud-starter-zuul</artifactId>
-
</dependency>
-
<dependency>
-
<artifactId>spring-boot-starter-actuator</artifactId>
-
<exclusions>
-
<exclusion>
-
<groupId>org.springframework.boot</groupId>
-
<artifactId>spring-boot-starter-logging</artifactId>
-
</exclusion>
-
</exclusions>
-
</dependency>
-
<dependency>
-
<groupId>org.springframework.boot</groupId>
-
<artifactId>spring-boot-starter-log4j2</artifactId>
-
</dependency>
-
<dependency>
-
<groupId>org.springframework.boot</groupId>
-
<artifactId>spring-boot-starter-data-redis</artifactId>
-
</dependency>
-
<dependency>
-
<groupId>org.springframework.boot</groupId>
-
<artifactId>spring-boot-starter-web</artifactId>
-
</dependency>
-
</dependencies>
-
<build>
-
<plugins>
-
<plugin>
-
<artifactId>maven-compiler-plugin</artifactId>
-
<configuration>
-
<source>1.8</source>
-
<target>1.8</target>
-
</configuration>
-
</plugin>
-
<plugin>
-
<artifactId>maven-shade-plugin</artifactId>
-
<executions>
-
<execution>
-
<phase>package</phase>
-
<goals>
-
<goal>shade</goal>
-
</goals>
-
<configuration>
-
<finalName>${project.artifactId}-${project.version}</finalName>
-
<transformers>
-
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
-
<resource>META-INF/spring.handlers</resource>
-
</transformer>
-
<transformer implementation="org.springframework.boot.maven.PropertiesMergingResourceTransformer">
-
<resource>META-INF/spring.factories</resource>
-
</transformer>
-
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
-
<resource>META-INF/spring.schemas</resource>
-
</transformer>
-
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
-
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
-
<mainClass>com.mettl.gatewayservice.application.SpringBootWebApplication</mainClass>
-
</transformer>
-
<transformer implementation="com.github.edwgiz.mavenShadePlugin.log4j2CacheTransformer.PluginsCacheFileTransformer" />
-
</transformers>
-
</configuration>
-
</execution>
-
</executions>
-
<dependencies>
-
<dependency>
-
<groupId>com.github.edwgiz</groupId>
-
<artifactId>maven-shade-plugin.log4j2-cachefile-transformer</artifactId>
-
<version>2.6.1</version>
-
</dependency>
-
</dependencies>
-
</plugin>
-
</plugins>
-
</build>
-
</project>
-
Spring Boot应用程序启动类,添加了Zuul、Redis等特性必需的注解:
-
package com.cfeindia.examples.zuul.application;
-
import org.springframework.boot.autoconfigure.SpringBootApplication;
-
import org.springframework.boot.SpringApplication;
-
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-
import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
-
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
-
import org.springframework.context.annotation.ComponentScan;
-
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
-
@SpringBootApplication
-
@EnableAutoConfiguration(exclude = {
-
RabbitAutoConfiguration.class
-
})
-
@EnableZuulProxy
-
@EnableRedisRepositories("com.cfeindia.examples.zuul")
-
@ComponentScan("com.cfeindia.examples.zuul")
-
public class SpringBootWebApplication {
-
public static void main(String[] args) {
-
SpringApplication.run(SpringBootWebApplication.class, args);
-
}
-
}
-
应用程序配置类,定义bean
-
package com.cfeindia.examples.zuul.config;
-
import org.springframework.context.annotation.Bean;
-
-
import org.springframework.context.annotation.Configuration;
-
import org.springframework.data.redis.connection.RedisConnectionFactory;
-
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
-
import org.springframework.data.redis.core.RedisTemplate;
-
import com.cfeindia.examples.zuul.model.DynamicRoute;
-
import redis.clients.jedis.JedisPoolConfig;
-
@Configuration
-
public class AppConfig {
-
private static final int REDIS_PORT = 6379;
-
private static final String REDIS_HOST_NAME = "localhost";
-
-
@Bean
-
public RedisConnectionFactory redisConnectionFactory() {
-
JedisPoolConfig poolConfig = new JedisPoolConfig();
-
//Change the configuration as per requirement
-
poolConfig.setMaxTotal(128);
-
poolConfig.setTestOnBorrow(true);
-
poolConfig.setTestOnReturn(true);
-
poolConfig.setMinIdle(5);
-
poolConfig.setMaxIdle(128);
-
JedisConnectionFactory connectionFactory = new JedisConnectionFactory(poolConfig);
-
connectionFactory.setUsePool(true);
-
connectionFactory.setHostName(REDIS_HOST_NAME);
-
connectionFactory.setPort(REDIS_PORT);
-
return connectionFactory;
-
}
-
-
@Bean
-
public RedisTemplate < String, DynamicRoute > redisTemplate() {
-
RedisTemplate < String, DynamicRoute > redisTemplate = new RedisTemplate < > ();
-
redisTemplate.setConnectionFactory(redisConnectionFactory());
-
redisTemplate.setEnableTransactionSupport(false);
-
return redisTemplate;
-
}
-
}
-
controller类,提供了动态路由注册和删除功能的API:
-
package com.cfeindia.examples.zuul.controller;
-
import org.slf4j.Logger;
-
import org.slf4j.LoggerFactory;
-
import org.springframework.beans.factory.annotation.Autowired;
-
import org.springframework.web.bind.annotation.RequestBody;
-
import org.springframework.web.bind.annotation.RequestMapping;
-
import org.springframework.web.bind.annotation.RequestMethod;
-
import org.springframework.web.bind.annotation.ResponseBody;
-
import org.springframework.web.bind.annotation.RestController;
-
import com.cfeindia.examples.zuul.model.DeleteRouteRequest;
-
import com.cfeindia.examples.zuul.model.DynamicRoute;
-
import com.cfeindia.examples.zuul.model.DynamicRouteResponse;
-
import com.cfeindia.examples.zuul.service.ZuulDynamicRoutingService;
-
-
@RestController
-
public class DynamicRouteController {
-
-
private static final Logger logger = LoggerFactory.getLogger(DynamicRouteController.class);
-
-
@Autowired
-
ZuulDynamicRoutingService zuulDynamicRoutingService;
-
-
@RequestMapping(value = "/proxyurl", method = RequestMethod.POST)
-
public @ResponseBody DynamicRouteResponse getProxyURL(@RequestBody DynamicRoute dynamicRoute) {
-
logger.debug("request received to add {}", dynamicRoute);
-
DynamicRouteResponse dynamicRouteResponse = zuulDynamicRoutingService.addDynamicRoute(dynamicRoute);
-
logger.debug("response sent {}", dynamicRouteResponse);
-
return dynamicRouteResponse;
-
}
-
-
@RequestMapping(value = "/proxyurl", method = RequestMethod.DELETE)
-
public @ResponseBody Boolean deleteProxyURL(@RequestBody DeleteRouteRequest deleteRouteRequest) {
-
logger.debug("request received to delete {}", deleteRouteRequest);
-
Boolean response = zuulDynamicRoutingService.removeDynamicRoute(deleteRouteRequest.getRequestURIUniqueKey());
-
logger.debug("response sent for delete {}", response);
-
return response;
-
}
-
}
-
Rest API请求和响应需要的POJO
DynamicRoute.java:
存储到Redis时用到的动态路由请求类。检查@RedisHash和@Id注解,这是保存、检索和删除动态路由所必需的。 它也被用于Rest API请求中,用来将传入的Json转换成动态路由对象。
-
package com.cfeindia.examples.zuul.model;
-
import java.io.Serializable;
-
import org.springframework.data.annotation.Id;
-
import org.springframework.data.redis.core.RedisHash;
-
-
/**
-
* Dynamic route request object saved in Redis
-
* It is also used in Rest API request to convert Json in Object
-
* @author vikasanand
-
*
-
*/
-
@RedisHash("Gateway_Service_Dynamic_Route")
-
public class DynamicRoute implements Serializable {
-
-
private static final long serialVersionUID = -6719150427066586659 L;
-
-
/**
-
* This can be a unique key different for each route registration. It should be different
-
* for each requestURI to be forwarded.
-
* i.e. asad121-sadas-hjjhhd
-
*/
-
@Id
-
public String requestURIUniqueKey;
-
/**
-
* This can be of format "/api1"
-
* It should be sub-path URI which needs to forwarded to different proxy
-
*/
-
private String requestURI;
-
-
/**
-
* Target Host name or IP
-
* i.e. https://adomain.com
-
*/
-
private String targetURLHost;
-
-
/**
-
* Target Port to forward
-
* i.e. 80
-
*/
-
private int targetURLPort;
-
-
/**
-
* Target URI to forward
-
* /proxy-api1
-
*/
-
private String targetURIPath;
-
-
public String getRequestURIUniqueKey() {
-
return requestURIUniqueKey;
-
}
-
-
public void setRequestURIUniqueKey(String requestURIUniqueKey) {
-
this.requestURIUniqueKey = requestURIUniqueKey;
-
}
-
-
public String getRequestURI() {
-
return requestURI;
-
}
-
-
public void setRequestURI(String requestURI) {
-
this.requestURI = requestURI;
-
}
-
-
public String getTargetURLHost() {
-
return targetURLHost;
-
}
-
-
public void setTargetURLHost(String targetURLHost) {
-
this.targetURLHost = targetURLHost;
-
}
-
-
public int getTargetURLPort() {
-
return targetURLPort;
-
}
-
-
public void setTargetURLPort(int targetURLPort) {
-
this.targetURLPort = targetURLPort;
-
}
-
-
public String getTargetURIPath() {
-
return targetURIPath;
-
}
-
-
public void setTargetURIPath(String targetURIPath) {
-
this.targetURIPath = targetURIPath;
-
}
-
-
@Override
-
public String toString() {
-
StringBuilder builder = new StringBuilder();
-
builder.append("DynamicRoute [requestURIUniqueKey=");
-
builder.append(requestURIUniqueKey);
-
builder.append(", requestURI=");
-
builder.append(requestURI);
-
builder.append(", targetURLHost=");
-
builder.append(targetURLHost);
-
builder.append(", targetURLPort=");
-
builder.append(targetURLPort);
-
builder.append(", targetURIPath=");
-
builder.append(targetURIPath);
-
builder.append("]");
-
return builder.toString();
-
}
-
}
其它POJO:
-
package com.cfeindia.examples.zuul.model;
-
/**
-
* Response of dynamic route creation and saving
-
* @author vikasanand
-
*
-
*/
-
public class DynamicRouteResponse {
-
/**
-
* statusCode is 0 for success.
-
* if statusCode is not 0 then check for errorMessage for details.
-
*/
-
private int statusCode;
-
-
private String errorMessage;
-
-
public DynamicRouteResponse() {
-
super();
-
}
-
-
public DynamicRouteResponse(int statusCode, String errorMessage) {
-
super();
-
this.statusCode = statusCode;
-
this.errorMessage = errorMessage;
-
}
-
-
public int getStatusCode() {
-
return statusCode;
-
}
-
-
public String getErrorMessage() {
-
return errorMessage;
-
}
-
-
@Override
-
public String toString() {
-
StringBuilder builder = new StringBuilder();
-
builder.append("DynamicRouteResponse [statusCode=");
-
builder.append(statusCode);
-
builder.append(", errorMessage=");
-
builder.append(errorMessage);
-
builder.append("]");
-
return builder.toString();
-
}
-
}
-
package com.cfeindia.examples.zuul.model;
-
public class DeleteRouteRequest {
-
-
public String requestURIUniqueKey;
-
-
public String getRequestURIUniqueKey() {
-
return requestURIUniqueKey;
-
}
-
-
public void setRequestURIUniqueKey(String requestURIUniqueKey) {
-
this.requestURIUniqueKey = requestURIUniqueKey;
-
}
-
}
-
与Redis服务器交互的DAO或repository类。使用Spring Data Redis库使得CRUD操作变得非常简单。我们只需要创建一个接口来扩展接口CrudRepository,并添加@Repository注解。请设置DynamicRouteRedisRepository所继承父类CrudRepository的泛型为DynamicRoute和String,第一个是值的类型,第二个是键的类型。
-
package com.cfeindia.examples.zuul.dao;
-
-
import org.springframework.data.repository.CrudRepository;
-
import org.springframework.stereotype.Repository;
-
import com.cfeindia.examples.zuul.model.DynamicRoute;
-
-
@Repository
-
public interface DynamicRouteRedisRepository extends CrudRepository < DynamicRoute, String > {}
-
Zuul使用的Filter类。这些类都是默认类。
-
package com.cfeindia.examples.zuul.filter;
-
-
import javax.servlet.http.HttpServletRequest;
-
import org.slf4j.Logger;
-
import org.slf4j.LoggerFactory;
-
import org.springframework.stereotype.Component;
-
import org.springframework.util.StringUtils;
-
import org.springframework.web.util.UrlPathHelper;
-
import com.netflix.zuul.ZuulFilter;
-
import com.netflix.zuul.context.RequestContext;
-
import com.netflix.zuul.http.HttpServletRequestWrapper;
-
-
@Component
-
public class PreFilter extends ZuulFilter {
-
-
private static Logger log = LoggerFactory.getLogger(PreFilter.class);
-
private UrlPathHelper urlPathHelper = new UrlPathHelper();
-
-
@Override
-
public String filterType() {
-
return "pre";
-
}
-
-
@Override
-
public int filterOrder() {
-
return 1;
-
}
-
-
@Override
-
public boolean shouldFilter() {
-
RequestContext ctx = RequestContext.getCurrentContext();
-
String requestURL = ctx.getRequest().getRequestURL().toString();
-
return !(requestURL.contains("proxyurl") || requestURL.contains("/admin/"));
-
}
-
-
@Override
-
public Object run() {
-
RequestContext ctx = RequestContext.getCurrentContext();
-
HttpServletRequest request = ctx.getRequest();
-
log.info("PreFilter: " +
-
String.format("%s request to %s", request.getMethod(), request.getRequestURL().toString()));
-
return null;
-
}
-
}
-
package com.cfeindia.examples.zuul.filter;
-
-
import javax.servlet.http.HttpServletRequest;
-
import org.slf4j.Logger;
-
import org.slf4j.LoggerFactory;
-
import org.springframework.stereotype.Component;
-
import com.netflix.zuul.ZuulFilter;
-
import com.netflix.zuul.context.RequestContext;
-
-
@Component
-
public class RouteFilter extends ZuulFilter {
-
-
private static Logger log = LoggerFactory.getLogger(RouteFilter.class);
-
-
@Override
-
public String filterType() {
-
return "route";
-
}
-
-
@Override
-
public int filterOrder() {
-
return 1;
-
}
-
-
@Override
-
public boolean shouldFilter() {
-
RequestContext ctx = RequestContext.getCurrentContext();
-
String requestURL = ctx.getRequest().getRequestURL().toString();
-
return !(requestURL.contains("proxyurl") || requestURL.contains("/admin/"));
-
}
-
-
@Override
-
public Object run() {
-
RequestContext ctx = RequestContext.getCurrentContext();
-
HttpServletRequest request = ctx.getRequest();
-
log.info("RouteFilter: " + String.format("%s request to %s", request.getMethod(), request.getRequestURL().toString()));
-
return null;
-
}
-
-
}
-
package com.cfeindia.examples.zuul.filter;
-
-
import javax.servlet.http.HttpServletResponse;
-
import org.slf4j.Logger;
-
import org.slf4j.LoggerFactory;
-
import org.springframework.stereotype.Component;
-
import com.netflix.zuul.ZuulFilter;
-
import com.netflix.zuul.context.RequestContext;
-
-
@Component
-
public class PostFilter extends ZuulFilter {
-
-
private static Logger log = LoggerFactory.getLogger(PostFilter.class);
-
-
@Override
-
public String filterType() {
-
return "post";
-
}
-
-
@Override
-
public int filterOrder() {
-
return 1;
-
}
-
-
@Override
-
public boolean shouldFilter() {
-
RequestContext ctx = RequestContext.getCurrentContext();
-
String requestURL = ctx.getRequest().getRequestURL().toString();
-
return !(requestURL.contains("proxyurl") || requestURL.contains("/admin/"));
-
}
-
-
@Override
-
public Object run() {
-
HttpServletResponse response = RequestContext.getCurrentContext().getResponse();
-
log.info("PostFilter: " + String.format("response is %s", response));
-
return null;
-
}
-
}
-
package com.cfeindia.examples.zuul.filter;
-
-
import javax.servlet.http.HttpServletResponse;
-
import org.slf4j.Logger;
-
import org.slf4j.LoggerFactory;
-
import org.springframework.stereotype.Component;
-
import com.netflix.zuul.ZuulFilter;
-
import com.netflix.zuul.context.RequestContext;
-
-
@Component
-
public class ErrorFilter extends ZuulFilter {
-
-
private static Logger log = LoggerFactory.getLogger(PostFilter.class);
-
-
@Override
-
public String filterType() {
-
return "error";
-
}
-
-
@Override
-
public int filterOrder() {
-
return 1;
-
}
-
-
@Override
-
public boolean shouldFilter() {
-
RequestContext ctx = RequestContext.getCurrentContext();
-
String requestURL = ctx.getRequest().getRequestURL().toString();
-
return !(requestURL.contains("proxyurl") || requestURL.contains("/admin/"));
-
}
-
-
@Override
-
public Object run() {
-
HttpServletResponse response = RequestContext.getCurrentContext().getResponse();
-
log.info("ErrorFilter: " + String.format("response is %s", response));
-
return null;
-
}
-
}
在Spring Boot的application.yml文件中配置Zuul和actuator:
-
server:
-
port: ${appPort:8071}
-
# Actuator endpoint path (/admin/info, /admin/health, ...)
-
server.servlet-path: /
-
management.context-path: /admin
-
management.security.enabled: false
-
endpoints.health.sensitive: false
-
# ribbon.eureka.enabled: false
-
zuul:
-
ignoredPatterns: /**/admin/**, /proxyurl
-
routes:
-
zuulDemo1:
-
path: /**
-
url: http://localhost/admin/health
-
# stripPrefix set to true if context path is set to /
-
stripPrefix: true
现在,Service对于不同的功能有不同的方法。创建ZuulDynamicRoutingService类并引入必要依赖,如下所示:
-
package com.cfeindia.examples.zuul.service;
-
-
import java.util.HashSet;
-
import javax.annotation.PostConstruct;
-
import org.slf4j.Logger;
-
import org.slf4j.LoggerFactory;
-
import org.springframework.beans.factory.annotation.Autowired;
-
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
-
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties.ZuulRoute;
-
import org.springframework.cloud.netflix.zuul.web.ZuulHandlerMapping;
-
import org.springframework.stereotype.Service;
-
import org.springframework.util.StringUtils;
-
import com.cfeindia.examples.zuul.dao.DynamicRouteRedisRepository;
-
import com.cfeindia.examples.zuul.model.DynamicRoute;
-
import com.cfeindia.examples.zuul.model.DynamicRouteResponse;
-
-
@Service
-
public class ZuulDynamicRoutingService {
-
-
private static final Logger logger = LoggerFactory.getLogger(ZuulDynamicRoutingService.class);
-
-
private static final String HTTP_PROTOCOL = "http://";
-
-
private final ZuulProperties zuulProperties;
-
private final ZuulHandlerMapping zuulHandlerMapping;
-
private final DynamicRouteRedisRepository dynamicRouteRedisRepository;
-
-
@Autowired
-
public ZuulDynamicRoutingService(final ZuulProperties zuulProperties, final ZuulHandlerMapping zuulHandlerMapping,
-
final DynamicRouteRedisRepository dynamicRouteRedisRepository) {
-
this.zuulProperties = zuulProperties;
-
this.zuulHandlerMapping = zuulHandlerMapping;
-
this.dynamicRouteRedisRepository = dynamicRouteRedisRepository;
-
}
-
.......
添加创建动态路由的方法。下面是请求JSON示例:
-
{
-
"requestURIUniqueKey" : "api1UniqueKey",
-
"requestURI": "/api1",
-
"targetURLHost": "localhost",
-
"targetURLPort": "8081",
-
"targetURIPath": "/proxy-api1"
-
}
ZuulRoute映射需要一个独一无二的key往map中添加route,因此API客户端应该总是为不同的请求URI和路由信息发送不同的唯一key;否则,新路由将覆盖已经存在的路由。
dynamicRoute.requestURIUniqueKey
在每个请求中应该有所不同。它可以是任何字符串。请求URI和目标URI路径应该从“/”开始,以使route正常工作。
在ZuulDynamicRoutingService中添加一条Route的相关操作
在添加一条route时,这几行代码很重要:
-
zuulProperties.getRoutes().put(dynamicRoute.getRequestURIUniqueKey(),
-
new ZuulRoute(
-
dynamicRoute.getRequestURIUniqueKey(),
-
dynamicRoute.getRequestURI() + "/**",
-
null, url, true, false, new HashSet<>())
-
);
-
-
zuulHandlerMapping.setDirty(true);
-
public DynamicRouteResponse addDynamicRoute(DynamicRoute dynamicRoute) {
-
-
logger.debug("request received in service to add {}", dynamicRoute);
-
addDynamicRouteInZuul(dynamicRoute);
-
logger.debug("going to add in cache {}", dynamicRoute);
-
addToCache(dynamicRoute);
-
logger.debug("added in cache {}", dynamicRoute);
-
zuulHandlerMapping.setDirty(true);
-
DynamicRouteResponse dynamicRouteResponse = new DynamicRouteResponse();
-
logger.debug("response sent {}", dynamicRouteResponse);
-
return dynamicRouteResponse;
-
}
-
-
private void addDynamicRouteInZuul(DynamicRoute dynamicRoute) {
-
String url = createTargetURL(dynamicRoute);
-
zuulProperties.getRoutes().put(dynamicRoute.getRequestURIUniqueKey(),
-
new ZuulRoute(dynamicRoute.getRequestURIUniqueKey(), dynamicRoute.getRequestURI() + "/**",
-
null, url, true, false, new HashSet < > ()));
-
}
-
-
private String createTargetURL(DynamicRoute dynamicRoute) {
-
StringBuilder sb = new StringBuilder(HTTP_PROTOCOL);
-
sb.append(dynamicRoute.getTargetURLHost()).append(":").append(dynamicRoute.getTargetURLPort());
-
if (StringUtils.isEmpty(dynamicRoute.getTargetURIPath())) {
-
sb.append("");
-
} else {
-
sb.append(dynamicRoute.getTargetURIPath());
-
}
-
return sb.toString();
-
}
此外,我们还需要在Redis缓存中添加路由信息:
-
private void addToCache(final DynamicRoute dynamicRoute) {
-
DynamicRoute dynamicRouteSaved = dynamicRouteRedisRepository.save(dynamicRoute);
-
logger.debug("Added in cache {}", dynamicRouteSaved);
-
}
按如下步骤操作,可以从Zuul Mapping和Redis缓存中删除route。
在移除一个route时,这几行代码比较重要。
-
ZuulRoute zuulRoute = zuulProperties.getRoutes().remove(requestURIUniqueKey);
-
zuulHandlerMapping.setDirty(true)
完整的代码:
-
public Boolean removeDynamicRoute(final String requestURIUniqueKey) {
-
-
DynamicRoute dynamicRoute = new DynamicRoute();
-
//Removal from redis will be done from unique key. No need for other params. So create object
-
//with just unique key
-
dynamicRoute.setRequestURIUniqueKey(requestURIUniqueKey);
-
if (zuulProperties.getRoutes().containsKey(requestURIUniqueKey)) {
-
ZuulRoute zuulRoute = zuulProperties.getRoutes().remove(requestURIUniqueKey);
-
logger.debug("removed the zuul route {}", zuulRoute);
-
//Removal from redis will be done from unique key. No need for other params
-
removeFromCache(dynamicRoute);
-
zuulHandlerMapping.setDirty(true);
-
return Boolean.TRUE;
-
}
-
return Boolean.FALSE;
-
}
-
-
private void removeFromCache(final DynamicRoute dynamicRoute) {
-
logger.debug("removing the dynamic route {}", dynamicRoute);
-
//Removal from redis will be done from unique key. No need for other params
-
dynamicRouteRedisRepository.delete(dynamicRoute);
-
}
在服务器启动时,从Redis缓存恢复路由可以这样做:
-
/**
-
* Load all routes from redis cache to restore the existing routes while restarting the zuul server
-
*/
-
@PostConstruct
-
public void initialize() {
-
try {
-
dynamicRouteRedisRepository.findAll().forEach(dynamicRoute -> {
-
addDynamicRouteInZuul(dynamicRoute);
-
});
-
zuulHandlerMapping.setDirty(true);
-
} catch (Exception e) {
-
logger.error("Exception in loading any previous route while restarting zuul routes.", e);
-
}
-
}
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的文章 ,其中一个子域的请求被路由到子路径,并且子域到子路径的路由可以动态注册。