ZUUL - Gateway proxy route recovery with Redis and Spring Data

Previous Post

Last post was about Zuul dynamic gateway both for API and website (http://huksablog.blogspot.in/2018/05/zuul-subdomain-to-subpath-proxy-gateway.html). Now this part explains an improvement regarding data restoration in case gateway server is restarted using Redis.

Enhancement


Whenever there is a server which keeps all the mapping in memory,  the data gets lost when server is restarted. Here the case was same. So Redis Cache is added to Gateway server to save the mapping whenever a new route is registered. Additionally, it restores the data from Redis if the server restarts.

Architecture



Steps


1. Install Redis
Depending on the OS, redis server can be downloaded and started with default configuration.

2. Add Maven dependancy. Please check the previous post about ZUUL to get the base pom.

    <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

3. Config changes:

Add these lines in AppConfig.java

@Bean
public RedisConnectionFactory redisConnectionFactory() {
JedisPoolConfig poolConfig = new JedisPoolConfig();
/**This value can be 128 depending on the concurrent users connecting to it.
Keeping it a less value and keeping redisTemplate.setEnableTransactionSupport(true);
make the connection to redis hung and the data does not get saved in redis.
Root cause seem to be blocking nature of the pool, yet to get a reliable solution of it.
/*
poolConfig.setMaxTotal(5);
poolConfig.setTestOnBorrow(true);
poolConfig.setTestOnReturn(true);
JedisConnectionFactory connectionFactory = new JedisConnectionFactory(poolConfig);
connectionFactory.setUsePool(true);
connectionFactory.setHostName("localhost");
connectionFactory.setPort(6379);
return connectionFactory;
}
@Bean
public RedisTemplate<String, DynamicRoute> redisTemplate() {
RedisTemplate<String, DynamicRoute> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());
/**Note: if there is an issue of connection exhaust or connection is taking too much time the you can set this value as false. Simple read/write should not be needed the transactionSupport enabling. redisTemplate.setEnableTransactionSupport(false);*/
redisTemplate.setEnableTransactionSupport(true);
return redisTemplate;
}
4. Add annotation in application to enable Redis repository


@SpringBootApplication
@EnableAutoConfiguration(exclude = { RabbitAutoConfiguration.class })
@EnableZuulProxy
@EnableRedisRepositories("com.example.gatewayservice.cache")
@ComponentScan("com.example.gatewayservice")
public class SpringBootWebApplication {

5. Add a Model with Id. Check the code in Bold, which is critical it to work.

package com.example.gatewayservice.model;

import java.io.Serializable;

import org.springframework.data.annotation.Id;
import org.springframework.data.redis.core.RedisHash;

@RedisHash("Gateway_Service_Dynamic_Route")
public class DynamicRoute implements Serializable {

private static final long serialVersionUID = -6719150427066586659L;

@Id
private String routeKey;
private String host;
private int port;
private String subpath;

public DynamicRoute() {
}
public DynamicRoute(String routeKey, String host, int port, String subpath) {
super();
this.routeKey = routeKey;
this.host = host;
this.port = port;
this.subpath = subpath;
}
public String getRouteKey() {
return routeKey;
}
public void setRouteKey(String routeKey) {
this.routeKey = routeKey;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getSubpath() {
return subpath;
}
public void setSubpath(String subpath) {
this.subpath = subpath;
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((routeKey == null) ? 0 : routeKey.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DynamicRoute other = (DynamicRoute) obj;
if (routeKey == null) {
if (other.routeKey != null)
return false;
} else if (!routeKey.equals(other.routeKey))
return false;
return true;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("DynamicRoute [routeKey=");
builder.append(routeKey);
builder.append(", host=");
builder.append(host);
builder.append(", port=");
builder.append(port);
builder.append(", subpath=");
builder.append(subpath);
builder.append("]");
return builder.toString();
}

}

6. Add a repository:

@Repository
public interface DynamicRouteRedisRepository extends CrudRepository<DynamicRoute, String> {}

7. Use it in service class

@Autowired
private DynamicRouteRedisRepository dynamicRouteRedisRepository;

8. Add Proxy route mapping in Redis while registering the route.
@Async
private void addToCache(String uuid, String host, int port, String subpath) {
dynamicRouteRedisRepository.save(new DynamicRoute(uuid, host,
port, subpath));
}

8. Restore the route mapping when server is restarted.

@PostConstruct
public void initialize() {
try {
dynamicRouteRedisRepository.findAll().forEach(dynamicRoute -> {
StringBuilder sb = new StringBuilder(HTTP);
String uuid = dynamicRoute.getRouteKey();
sb.append(dynamicRoute.getHost()).append(":").append(dynamicRoute.getPort());
zuulProperties.getRoutes().put(uuid,
new ZuulRoute(uuid, "/" + uuid + "/**", null, sb.toString(), true, false, new HashSet<>()));
});
zuulHandlerMapping.setDirty(true);
} catch (Exception e) {
logger.error("Exception in loading any previous route while restarting zuul routes.", e);
    }
}

10. Now you are free to restart the gateway server with new changes without loosing the proxy route already registered. 

11. Additionally, you can check the data in redis by using redis-cli.

I hope the post would help in multiple ways. As it is a complete working example, so you can use it as it is. Let me know if there any questions.




Comments

Popular posts from this blog

Huksa: Virtual Hospital

Huksa: Entrepreneurship Guru