Configuring Jedis on Spring boot 2 - spring

I am trying to migrate from spring boot 1.5.5 to spring boot 2. I am getting the following for JedisPool
Parameter 0 of method getJedisPool in com.company.spring.config.ApplicationConfig required a bean of type 'org.springframework.data.redis.connection.jedis.JedisConnectionFactory' that could not be found.
- Bean method 'redisConnectionFactory' in 'JedisConnectionConfiguration' not loaded because #ConditionalOnMissingBean (types: org.springframework.data.redis.connection.RedisConnectionFactory; SearchStrategy: all) found beans of type 'org.springframework.data.redis.connection.RedisConnectionFactory' redisConnectionFactory
I am trying to configure using Jedis and not lettuce. I have ignored the lettuce module when importing the spring-starter-redis-data as suggested in the docs.
The below is the code that is trying to initialize the JedisPool.
#Bean
public JedisPool getJedisPool(JedisConnectionFactory jedisConnectionFactory) {
String host = jedisConnectionFactory.getHostName();
int port = jedisConnectionFactory.getPort();
String password = StringUtils.isEmpty(jedisConnectionFactory.getPassword()) ? null : jedisConnectionFactory.getPassword();
int timeout = jedisConnectionFactory.getTimeout();
GenericObjectPoolConfig poolConfig = jedisConnectionFactory.getPoolConfig();
log.info("Starting Redis with Host:{}, Port:{}, Timeout:{}, PoolConfig:{}", host, port, timeout, poolConfig);
return new JedisPool(poolConfig, host, port, timeout, password);
}

I fixed the issue by changing the bean to use RedisProperties. Here is the code that ended up working.
#Bean
public JedisPool getJedisPool(RedisProperties redisProperties) {
Pool jedisProperties = redisProperties.getJedis().getPool();
String password = StringUtils.isEmpty(redisProperties.getPassword()) ? null : redisProperties.getPassword();
int timeout = (int) redisProperties.getTimeout().toMillis();
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
poolConfig.setMaxIdle(jedisProperties.getMaxIdle());
poolConfig.setMaxTotal(jedisProperties.getMaxActive() + jedisProperties.getMaxIdle());
poolConfig.setMinIdle(jedisProperties.getMinIdle());
log.info("Starting Redis with Host:{}, Port:{}, Timeout(ms):{}, PoolConfig:{}", redisProperties.getHost(), redisProperties.getPort(),
timeout, poolConfig);
return new JedisPool(poolConfig, redisProperties.getHost(), redisProperties.getPort(), timeout, password);
}
I am not very sure if this is the correct way to configure Jedis on SpringBoot 2.0. In any case, this works.

Related

Spring Boot microservices - dependency

There are two microservices deployed with docker compose. A dependecy between services is defined in docker compose file by depends_on property. Is it possible to achieve the same effect implicitly, inside the spring boot application?
Let's say the microservice 1 depends on microservice 2. Which means, microsearvice 1 doesn't boot up before microservice 2 is healthy or registered on Eureka server.
By doing some research, I found a solution to the problem.
Spring Retry resolves dependency on Spring Cloud Config Server. Maven dependency spring-retry should be added into the pom.xml, and the properties below into the .properties file:
spring.cloud.config.fail-fast=true
spring.cloud.config.retry.max-interval=2000
spring.cloud.config.retry.max-attempts=10
The following configuration class is used to resolve dependency on other microservices.
#Configuration
#ConfigurationProperties(prefix = "depends-on")
#Data
#Log
public class DependsOnConfig {
private List<String> services;
private Integer periodMs = 2000;
private Integer maxAttempts = 20;
#Autowired
private EurekaClient eurekaClient;
#Bean
public void dependentServicesRegisteredToEureka() throws Exception {
if (services == null || services.isEmpty()) {
log.info("No dependent services defined.");
return;
}
log.info("Checking if dependent services are registered to eureka.");
int attempts = 0;
while (!services.isEmpty()) {
services.removeIf(this::checkIfServiceIsRegistered);
TimeUnit.MILLISECONDS.sleep(periodMs);
if (maxAttempts.intValue() == ++attempts)
throw new Exception("Max attempts exceeded.");
}
}
private boolean checkIfServiceIsRegistered(String service) {
try {
eurekaClient.getNextServerFromEureka(service, false);
log.info(service + " - registered.");
return true;
} catch (Exception e) {
log.info(service + " - not registered yet.");
return false;
}
}
}
A list of services that the current microservice depends on are defined in .properties file:
depends-on.services[0]=service-id-1
depends-on.services[1]=service-id-2
A bean dependentServicesRegisteredToEureka is not being initialized until all services from the list register to Eureka. If needed, annotation #DependsOn("dependentServicesRegisteredToEureka") can be added to beans or components to prevent attempting an initialization before dependentServicesRegisteredToEureka initialize.

Problem with connection to Neo4j test container using Spring boot 2 and JUnit5

Problem with connection to Neo4j test container using Spring boot 2 and JUnit5
int test context. Container started successfully but spring.data.neo4j.uri property has a wrong default port:7687, I guess this URI must be the same when I call neo4jContainer.getBoltUrl().
Everything works fine in this case:
#Testcontainers
public class ExampleTest {
#Container
private static Neo4jContainer neo4jContainer = new Neo4jContainer()
.withAdminPassword(null); // Disable password
#Test
void testSomethingUsingBolt() {
// Retrieve the Bolt URL from the container
String boltUrl = neo4jContainer.getBoltUrl();
try (
Driver driver = GraphDatabase.driver(boltUrl, AuthTokens.none());
Session session = driver.session()
) {
long one = session.run("RETURN 1",
Collections.emptyMap()).next().get(0).asLong();
assertThat(one, is(1L));
} catch (Exception e) {
fail(e.getMessage());
}
}
}
But SessionFactory is not created for the application using autoconfiguration following to these recommendations - https://www.testcontainers.org/modules/databases/neo4j/
When I try to create own primary bean - SessionFactory in test context I get the message like this - "URI cannot be returned before the container is not loaded"
But Application runs and works perfect using autoconfiguration and neo4j started in a container, the same cannot be told about the test context
You cannot rely 100% on Spring Boot's auto configuration (for production) in this case because it will read the application.properties or use the default values for the connection.
To achieve what you want to, the key part is to create a custom (Neo4j-OGM) Configuration bean. The #DataNeo4jTest annotation is provided by the spring-boot-test-autoconfigure module.
#Testcontainers
#DataNeo4jTest
public class TestClass {
#TestConfiguration
static class Config {
#Bean
public org.neo4j.ogm.config.Configuration configuration() {
return new Configuration.Builder()
.uri(databaseServer.getBoltUrl())
.credentials("neo4j", databaseServer.getAdminPassword())
.build();
}
}
// your tests
}
For a broader explanation have a look at this blog post. Esp. the section Using with Neo4j-OGM and SDN.

use spring boot data redis Connect to the redis cluster problem

I used spring boot data redis to connect to the redis cluster, using version 2.1.3 The configuration is as follows:
#Bean
#Primary
public RedisConnectionFactory myLettuceConnectionFactory(GenericObjectPoolConfig poolConfig) {
RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
final List<String> nodeList = redisProperties.getCluster().getNodes();
Set<RedisNode> nodes = new HashSet<RedisNode>();
for (String ipPort : nodeList) {
String[] ipAndPort = ipPort.split(":");
nodes.add(new RedisNode(ipAndPort[0].trim(), Integer.valueOf(ipAndPort[1])));
}
redisClusterConfiguration.setPassword(RedisPassword.of(redisProperties.getPassword()));
redisClusterConfiguration.setClusterNodes(nodes);
redisClusterConfiguration.setMaxRedirects(redisProperties.getCluster().getMaxRedirects());
LettuceClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder()
.commandTimeout(redisProperties.getTimeout())
.poolConfig(poolConfig)
.build();
RedisClusterClient clusterClient ;
LettuceConnectionFactory factory = new LettuceConnectionFactory(redisClusterConfiguration,clientConfig);
return factory;
}
However, during the operation, a WARN exception message will always be received as follows:
Well, this seems to be a problem with lettuce, How to map remote host & port to localhost using Lettuce,but I don't know how to use it in spring boot data redis. Any solution is welcome, thank you
I've got the answer, so let's define a ClinentRourse like this:
MappingSocketAddressResolver resolver = MappingSocketAddressResolver.create(DnsResolvers.UNRESOLVED ,
hostAndPort -> {
if(hostAndPort.getHostText().startsWith("172.31")){
return HostAndPort.of(ipStr, hostAndPort.getPort());
}
return hostAndPort;
});
ClientResources clientResources = ClientResources.builder()
.socketAddressResolver(resolver)
.build();
Then through LettuceClientConfiguration.clientResources method set in, the normal work of the lettuce.

Jedis, Cannot get jedis connection: cannot get resource from pool

I have seen answers in couple of threads but didn't work out for me and since my problem occurs occasionally, asking this question if any one has any idea.
I am using jedis version 2.8.0, Spring Data redis version 1.7.5. and redis server version 2.8.4 for our caching application.
I have multiple cache that gets saved in redis and get request is done from redis. I am using spring data redis APIs to save and get data.
All save and get works fine, but getting below exception occasionally:
Cannot get Jedis connection; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool | org.springframework.data.redis.RedisConnectionFailureException: Cannot get Jedis connection; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the poolorg.springframework.data.redis.RedisConnectionFailureException: Cannot get Jedis connection; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
org.springframework.data.redis.connection.jedis.JedisConnectionFactory.fetchJedisConnector(JedisConnectionFactory.java:198)
org.springframework.data.redis.connection.jedis.JedisConnectionFactory.getConnection(JedisConnectionFactory.java:345)
org.springframework.data.redis.core.RedisConnectionUtils.doGetConnection(RedisConnectionUtils.java:129)
org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:92)
org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:79)
org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:191)
org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:166)
org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:88)
org.springframework.data.redis.core.DefaultHashOperations.get(DefaultHashOperations.java:49)
My redis configuration class:
#Configuration
public class RedisConfiguration {
#Value("${redisCentralCachingURL}")
private String redisHost;
#Value("${redisCentralCachingPort}")
private int redisPort;
#Bean
public StringRedisSerializer stringRedisSerializer() {
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
return stringRedisSerializer;
}
#Bean
JedisConnectionFactory jedisConnectionFactory() {
JedisConnectionFactory factory = new JedisConnectionFactory();
factory.setHostName(redisHost);
factory.setPort(redisPort);
factory.setUsePool(true);
return factory;
}
#Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(jedisConnectionFactory());
redisTemplate.setExposeConnection(true);
// No serializer required all serialization done during impl
redisTemplate.setKeySerializer(stringRedisSerializer());
//`redisTemplate.setHashKeySerializer(stringRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericSnappyRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
#Bean
public RedisCacheManager cacheManager() {
RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate());
redisCacheManager.setTransactionAware(true);
redisCacheManager.setLoadRemoteCachesOnStartup(true);
redisCacheManager.setUsePrefix(true);
return redisCacheManager;
}
}
Did anyone faced this issue or have any idea on this, why might this happen?
We were facing the same problem with RxJava, the application was running fine but after some time, no connections could be aquired from the pool anymore. After days of debugging we finally figured out what caused the problem:
redisTemplate.setEnableTransactionSupport(true)
somehow caused spring-data-redis to not release connections. We needed transaction support for MULTI / EXEC but in the end changed the implementation to get rid of this problem.
Still we don't know if this is a bug or wrong usage on our side.
I moved from redis.template to plain jedis.
Added below configuration(can be added in redis template too) for pool and don't see any exception now:
jedisPoolConfig.setMaxIdle(30);
jedisPoolConfig.setMinIdle(10);
for redis template:
jedisConnectionFactory.getPoolConfig().setMaxIdle(30);
jedisConnectionFactory.getPoolConfig().setMinIdle(10);
Same above config can be added in redis template too.
The problem is with the Redis configuration
For me, I was using this property for my local, when I commented on this property, the issue got resolved
#spring.redis.database=12
The correct property will be
spring.redis.sentinel.master=mymaster
spring.redis.password=
spring.redis.sentinel.nodes=localhost:5000
I fixed mine by changing this in my application.yml file:
redis:
password: ${REDIS_SECRET_KEY: null}
to this:
password: ${REDIS_SECRET_KEY:}

Grizzly, sharing spring generated context

I have a standalone spring project and i need to start an embedded rest service with it.
I could be able to start the server with grizzly, my problem is, when i start grizzly server, it creates its own application context. so the instances created by my parent application is not accessible through the REST service.
Is there anyway of sharing the parent application's context between Grizzly server and parent application, other than getting grizzly generated application context.
This is my code for starting the grizzly server.
public class RemotingServer {
private HttpServer httpServer;
private String host;
private int port;
public RemotingServer(String host, int port) {
this.host = host;
this.port = port;
}
public void init() throws Exception {
URI uri = UriBuilder.fromUri("http://" + host + "/").port(port).build();
ResourceConfig rc = new DefaultResourceConfig();
ConfigurableApplicationContext cac =
new ClassPathXmlApplicationContext("classpath:remoting-context.xml");
IoCComponentProviderFactory factory = new SpringComponentProviderFactory(rc, cac);
httpServer = GrizzlyServerFactory.createHttpServer(uri, rc, factory);
httpServer.start();
}
public void stop() {
httpServer.stop();
}
}
I tried setting current context as cac's parent too. Then i got following exception.
java.lang.IllegalStateException: BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext
thanks.
Try this:
ConfigurableApplicationContext cac =
new ClassPathXmlApplicationContext("classpath:remoting-context.xml");
// Have Spring load the context
cac.refresh();
IoCComponentProviderFactory factory = new SpringComponentProviderFactory(rc, cac);

Resources