DATAREDIS-682 - Connect to Redis using unix domain sockets.

We now support Redis connections using OS-native transports through unix domain sockets when using the Lettuce driver. Domain sockets do not require a roundtrip through the networking layer but directly use native epoll or kqueue interfaces.

LettuceConnectionFactory factory = new LettuceConnectionFactory(new RedisSocketConfiguration("/var/run/redis.sock"));

Unix domain socket support requires netty's native epoll/kqueue dependencies matching the runtime environment:

<dependency>
	<groupId>io.netty</groupId>
	<artifactId>netty-transport-native-epoll</artifactId>
	<classifier>linux-x86_64</classifier>
	<version>${netty}</version>
</dependency>

<dependency>
	<groupId>io.netty</groupId>
	<artifactId>netty-transport-native-kqueue</artifactId>
	<classifier>osx-x86_64</classifier>
	<version>${netty}</version>
</dependency>

Original Pull Request: #286
This commit is contained in:
Mark Paluch
2017-10-13 10:46:04 +02:00
committed by Christoph Strobl
parent 5c16a6044c
commit d08ab93421
10 changed files with 386 additions and 53 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2013 the original author or authors.
* Copyright 2011-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,19 +17,25 @@ package org.springframework.data.redis;
import java.util.Properties;
import org.springframework.data.redis.connection.RedisSocketConfiguration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
/**
* Utility class exposing connection settings to connect Redis instances during test execution. Settings can be adjusted
* by overriding these in {@literal org/springframework/data/redis/test.properties}.
*
* @author Costin Leau
* @author Mark Paluch
*/
public abstract class SettingsUtils {
private final static Properties DEFAULTS = new Properties();
private static final Properties SETTINGS;
static {
DEFAULTS.put("host", "127.0.0.1");
DEFAULTS.put("port", "6379");
DEFAULTS.put("socket", "work/redis-6379.sock");
SETTINGS = new Properties(DEFAULTS);
@@ -40,15 +46,44 @@ public abstract class SettingsUtils {
}
}
private SettingsUtils() {}
/**
* @return the Redis hostname.
*/
public static String getHost() {
return SETTINGS.getProperty("host");
}
/**
* @return the Redis port.
*/
public static int getPort() {
return Integer.valueOf(SETTINGS.getProperty("port"));
}
/**
* @return path to the unix domain socket.
*/
public static String getSocket() {
return SETTINGS.getProperty("socket");
}
/**
* Construct a new {@link RedisStandaloneConfiguration} initialized with test endpoint settings.
*
* @return a new {@link RedisStandaloneConfiguration} initialized with test endpoint settings.
*/
public static RedisStandaloneConfiguration standaloneConfiguration() {
return new RedisStandaloneConfiguration(getHost(), getPort());
}
/**
* Construct a new {@link RedisSocketConfiguration} initialized with test endpoint settings.
*
* @return a new {@link RedisSocketConfiguration} initialized with test endpoint settings.
*/
public static RedisSocketConfiguration socketConfiguration() {
return new RedisSocketConfiguration(getSocket());
}
}

View File

@@ -19,11 +19,15 @@ import static org.hamcrest.core.Is.*;
import static org.hamcrest.core.IsEqual.*;
import static org.hamcrest.core.IsNull.*;
import static org.junit.Assert.*;
import static org.junit.Assume.*;
import io.lettuce.core.EpollProvider;
import io.lettuce.core.KqueueProvider;
import io.lettuce.core.RedisException;
import io.lettuce.core.api.async.RedisAsyncCommands;
import io.lettuce.core.api.reactive.BaseRedisReactiveCommands;
import java.io.File;
import java.time.Duration;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
@@ -362,6 +366,25 @@ public class LettuceConnectionFactoryTests {
factory.destroy();
}
@Test // DATAREDIS-687
public void connectsThroughRedisSocket() {
assumeTrue(EpollProvider.isAvailable() || KqueueProvider.isAvailable());
assumeTrue(new File(SettingsUtils.getSocket()).exists());
LettuceClientConfiguration configuration = LettuceTestClientConfiguration.create();
LettuceConnectionFactory factory = new LettuceConnectionFactory(SettingsUtils.socketConfiguration(), configuration);
factory.setShareNativeConnection(false);
factory.afterPropertiesSet();
RedisConnection connection = factory.getConnection();
assertThat(connection.ping(), is(equalTo("PONG")));
connection.close();
factory.destroy();
}
@Test // DATAREDIS-576
public void connectionAppliesClientName() {

View File

@@ -43,6 +43,7 @@ import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisClusterConnection;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.RedisSocketConfiguration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.test.util.ReflectionTestUtils;
@@ -285,6 +286,21 @@ public class LettuceConnectionFactoryUnitTests {
}
}
@Test // DATAREDIS-682
public void socketShouldBeSetOnStandaloneClient() {
LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(new RedisSocketConfiguration());
connectionFactory.afterPropertiesSet();
ConnectionFactoryTracker.add(connectionFactory);
AbstractRedisClient client = (AbstractRedisClient) getField(connectionFactory, "client");
assertThat(client, instanceOf(RedisClient.class));
RedisURI redisUri = (RedisURI) getField(client, "redisURI");
assertThat(redisUri.getSocket(), is("/tmp/redis.sock"));
}
@Test // DATAREDIS-574
public void shouldReadStandalonePassword() {
@@ -337,6 +353,20 @@ public class LettuceConnectionFactoryUnitTests {
assertThat(envConfig.getPassword(), is(equalTo(RedisPassword.of("bar"))));
}
@Test // DATAREDIS-682
public void shouldWriteSocketPassword() {
RedisSocketConfiguration envConfig = new RedisSocketConfiguration();
envConfig.setPassword(RedisPassword.of("foo"));
LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(envConfig,
LettuceClientConfiguration.defaultConfiguration());
connectionFactory.setPassword("bar");
assertThat(connectionFactory.getPassword(), is(equalTo("bar")));
assertThat(envConfig.getPassword(), is(equalTo(RedisPassword.of("bar"))));
}
@Test // DATAREDIS-574
public void shouldReadClusterPassword() {
@@ -415,6 +445,20 @@ public class LettuceConnectionFactoryUnitTests {
assertThat(envConfig.getDatabase(), is(3));
}
@Test // DATAREDIS-682
public void shouldWriteSocketDatabaseIndex() {
RedisSocketConfiguration envConfig = new RedisSocketConfiguration();
envConfig.setDatabase(2);
LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(envConfig,
LettuceClientConfiguration.defaultConfiguration());
connectionFactory.setDatabase(3);
assertThat(connectionFactory.getDatabase(), is(3));
assertThat(envConfig.getDatabase(), is(3));
}
@Test // DATAREDIS-574
public void shouldApplyClientConfiguration() {
@@ -456,6 +500,19 @@ public class LettuceConnectionFactoryUnitTests {
assertThat(connectionFactory.getClusterConfiguration(), is(nullValue()));
}
@Test // DATAREDIS-682
public void shouldReturnSocketConfiguration() {
RedisSocketConfiguration configuration = new RedisSocketConfiguration("/var/redis/socket");
LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(configuration,
LettuceClientConfiguration.defaultConfiguration());
assertThat(connectionFactory.getStandaloneConfiguration(), is(notNullValue()));
assertThat(connectionFactory.getSocketConfiguration(), is(configuration));
assertThat(connectionFactory.getSentinelConfiguration(), is(nullValue()));
assertThat(connectionFactory.getClusterConfiguration(), is(nullValue()));
}
@Test // DATAREDIS-574
public void shouldReturnSentinelConfiguration() {