Redisson์ ์ค๋ฌด์ ์ ์ฉํ๋ฉด์ ๊ฒผ์๋ ์ฐ๊ฒฐ ๋ฌธ์ ์ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ํ๋ฒ ์ ๋ฆฌํด๋ณด๋ ค๊ณ ํ๋ค.
์ฒซ ๋ฒ์งธ๋ก, ๋ค์๊ณผ ๊ฐ์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค.
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.redisson.api.RedissonClient]: Factory method 'redissonClient' threw exception; nested exception is org.redisson.client.RedisConnectionException: Unable to connect to Redis server: [Redis ์ฃผ์]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185)
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653)
... 38 common frames omitted
Caused by: org.redisson.client.RedisConnectionException: Unable to connect to Redis server: [addr=redis://Redis ์ฃผ์:ํฌํธ]
at org.redisson.connection.pool.ConnectionPool.lambda$createConnection$0(ConnectionPool.java:132)
at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:859)
at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:837)
at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:506)
at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2088)
at org.redisson.connection.pool.ConnectionPool.promiseFailure(ConnectionPool.java:294)
at org.redisson.connection.pool.ConnectionPool.lambda$createConnection$6(ConnectionPool.java:253)
at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:859)
at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:837)
at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:506)
at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2088)
at org.redisson.client.RedisClient$1$1.run(RedisClient.java:248)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:164)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:469)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:503)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:986)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: java.util.concurrent.CompletionException: org.redisson.client.RedisTimeoutException: Command execution timeout for command: (AUTH), params: (password masked), Redis client: [addr=redis://Redis ์ฃผ์:ํฌํธ]
at java.base/java.util.concurrent.CompletableFuture.encodeRelay(CompletableFuture.java:367)
at java.base/java.util.concurrent.CompletableFuture.completeRelay(CompletableFuture.java:376)
at java.base/java.util.concurrent.CompletableFuture$UniRelay.tryFire(CompletableFuture.java:1019)
at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:506)
at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2088)
at org.redisson.client.RedisConnection.lambda$async$0(RedisConnection.java:257)
at io.netty.util.HashedWheelTimer$HashedWheelTimeout.run(HashedWheelTimer.java:715)
at io.netty.util.concurrent.ImmediateExecutor.execute(ImmediateExecutor.java:34)
at io.netty.util.HashedWheelTimer$HashedWheelTimeout.expire(HashedWheelTimer.java:703)
at io.netty.util.HashedWheelTimer$HashedWheelBucket.expireTimeouts(HashedWheelTimer.java:790)
at io.netty.util.HashedWheelTimer$Worker.run(HashedWheelTimer.java:503)
... 2 common frames omitted
Caused by: org.redisson.client.RedisTimeoutException: Command execution timeout for command: (AUTH), params: (password masked), Redis client: [addr=redis://Redis ์ฃผ์:ํฌํธ]
at org.redisson.client.RedisConnection.lambda$async$0(RedisConnection.java:256)
... 7 common frames omitted
์ค๋ฅ๋ฅผ ๋ณด๋ฉด Redis ์๋ฒ์ ์ฐ๊ฒฐํ ์ ์์ด์ ํ์์์์ด ๋ฐ์ํ๋ค๋๋ฐ, ์ด์ํ ์ ์ ๊ธฐ์กด์ ์ฌ์ฉํ๋ Lettuce์์๋ ์ ์์ ์ผ๋ก ์ฐ๊ฒฐ์ด ๋๊ณ , Redisson Client์์๋ง ์ฐ๊ฒฐ์ด ๊ณ์ํด์ ์คํจํ๋ ์ ์ด์๋ค.
Lettuce์๋ ์ ์์ ์ผ๋ก ์ฐ๊ฒฐ์ด ๋๊ธฐ์, password๊ฐ ํ๋ฆฐ ๋ฌธ์ ๋ ์๋๊ณ , Redisson๋ Lettuce์ ๋ง์ฐฌ๊ฐ์ง๋ก SSL ํต์ ์ ํ๊ธฐ ์ํด setSslEnableEndpointIdentification(true)๋ฅผ ์ค์ ํด ์ฃผ์๋๋ฐ ๊ณ์ ์คํจํ๋ค. ๋ฌธ์๋ Baeldung์ Redisson ๊ฐ์ด๋ ์ฌ๊ธฐ๋ฅผ ์ฐธ๊ณ ํด์ ์ค์ ํ๋๋ฐ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
@Configuration
@EnableRedisRepositories(enableKeyspaceEvents = RedisKeyValueAdapter.EnableKeyspaceEvents.ON_STARTUP)
public class RedisConfig {
...
private static final String REDISSON_HOST_PREFIX = "redis://";
private final ObjectMapper objectMapper;
@Value("${redis.host}")
private String redisHost;
@Value("${redis.port}")
private Integer redisPort;
@Value("${redis.database}")
private int redisSessionDatabase;
@Value("${redis.password}")
private String redisPassword;
@Bean
public RedisConnectionFactory redisConnectionFactory() {
LettuceConnectionFactory connectionFactory =
new LettuceConnectionFactory(redisStandaloneConfiguration(), lettuceClientConfiguration());
connectionFactory.setDatabase(this.redisSessionDatabase);
return connectionFactory;
}
@Bean
RedisStandaloneConfiguration redisStandaloneConfiguration() {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(this.redisHost);
redisStandaloneConfiguration.setPort(this.redisPort);
redisStandaloneConfiguration.setPassword(this.redisPassword);
return redisStandaloneConfiguration;
}
@Bean
LettuceClientConfiguration lettuceClientConfiguration() {
return LettuceClientConfiguration
.builder()
.useSsl()
.build();
}
@Bean
public RedissonClient redissonClient() {
Config config = new Config();
config.useSingleServer()
.setAddress(REDISSON_HOST_PREFIX + this.redisHost + ":" + this.redisPort)
.setPassword(this.redisPassword)
.setSslEnableEndpointIdentification(true);
return Redisson.create(config);
}
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());
redisTemplate.setKeySerializer(new StringRedisSerializer());
Jackson2JsonRedisSerializer<String> stringJackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(String.class);
stringJackson2JsonRedisSerializer.setObjectMapper(objectMapper);
redisTemplate.setValueSerializer(stringJackson2JsonRedisSerializer);
return redisTemplate;
}
...
}
๋ณด์์ ๊ฐ๋ฆด ๊ฑฐ ๋ค ๊ฐ๋ฆฌ๊ณ , ๋ณด์ฌ์ค ์ ์๋ Config๋ ์์ ๊ฐ๋ค.
ํผ์ ํ 3-4์๊ฐ ์ฝ์งํ๋ค๊ฐ, ํ์๋ถ์ด ํด๊ฒฐ์ฑ ์ ์ฐพ์์ฃผ์๋๋ฐ, Redisson ์ฐ๊ฒฐ ์ค์ ์์ ์ฌ์ฉํ REDISSON_HOST_PREFIX๊ฐ ์์ธ์ด์๋ค. SSL ํต์ ์ ์ํด์๋ "redis://"๊ฐ ์๋ "rediss://"๋ก ์ค์ ํด์ผ ํ๋ ๊ฒ์ด๋ค. Lettuce์์๋ LettuceClientConfiguration์์ userSsl()์ ํ์ฑํํ์ฌ ์๋์ผ๋ก "rediss://"๋ก ์ค์ ์ด ๋์ด์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ง ์์๋ ๊ฒ์ด์๋ค.
์ค์ ๋ก lettuce-core ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ RedisURI.java ํ์ผ์ ํ์ธํ๋ฉด 166๋ฒ์งธ ์ค์ URI_SCHEME_REDIS_SECURE๊ฐ "rediss"๋ก ์ค์ ๋์ด ์์์ ํ์ธํ ์ ์๋ค.(๋ถ๊ฐ์ ์ธ ์ค์ ์ ๊ณต์ ์ํค๋ฅผ ํ์ธํ์.)
์ธํฐ๋ท์ ๋ง์ Redisson Client ์ ์ฉ ์์๋ค์ ๋ก์ปฌ ํ๊ฒฝ์์ ๋์ํ๋ ๊ฒ์ ๊ธฐ๋ฐ์ผ๋ก ํ๋ฏ๋ก, secure๊น์ง ์ค์ ํ๋ ๊ฒฝ์ฐ๊ฐ ๋๋ฌผ์ด "redis://"๋ก ์ค์ ํ ์ฝ๋๋ค์ด ๋ง๋ค. ํ์ง๋ง ๋ณด์์ ๊ณ ๋ คํ ์ค๋ฌด ํ๊ฒฝ์์๋ ์ด๋ฌํ ์ธ๋ถ ์ค์ ์ด ์ค์ํ๋ฏ๋ก ๊ผญ ํ์ธํ์ฌ ๋์ฒ๋ผ ์๊ฐ์ ๋ญ๋นํ๋ ์ผ์ด ์์์ผ๋ฉด ์ข๊ฒ ๋ค.(ํนํ๋ ์ฐธ๊ณ ํ๋ ๋ฒจ๋ฉ์ ๋ฌธ์์๋ ๋์์์ง ์์๋ค.)
๋ ๋ฒ์งธ๋ ์๋์ค๋ฅ์ด๋ค.
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'javax.sql.DataSource' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier(value="securityDataSource")}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1799)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1394)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1309)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:887)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
... 188 common frames omitted
NoSuchBeanDefinitionException ์ค๋ฅ๊ฐ ๋ฐ์ํ๋๋ฐ, securityDataSource ๋น์ ์ฐพ์ง ๋ชปํ๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
์ฝ๋๋ฅผ ์ดํด๋ณด๋ฉด, DataSourceConfig ํด๋์ค์ ์ฌ๋ฌ DataSource ๋น๋ค์ด ์ ์๋์ด ์๋๋ฐ, ๊ทธ์ค securityDataSource ๋น์ด ์ฃผ์ ๋๋ ค๊ณ ํ ๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
@Slf4j
@Configuration
@RequiredArgsConstructor
public class DataSourceConfig {
// ... ๋ค๋ฅธ ๋น ์ ์๋ค
@Bean
public DataSource securityDataSource() {
return getDataSource();
}
private DataSource getDataSource() {
JndiObjectFactoryBean jndi = new JndiObjectFactoryBean();
jndi.setProxyInterface(DataSource.class);
jndi.setJndiName(JndiResource.JNDI_NAME);
jndi.setResourceRef(true);
try {
jndi.afterPropertiesSet();
return (DataSource) jndi.getObject();
} catch (NamingException ne) {
log.error(JndiResource.JNDI_NAME, ne);
return null;
}
}
// ... ๋ค๋ฅธ ๋ฉ์๋๋ค
}
ํน์ดํ ์ ์, ์ ๊ธฐ์ JNDI๋ก DataSource ์ฃผ์ ํ๋ ๊ฒ ์๋๋ผ ์๋์ฒ๋ผ HikariCP๋ฅผ ์ฌ์ฉํด์ ์ฃผ์ ํ๋ฉด ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ง ์๋๋ค๋ ์ ์ด๋ค.
@Slf4j
@Configuration
@RequiredArgsConstructor
public class DataSourceConfig {
// ... ์์ ๋์ผ
public DataSource securityDataSource() {
return getDataSource();
}
private DataSource getDataSource() {
HikariConfig config = new HikariConfig();
config.setDriverClassName(Driver.class.getName());
config.setJdbcUrl("url");
config.setUsername("username");
config.setPassword("password");
config.setMaximumPoolSize(size);
return new HikariDataSource(config);
}
}
์๊ฒ๋ ํผ์ ๋ณ์ง ๋คํด๋ณด๋ค๊ฐ ๊ฒฐ๊ตญ ์ ์ฅ๋์ด ์์ธ์ ์ฐพ์์ฃผ์ จ๋๋ฐ, ๋ฌธ์ ์ ๊ทผ์์ RedisConfig์์ ObjectMapper๋ฅผ ์ฃผ์ ๋ฐ๋ ๊ณผ์ ์์ ๋ฐ์ํ๋ค.
ServletConfig์๋ ObjectMapper๊ฐ ์ ์๋์ด ์์๊ณ , PublicAuthInterceptor์๋ DB ์ ๊ทผ์ด ํ์ํ PrincipalRepository๊ฐ ํฌํจ๋์ด ์์๋๋ฐ, ์ด๋ก ์ธํด securityDataSource๊ฐ ์์ง ์์ฑ๋๊ธฐ ์ ์ ์ ๊ทผํ๋ ค ํด์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ ๊ฒ์ด๋ค.
@Configuration
@RequiredArgsConstructor
public class ServletConfig implements WebMvcConfigurer {
// ... ๋ค๋ฅธ ์ ์๋ค
private final PublicAuthInterceptor publicAuthInterceptor;
@Bean
public ObjectMapper objectMapper() {
// ... objectMapper ์ค์
}
// ...
}
@Slf4j
@Component
@RequiredArgsConstructor
public class PublicAuthInterceptor implements HandlerInterceptor {
private final PrincipalRepository principalRepository;
// ...
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// principalRepository ์ฌ์ฉ ์ฝ๋
}
// ...
}
ํด๊ฒฐ์ฑ ์ ๊ฐ๋จํ๋๋ฐ, ObjectMapper๋ฅผ ServletConfig์์ ๋ถ๋ฆฌํ์ฌ ์๋์ฒ๋ผ ๋ณ๋์ ํด๋์ค์์ ์ฃผ์ ํด ์ฃผ๋ฉด ํด๊ฒฐ๋์๋ค.
@Configuration
public class ObjectMapperConfig {
@Bean
public ObjectMapper objectMapper() {
// ... objectMapper ์ค์
}
}
์ดํด๋์ง ์๋ ์ ๋ค
๊ฒฐ๊ตญ... ํด๊ฒฐ์ ํ์ง๋ง, ์์ง๊น์ง ์ RedissonClient ๋น์ ์ถ๊ฐํ๋ฉด JNDI ์ฃผ์ ์์๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๊ณ , HikariCP ์ฃผ์ ์์๋ ๋ฐ์ํ์ง ์๋์ง๋ฅผ ์ ํํ ๋ชจ๋ฅด๊ฒ ๋ค. RedisConfig์์ Redisson ์ฐ๊ฒฐ์ ์ํ ์๋ RedissClient ๋น๋ง ๋นผ๋ฉด JNDI ์ฃผ์ ์ผ๋ก๋ ์ ํ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ง ์๊ธฐ ๋๋ฌธ์ด๋ค.
@Bean
public RedissonClient redissonClient() {
Config config = new Config();
config.useSingleServer()
.setAddress(REDISSON_HOST_PREFIX + this.redisHost + ":" + this.redisPort)
.setPassword(this.redisPassword)
.setSslEnableEndpointIdentification(true);
return Redisson.create(config);
}
๊ทธ๋ฆฌ๊ณ RedissonClient ์์ด, ๊ธฐ์กด์ฒ๋ผ LettuceClient๋ง ์์ผ๋ฉด HikariCP, JNDI ์ฃผ์ ์ ๋ถ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ง ์๊ธฐ์ ๋๋์ฑ ์ดํด๊ฐ ๊ฐ์ง ์๋๋ค. RedissonClient๋ Datasource๋ ๊ด๊ณ๊ฐ ์๋ ๊ฑธ๊น..?
'BackEnd๐ฑ > DB & SQL' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[MySQL] ์ฌ์ฉ์ ์์ฑ ๋ฐ ๊ถํ ๋ถ์ฌ ๋ฐฉ๋ฒ (0) | 2023.10.06 |
---|---|
MySQL์์ VARCHAR์ TEXT์ ์ฐจ์ด (1) | 2023.09.17 |
Redis์ ๋ฐ์ดํฐ ํ์ , ๋ช ๋ น์ด์ ํ์ฉ ์ฌ๋ก (0) | 2023.09.05 |
Docker ์ปจํ ์ด๋์ MariaDB ์ค์นํ๊ธฐ (4) | 2023.05.12 |
์ธ๋ฑ์ค๋ฅผ ์ํ๋ ์ฟผ๋ฆฌ๋ค (0) | 2023.03.23 |
[MySQL] UPSERT (ON DUPLICATE KEY UPDATE) (0) | 2022.05.23 |
๋๊ธ