Spring Boot&Caching 2 – Redis

Merhabalar.

Spring Boot&Caching 1 yazısında Spring Boot projesinde Spring’in caching özelliğinin nasıl kullanılabileceğine değinmiştik. Bu yazıda ise popüler ve yaygın bir cachleme aracı/inmemory db olan Redis’in Spring Boot projesinde nasıl kullanabileceğine bakacağız.

Başlamadan önce bu uygulamadaki örnek kodlara https://github.com/ilkgunel/SpringBootAndCachingTutorial/tree/spring-data-redis-example adresinden erişebilirsiniz.

Başlayalım.

AMAÇ:

Bu yazıda bir Spring Boot projesinin Redis ile birlikte nasıl kullanılabileceğinin gösterilmesi amaçlanmaktadır.

VERİ SETİ:

Veritabanımda driver tablomda şu şekilde 10 kayıt bulunmakta. Şimdi yapacağımız örnekte ben id ile bir sorgulama yapacağım ve redis ile cache’lemenin bu sorgulamadaki performans etkisini inceleyeceğiz.

ÖRNEK KODLAR:

Bu yazıda bir önceki yazıda yaptığımız projenin neredeyse aynısını yapacağız. O yüzden farklı ya da çok yeni bir kodumuz olmayacak, sadece onun üstüne eklemeler yapacağız.

Öncelikle pom.xml dosyamıza Spring Boot ve Redis’i en optimal şekilde birlikte kullanmamızı sağlayacak spring-boot-starter-data-redis kütüphanesini eklemeliyiz.

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

Bir Spring Boot projesi Redisson gibi başka bir üçüncü parti library ile de Redis ile bağlanabilir ancak söz konusu Spring Boot projesi olunca yine Spring’in sunduğu bir çözümü kullanmak daha avantajlı olabiliyor.

Ardından application.properties dosyamızda da caching için Redis’i kullanacağımızı Spring Boot’a söylememiz gerekiyor. Aşağıdaki 3 property’i ekleyerek Spring Boot ve Redis arasındaki bağlantıyı sağlayabiliriz.

spring.cache.type=redis
spring.redis.host=localhost
spring.redis.port=6379

  • spring.cache.type property’si ile kullanacağımız caching’in hangisi olduğunu söylüyoruz. Bir Redis kullanacağımız için redis atadık değerine.
  • spring.redis.host property’si ise bağlanacağımız redis’in bağlantı adresini belirtiyor.
  • spring.redis.port property’si de redis uygulamasının dışarıdan gelen istekleri kabul ettiği port’u taşıyor.

Şimdi kodları incelemeye geçelim.

DriverController sınıfına mevcutta var olan bir driver’ı update edebilmek için şöyle bir API ekleyelim:

@PutMapping(value = "/{driverId}") 
public ResponseEntity<HttpStatus> updateDriverById(@PathVariable(value = "driverId") Long driverId, @RequestBody Driver driver) {
    Instant start = Instant.now();

    driverService.updateDriverById(driverId, driver);

    Instant finish = Instant.now();
    long time = Duration.between(start, finish).toMillis();
    logger.info("Elapsed Time in updateDriverById: {} ms", time);

    return new ResponseEntity<>(HttpStatus.OK);
}

updateDriverById metodu kendisine gelen API isteğinde gönderilen id bilgisi ve güncelleme talep edilen Driver objesini driverService’deki updateDriverById metoduna gönderiyor. Burada metodun başında ve return’den önce arada geçen zamanı hesaplayan satırlarımız var. Bu metotun doğrudan cache’e etkisi yok fakat arada ne kadar süre geçiyor, onu görelim diyerek ekledim.

Şimdi DriverService içindeki updateDriverById metoduna bakalım.

@CachePut(value = "drivers", key = "#driverId") 
public Driver updateDriverById(Long driverId, Driver incomingDriver) {
    Driver existingDriver = driverRepository.findById(driverId).orElseThrow(() -> new RuntimeException("Couldn't find any driver with that driverId!"));
    existingDriver.setName(incomingDriver.getName());
    existingDriver.setSurname(incomingDriver.getSurname());
    driverRepository.save(existingDriver);
    return existingDriver;
}

DriverService içindeki updateDriverById metodu kendisine gelen id parametresi ile db’de böyle bir kayıt var mı diye kontrol edip varsa gelen objedeki bilgileri db’deki mevcut objenin üstüne yazıyor ve onu geri kaydediyor. Eğer gelen id ile böyle bir kayıt yoksa da hata fırlatıyor. Burada önemli olan nokta metodun işaretlendiği @CachePut notasyonu. Biz @CachePut notasyonu ile db’deki bir kaydı güncellerken aynı anda cache’deki veriyi de güncelleyebiliyoruz. Böylece o kayıt için yapacağımız yeni getirmelerde db’deki güncel hali ile eşlenik olan cache kaydını almaya devam edebileceğiz.

Notasyona verilen value attribute’unu küme ismi olarak düşünebilirsiniz. Yani ben burada value’ya drivers vererek cache içerisinde sürücülerin toplandığı bir küme oluşturmuş oluyorum. key attribute’u ise küme içerisindeki her bir elemanın cache’leneceği unique bir bilgidir. Biz her bir sürücüyü db’deki id bilgisi ile cachelediğimiz için burada da key bilgisine metodada parametre olarak gelen driverId bilgisini geçtik.

Şimdi ufak bir demo yapalım.

İlk olarak http://localhost:8080/driver/driver/7 adresine istek gönderip 7 nolo sürücüyü getirtiyorum.

Ardından Redis Cache’i kontrol ettiğimde bu kayıt için cache’e veri koyulduğunu da görüyorum.

Logu kontrol ettiğimde ise işlemin tamamlanma süresi 721 ms olarak görünüyor:

2023-03-17 09:00:19.999 INFO 22430 --- [nio-8080-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2023-03-17 09:00:20.000 INFO 22430 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2023-03-17 09:00:20.005 INFO 22430 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Completed initialization in 5 ms
2023-03-17 09:00:20.826 INFO 22430 --- [nio-8080-exec-2] com.ilkaygunel.api.DriverController : Elapsed Time in getDriverById: 721 ms

Şimdi sürücüyü getirme isteğini 3 kez tekrarlıyorum ve sürenin 32 ms ile 58 ms arasında değiştiğini görüyorum.

2023-03-17 09:00:19.999 INFO 22430 --- [nio-8080-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2023-03-17 09:00:20.000 INFO 22430 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2023-03-17 09:00:20.005 INFO 22430 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Completed initialization in 5 ms
2023-03-17 09:00:20.826 INFO 22430 --- [nio-8080-exec-2] com.ilkaygunel.api.DriverController : Elapsed Time in getDriverById: 721 ms
2023-03-17 09:05:42.117 INFO 22430 --- [nio-8080-exec-4] com.ilkaygunel.api.DriverController : Elapsed Time in getDriverById: 48 ms
2023-03-17 09:06:20.972 INFO 22430 --- [nio-8080-exec-6] com.ilkaygunel.api.DriverController : Elapsed Time in getDriverById: 32 ms
2023-03-17 09:06:25.276 INFO 22430 --- [nio-8080-exec-7] com.ilkaygunel.api.DriverController : Elapsed Time in getDriverById: 58 ms

Şimdi de http://localhost:8080/driver/7 adresine HTTP PUT isteği gönderiyorum. Mika Hakkinen olan sürücü ismi ve soyismi bilgisi Eddie Irvine olarak değiştirilecek.

Bu isteğin ardından tekrar http://localhost:8080/driver/driver/7 adresine bir istek geçiyorum ve bana güncellenen şekilde isim ve soyisim bilgisi Eddie Irvine olarak dönüyor. Logu kontrol ettiğimde ise güncelleme işleminin 95 ms sürdüğü, veriyi cache’den getirmenin ise 3 ms sürdüğünü görüyorum. Gördüğünüz gibi veriyi hem cache’de hem db’de aynı anda güncelleyebiliyorum ve güncellenmiş halini cache’den yine oldukça hızlı bir şekilde alabiliyorum.

2023-03-17 09:00:19.999 INFO 22430 --- [nio-8080-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2023-03-17 09:00:20.000 INFO 22430 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2023-03-17 09:00:20.005 INFO 22430 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Completed initialization in 5 ms
2023-03-17 09:00:20.826 INFO 22430 --- [nio-8080-exec-2] com.ilkaygunel.api.DriverController : Elapsed Time in getDriverById: 721 ms
2023-03-17 09:05:42.117 INFO 22430 --- [nio-8080-exec-4] com.ilkaygunel.api.DriverController : Elapsed Time in getDriverById: 48 ms
2023-03-17 09:06:20.972 INFO 22430 --- [nio-8080-exec-6] com.ilkaygunel.api.DriverController : Elapsed Time in getDriverById: 32 ms
2023-03-17 09:06:25.276 INFO 22430 --- [nio-8080-exec-7] com.ilkaygunel.api.DriverController : Elapsed Time in getDriverById: 58 ms
2023-03-17 09:09:43.950 INFO 22430 --- [nio-8080-exec-9] com.ilkaygunel.api.DriverController : Elapsed Time in updateDriverById: 95 ms
2023-03-17 09:09:52.757 INFO 22430 --- [nio-8080-exec-1] com.ilkaygunel.api.DriverController : Elapsed Time in getDriverById: 3 ms

Şimdi de bir verinin cache’den nasıl temizlenebileceği konusuna bakalım.

Bir veriyi db’den sildiğiniz ya da bir sonraki istekte cache’e yenilenerek yüklensin dediğiniz durumlarda veriyi cache’den @CacheEvict notasyonu ile silebiliriz. Onun da örnek koduna bakalım.

@DeleteMapping(value = "/{driverId}") 
public ResponseEntity<HttpStatus> deleteDriverById(@PathVariable(value = "driverId") Long driverId) {
    driverService.deleteDriverById(driverId);
    return new ResponseEntity<>(HttpStatus.OK);
}

DriverController içerisindeki deleteDriverById metodu gelen id bilgisini driverService içerisindeki deleteDriverById metoduna geçiyor.

DriverService içerisindeki deleteDriverById metoduna bakalım.

@CacheEvict(value = "drivers", key = "#driverId") 
public void deleteDriverById(Long driverId) {
    driverRepository.deleteById(driverId);
}

deleteDriverById metodumuz db’deki driver’ı silerken aynı zamanda cache’deki drivers kümesinden ilgili id’li driver’ı silecek.

Şimdi bu örnekleyelim.

Öncelikle http://localhost:8080/driver/driver/8 adresine istek geçiyorum ve 8 nolu driver’ı getirtiyorum. Akabinde cache’deki verileri kontrol ediyorum:

Gördüğünüz gibi cache’de hem 7 hem de 8 nolu sürücüler için veri mevcut halde. Şimdi de http://localhost:8080/driver/8 adresine HTTP DELETE isteği gönderiyorum.

Veritabanını kontrol ettiğimde kaydın DB’den silindiği görülebiliyor.

Cache’i kontrol ettiğimde ise veri cache’den de silinmiş.

Bu yazıda anlatacaklarım da bu kadar arkadaşlar. Bir sonraki yazıda Spring Boot ve MemCache’in birlikte kullanımına değinmeye çalışacağız.

Görüşene kadar hoşçakalın.

Latest posts by İlkay Günel (see all)

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir