本文共 23042 字,大约阅读时间需要 76 分钟。
JackieTang 今天
原文链接:https://dzone.com/articles/persistent-and-fault-tolerant-dynamic-routes-using
作者: Vikas Anand
译者:helloworldtang
我们将使用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正常工作。
在添加一条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);
}
}
下面展示了不同操作及其对服务器数据影响的快照列表。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的文章 ,其中一个子域的请求被路由到子路径,并且子域到子路径的路由可以动态注册。