Skip to content

Commit

Permalink
Redis changes are implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
musab.bozkurt committed Feb 4, 2024
1 parent d83e743 commit 4322ea0
Show file tree
Hide file tree
Showing 17 changed files with 462 additions and 4 deletions.
29 changes: 27 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
<li><a href="#How_To_Run_And_Test_Application">How To Run And Test Application</a></li>
<li><a href="#How_To_Run_And_Test_Dockerfile">How To Run And Test Application with Dockerfile (OPTIONAL)</a></li>
<li><a href="#How_To_Run_And_Test_Docker_Compose">How To Run And Test Application with docker-compose.yml (OPTIONAL)</a></li>
<li><a href="#Redis">Redis Commands</a></li>
<li><a href="#References">References</a></li>
</ol>
</details>

Expand Down Expand Up @@ -45,7 +47,7 @@
- `Micrometer` dependencies were added to track the logs easily
- `Testcontainers` dependencies were added for integration tests
- `docker-compose.yml` contains `Grafana`, `Prometheus` and `Zipkin` to track metrics, `Kafka` for event-driven
architecture
architecture, `Redis` for caching
- `Actuator`: http://localhost:8080/actuator
- `Kafka UI`: http://localhost:9091/
- `Grafana`
Expand Down Expand Up @@ -117,8 +119,31 @@

-------

### Redis

* The following command returns all matched data by `'keyPattern:*'` pattern
* `redis-cli --scan --pattern 'keyPattern:*'`

* The following command deletes all matched data by `'keyPattern:*'` pattern
* `redis-cli KEYS 'keyPattern:*' | xargs redis-cli DEL`

* The following command finds `TYPE` in redis with `KEY`
* `TYPE key` -> `TYPE xxx:hashedIdOrSomethingElse`

* The following commands search by `TYPE`

* for `"string" TYPE`: `get key`
* for `"hash" TYPE`: `hgetall key`
* for `"list" TYPE`: `lrange key 0 -1`
* for `"set" TYPE`: `smembers key`
* for `"zset" TYPE`: `zrange key 0 -1 withScores`

* RedisInsight:

### References

- [Metrics Made Easy Via Spring Actuator, Docker, Prometheus, and Grafana](https://www.youtube.com/watch?v=Utv7MWgNTvI)
- https://prometheus.io/docs/prometheus/latest/installation/#volumes-bind-mount
- [Spring Boot Rest Controller Unit Test with @WebMvcTest](https://www.bezkoder.com/spring-boot-webmvctest/)
- [Spring Boot Rest Controller Unit Test with @WebMvcTest](https://www.bezkoder.com/spring-boot-webmvctest/)
- [Redis Commands](https://auth0.com/blog/introduction-to-redis-install-cli-commands-and-data-types/)
- [Running RedisInsight using Docker Compose](https://collabnix.com/running-redisinsight-using-docker-compose/)
14 changes: 14 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,20 @@ services:
ports:
- "9411:9411"

redis:
image: redis
restart: always
container_name: redis
ports:
- "6378:6379" # Default port is 6379

redisinsight:
image: redislabs/redisinsight:latest
restart: always
container_name: redisinsight
ports:
- '8001:8001'

volumes:
zookeeper_data:
driver: local
Expand Down
22 changes: 22 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,22 @@
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

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

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

<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.26.0</version>
</dependency>

<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
Expand Down Expand Up @@ -142,6 +158,12 @@
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>kafka</artifactId>
<scope>test</scope>
</dependency>

</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.mb.livedataservice.api.controller;

import com.mb.livedataservice.data.entity.RedisHashData;
import com.mb.livedataservice.service.RedisHashService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
@RequiredArgsConstructor
public class RedisController {

private final RedisHashService redisHashService;

/**
* Create RedisHashData
*/
@PostMapping("/redis-hash")
public RedisHashData createRedisHashData() {
log.info("Received a request to create RedisHashData. createRedisHashData.");
return redisHashService.save(RedisHashData.builder().destination("hello_world").build());
}
}
61 changes: 61 additions & 0 deletions src/main/java/com/mb/livedataservice/config/CacheConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.mb.livedataservice.config;

import com.mb.livedataservice.utils.RedisConstants;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisKeyValueAdapter;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
import org.springframework.session.data.redis.config.ConfigureRedisAction;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableCaching
@AutoConfigureAfter(RedisAutoConfiguration.class)
@ConditionalOnClass({RedisOperations.class, RedisConnectionFactory.class, RedisCacheConfiguration.class})
@EnableRedisRepositories(enableKeyspaceEvents = RedisKeyValueAdapter.EnableKeyspaceEvents.ON_STARTUP)
public class CacheConfig {

@Bean(name = "cacheManager")
@ConditionalOnMissingBean(name = "cacheManager")
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration expireIn1Day = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofDays(1));

Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();

cacheConfigurations.put(RedisConstants.CACHE_KEY, expireIn1Day);

return RedisCacheManager.RedisCacheManagerBuilder
.fromConnectionFactory(connectionFactory)
.withInitialCacheConfigurations(cacheConfigurations)
.build();
}

/*
* If the Redis client is protected, add this config bean. Otherwise, this bean can be removed.
*
* However, if you can run any commands in redis, please run the following command to enable org.springframework.data.redis.core.RedisKeyExpiredEvent
* command -> redis-cli config set notify-keyspace-events xE
*
* notify-keyspace-events should be xE.
* To get the value of notify-keyspace-events run this -> config get "notify-keyspace-events"
*
* This means that Spring Session cannot configure Redis Keyspace events for you.
* To disable the automatic configuration add ConfigureRedisAction.NO_OP as a bean.
* */
@Bean
public static ConfigureRedisAction configureRedisAction() {
return ConfigureRedisAction.NO_OP;
}
}
22 changes: 22 additions & 0 deletions src/main/java/com/mb/livedataservice/config/RedissonConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.mb.livedataservice.config;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RedissonConfig {

@Bean
@ConditionalOnProperty(value = "redisson.enabled", havingValue = "true")
public RedissonClient redissonClient(@Value("${redisson.url}") String address) {
Config config = new Config();
config.useSingleServer().setAddress(address);

return Redisson.create(config);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.mb.livedataservice.data.entity;

import jakarta.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.redis.core.RedisHash;
import org.springframework.data.redis.core.TimeToLive;
import org.springframework.data.redis.core.index.Indexed;

import java.util.UUID;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RedisHash(value = "RedisHashData")
public class RedisHashData {

@Id
@Builder.Default
private String id = UUID.randomUUID().toString();

@Indexed
private String redisHashCode;

@Indexed
private String reference;

private String destination;

private int count;

@TimeToLive
@Builder.Default
private long expiration = 60L;

public RedisHashData(String redisHashCode, String reference) {
this.redisHashCode = redisHashCode;
this.reference = reference;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.mb.livedataservice.data.repository;

import com.mb.livedataservice.data.entity.RedisHashData;
import org.springframework.data.repository.CrudRepository;

public interface RedisHashDataRepository extends CrudRepository<RedisHashData, String> {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.mb.livedataservice.queue;

import com.mb.livedataservice.data.entity.RedisHashData;
import com.mb.livedataservice.service.RedisHashService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.data.redis.core.RedisKeyExpiredEvent;
import org.springframework.stereotype.Component;

@Slf4j
@Component
@AllArgsConstructor
public class RedisKeyExpiredEventListenerImpl {

private final RedisHashService redisHashService;

@EventListener(condition = "#event.keyspace == 'RedisHashData'")
public void redisExpiredKeyEventForRedisHashData(RedisKeyExpiredEvent<?> event) {
log.info("Redis key expired event log. RedisHashData - event:{}", event.toString());
redisHashService.delete(RedisHashData.builder().id(new String(event.getId())).build());
}
}
16 changes: 16 additions & 0 deletions src/main/java/com/mb/livedataservice/service/RedisHashService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.mb.livedataservice.service;

import com.mb.livedataservice.data.entity.RedisHashData;

import java.util.Optional;

public interface RedisHashService {

RedisHashData save(RedisHashData redisHashData);

Optional<RedisHashData> findById(String id);

void delete(RedisHashData redisHashData);

void deleteRedisHashDataById(String redisHashDataId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.mb.livedataservice.service;

public interface RedisTokenStoreService {

String getToken(String tokenId, String key);

void storeToken(String tokenId, String key);

void deleteToken(String tokenId, String key);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.mb.livedataservice.service.impl;

import com.mb.livedataservice.data.entity.RedisHashData;
import com.mb.livedataservice.data.repository.RedisHashDataRepository;
import com.mb.livedataservice.service.RedisHashService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.Optional;

@Slf4j
@Service
@RequiredArgsConstructor
public class RedisHashServiceImpl implements RedisHashService {

private final RedisHashDataRepository redisHashDataRepository;

@Override
public RedisHashData save(RedisHashData redisHashData) {
return redisHashDataRepository.save(redisHashData);
}

@Override
public Optional<RedisHashData> findById(String id) {
return redisHashDataRepository.findById(id);
}

@Override
public void delete(RedisHashData redisHashData) {
redisHashDataRepository.delete(redisHashData);
}

@Override
public void deleteRedisHashDataById(String redisHashDataId) {
log.info("Deleting RedisHashData by ID: '{}'.", redisHashDataId);
redisHashDataRepository.deleteById(redisHashDataId);
}
}
Loading

0 comments on commit 4322ea0

Please sign in to comment.