SpringData ve Redis
Bir önceki yazıda Redis’in ne olduÄŸuna, kısaca geliÅŸtirme ortamınız için nasıl Redis kurulumu yapabileceÄŸinize deÄŸinmiÅŸtik. Bu yazıda ise bir seviye daha ileriye gidip spring-data kullanarak, Redis ile nasıl haberleÅŸebileceÄŸimize bakalım.
Projemiz basit bir “Hello, World” tipi bir uygulama olucak. Yani kısaca String tipindeki key-value ikililerinin nasıl Redis üzerinde tutulacağına bakacağız. Basitçe Redis’in nasıl kullanıldığını anladıktan sonra, cache’lemek için Redis’i nasıl kullanacağımıza deÄŸineceÄŸiz. Projeyi geliÅŸtirirken spring-boot ve spring-Data kütüphanelerini kullanacağız. Ben geliÅŸtirme ortamı olarak Intellij 14 kullanıyorum. Temel sebebi spring-boot projelerini çok rahatlıkla oluÅŸturabilmenizdir. Fakat isterseniz https://start.spring.io adresinden de aynı ÅŸekilde bir porje oluÅŸturabilir ve bunu favori geliÅŸtirme ortamınıza ekleyebilirsiniz. Build aracı olarak her zamanki gibi maven kullanacağım. Spring boot bizim için herÅŸeyi hallettiÄŸinden, pom.xml ile ya da baÅŸka herhangibir xml dosyası ile uÄŸraÅŸmayacağız. Ondan gradle kullanmamanız için de bir sebep yok.
Projenin Yaratılması
Projemizin ne olduğuna kısaca değindiğimize göre şimdi projemizi nasıl yaratılacağına değinebiliriz. Dediğim gibi ben intellij kullanarak anlatacağım ama start.spring.io üzerinden de aynı işlemleri gerçekleyebilirsiniz.
Öncelikle yeni bir proje yaratıyoruz. Proje tipi olarak Spring Initializr seçiyoruz.
Daha sonra gelen ekranda hangi kütüphaneleri kullanacağımızı seçiyoruz. Biz sadece spring-data ve Redis kullanacağımız için, sadece Redis’i seçiyoruz.
Daha sonra gelen ekranda proje bilgilerini dolduruyoruz. Ben aşağıdaki şekilde doldurdum.
Böylelikle projemizi yaratmış oluyoruz.
“Hello, World”
Åžimdi hızlıca “Hello, World” uygulamasını nasıl yapacağımıza bakalım. SpringBoot saÄŸolsun bizim için herÅŸeyi ayarladığından bizim yapacağımız çok fazla bir ÅŸey kalmıyor. Åžimdi SpringRedisApplication.java sınıfını aÅŸağıdaki gibi güncelleyelim.
SpringRedisApplication.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | package com.bahadirakin.redis; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.CacheManager; import org.springframework.context.annotation.Bean; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.core.StringRedisTemplate; @SpringBootApplication public class SpringRedisApplication implements CommandLineRunner { private static final Logger logger = LoggerFactory.getLogger(SpringRedisApplication.class); @Autowired private StringRedisTemplate stringRedisTemplate; public static void main(String[] args) { SpringApplication.run(SpringRedisApplication.class, args); } @Override public void run(String... strings) throws Exception { // Messaging Demonstration logger.info("Sending message....."); stringRedisTemplate.opsForValue().set("message", "Hello, World"); final String message = stringRedisTemplate.opsForValue().get("message"); logger.info("Message From Redis Server {}", message); } } |
Uygulamamızı bir önceki yazımızda hazırladığımız Vagrant dosyasını kullanarak çalıştıracağız. Vagrant ile yeni bir sanal makine oluÅŸturmuÅŸ oluyoruz. Ve kendi makinemizle bu sanal makine bir aÄŸda çalışıyor oluyorlar. Biz Vagrant’ın oluÅŸturduÄŸu sanal makineye Vagrantfile içerisinde verdiÄŸimiz adresten yani 192.168.33.10’ten ulaşıyoruz. Aynı ÅŸekilde yeni yazdığımız uygulamamıza da Redis sunucusuna hangi adresten ulaÅŸabileceÄŸini söylememiz gerekiyor. Bunun için application.properties dosyasına aÅŸağıdaki satırları eklememiz yeterli olacaktır.
application.properties
1 2 | spring.redis.host=192.168.33.10 spring.redis.port=6379 |
Åžimdi SpringRedisApplication uygulamamızı çalıştıralım ve mesajımızın düzgün ÅŸekilde iletilip alındığını görmüş olalım. Peki sadece String tipinde mi mesajlar gönderebiliyoruz? Tabiki de hayır. RedisSerializer tipinde bir interface bulunuyor. Bunu implement ederek istediÄŸiniz her türde veriyi gönderip almanız mümkün. Ama unutmayın uygulamanızı ayarlarken key için hangi RedisSerializer’ı, ilgili deÄŸer için hangi RedisSerializer’ı kullanacağınızı belirtmeniz gerekiyor.
Redis Cache
Temel kısmı hızlıca halletiÄŸimize göre asıl eÄŸlenceli kısma gelebiliriz. Spring-Cache’i daha önce kullandıysanız ne kadar güzel bir kütüphane olduÄŸu konusunda az buçuk fikriniz vardır. Daha önce kullanmamış olanlar için kısaca ne olduÄŸundan bahsedeyim.
Spring Cache
Spring Cache annotasyonlar üzerinden bilgileri cache’lemenize olanak saÄŸlayan bir mekanizma. Spring’in bir parçası. Tek kısıtı, annotasyonlarınızı kullandığınız Bean’lerin Spring tarafından yönetilmesi gerekiyor. En güzel yanı ise alt tarafta istediÄŸiniz cache kütüphanesini(Ehcache, Memcache, Guava, Redis vs.) kullanabilirsiniz. İleride bununla ilgili daha detaylı bir yazı yazmayı düşünüyorum zaten.
Her ne kadar biz örneğimizde tek tip annotasyonunu kullanacak olsakta, kısaca tüm annotasyonlarının üzerinden geçeyim.
Annotasyon | İşlevi |
@Cacheable | İlgili metodun sonucu ayarlanan cache kütüphanesine kaydedilir. EÄŸer yeni bir talep gelirse ve aynı ‘key’ deÄŸeri kullanıldıysa yeni sonuç doÄŸruca cacheten okunur. Default olarak metodun parametreleri ‘key’ olarak kullanılmaya çalışılır. Tabi burada parametrelerin alt taraftaki cache kütüphanesi tarafından desteklenen veri tiplerinden olması gerekiyor. |
@CachePut | @Cacheable’da olduÄŸu gibi metod sonucunu ilgili cache kütüphanesine kaydeder. Fakat bir sonraki çağırımda yine method çalışır. |
@CacheEvict | Bir verinin cache’lenmesinde en önemli konu, cache’teki verinin güncelliÄŸinin korunmasıdır. Bu annotasyon cache’inizin gerektiÄŸinde güncellenmesine olanak saÄŸlar. İlgili metod çağırıldığında ‘key’ deÄŸerindeki veri cache’ten silinir. |
@EnableCaching | Bu daha çok ayarlarla ilgili. Ayarlarınızı java sınıfı üzerinden yapıyorsanız, ilgili ayar sınıfınızın tepesine bu annotasyonu koyduÄŸunuzda cache’iniz aktif hale gelir. Fakat sadece aktif etmeniz yetmez bit CacheManager tipinde Bean yaratmanız ve hangi Cache kütüphanesinin yaratılacağını belirtmeniz gerekir. |
Cache’lenek Servis Katmanı
Şimdi, çok zaman tüketen, bir hayli işlem yapan bir servis katmanı yazalım. Daha sonra annotasyonlarımızı ekleyeylim. Öncelikle src/main/java altında, com.bahadirakin.redis paketine SomeLongBusiness.java isminde bir interface yaratalım. Tahmin edebileceğiniz gibi bu interface bizim zaman alan servis katmanımızı oluşturacak.
SomeLongBusiness.java
1 2 3 4 5 6 | package com.bahadirakin.redis; public interface SomeLongBusiness { String doSomeHeavyWork(String input); } |
Şimdi de bu sınıfımızın implementasyonunu aynı paket içerisine yaratalım.
SomeLongBuinessImpl.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | package com.bahadirakin.redis; import org.springframework.cache.annotation.Cacheable; public class SomeLongBusinessImpl implements SomeLongBusiness { @Cacheable(value = "resultCache") @Override public String doSomeHeavyWork(String input) { try { Thread.sleep(5000); return "Result: " + input; } catch (InterruptedException e) { throw new RuntimeException(e); } } } |
Gördüğünüz gibi işlemimiz bir hayli zaman alacak şekilde tasarlanmış. Yaklaşık 5sn sürecek bu işlem. Anlaşılmayan bir konu olduğunu düşünmüyorum. Şimdi bunu çağıracak sınıfımıza bakalım. SpringRedisApplication.java dosyasını aşağıdaki gibi düzenleyelim.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | package com.bahadirakin.redis; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.core.StringRedisTemplate; @EnableCaching @SpringBootApplication public class SpringRedisApplication implements CommandLineRunner { private static final Logger logger = LoggerFactory.getLogger(SpringRedisApplication.class); @Autowired private StringRedisTemplate stringRedisTemplate; @Autowired private SomeLongBusiness someLongBusiness; public static void main(String[] args) { SpringApplication.run(SpringRedisApplication.class, args); } @Bean CacheManager cacheManager(StringRedisTemplate stringRedisTemplate) { return new RedisCacheManager(stringRedisTemplate); } @Bean SomeLongBusiness someLongBusiness() { return new SomeLongBusinessImpl(); } @Override public void run(String... strings) throws Exception { // Messaging Demonstration logger.info("Sending message....."); stringRedisTemplate.opsForValue().set("message", "Hello, World"); final String message = stringRedisTemplate.opsForValue().get("message"); logger.info("Message From Redis Server {}", message); // Caching Demonstration logger.info("Heavy work is starting for the first time"); final String firstResult = someLongBusiness.doSomeHeavyWork(message); logger.info("First (Takes 5 sec): {}", firstResult); final String secondResult = someLongBusiness.doSomeHeavyWork(message); logger.info("Second: {}", secondResult); final String thirdResult = someLongBusiness.doSomeHeavyWork("Different Message"); logger.info("Third (Takes 5 sec): {}", thirdResult); final String forthResult = someLongBusiness.doSomeHeavyWork("Different Message"); logger.info("Forth: {}", forthResult); } } |
Son
Böylelikle hem mesajlaÅŸma hem de cahce’leme yapan uygulamamızı geliÅŸtirmiÅŸ olduk. Öncelikle “vagrant up” ile sanal makinemizi ayaÄŸa kaldırıp uygulamamızı çalıştırıyoruz. İlk çalıştırmamızda aÅŸağıdakine benzer çıktılar almamız gerekiyor.
1 2 3 4 5 6 7 8 9 | 2015-07-12 15:26:38.513 INFO 984 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup 2015-07-12 15:26:38.518 INFO 984 --- [ main] c.b.redis.SpringRedisApplication : Sending message..... 2015-07-12 15:26:38.562 INFO 984 --- [ main] c.b.redis.SpringRedisApplication : Message From Redis Server Hello, World 2015-07-12 15:26:38.563 INFO 984 --- [ main] c.b.redis.SpringRedisApplication : Heavy work is starting for the first time 2015-07-12 15:26:43.582 INFO 984 --- [ main] c.b.redis.SpringRedisApplication : First (Takes 5 sec): Result: Hello, World 2015-07-12 15:26:43.583 INFO 984 --- [ main] c.b.redis.SpringRedisApplication : Second: Result: Hello, World 2015-07-12 15:26:48.591 INFO 984 --- [ main] c.b.redis.SpringRedisApplication : Third (Takes 5 sec): Result: Different Message 2015-07-12 15:26:48.593 INFO 984 --- [ main] c.b.redis.SpringRedisApplication : Forth: Result: Different Message 2015-07-12 15:26:48.595 INFO 984 --- [ main] c.b.redis.SpringRedisApplication : Started SpringRedisApplication in 11.089 seconds (JVM running for 11.428) |
İkinciye çalıştırdığımız da ise hiç bir bekleme yapmayacaktır ve bu sefer aşağıdakine benzer çıktılar alacağız.
1 2 3 4 5 6 7 8 9 | 2015-07-12 15:27:23.530 INFO 987 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup 2015-07-12 15:27:23.535 INFO 987 --- [ main] c.b.redis.SpringRedisApplication : Sending message..... 2015-07-12 15:27:23.579 INFO 987 --- [ main] c.b.redis.SpringRedisApplication : Message From Redis Server Hello, World 2015-07-12 15:27:23.580 INFO 987 --- [ main] c.b.redis.SpringRedisApplication : Heavy work is starting for the first time 2015-07-12 15:27:23.585 INFO 987 --- [ main] c.b.redis.SpringRedisApplication : First (Takes 5 sec): Result: Hello, World 2015-07-12 15:27:23.586 INFO 987 --- [ main] c.b.redis.SpringRedisApplication : Second: Result: Hello, World 2015-07-12 15:27:23.588 INFO 987 --- [ main] c.b.redis.SpringRedisApplication : Third (Takes 5 sec): Result: Different Message 2015-07-12 15:27:23.589 INFO 987 --- [ main] c.b.redis.SpringRedisApplication : Forth: Result: Different Message 2015-07-12 15:27:23.591 INFO 987 --- [ main] c.b.redis.SpringRedisApplication : Started SpringRedisApplication in 1.122 seconds (JVM running for 1.496) |
Redis kullandığımızdan cache’lediÄŸimiz veriler farklı çalıştırmalarda da saklanmış oluyor. Bu aslında bakarsanız, clustered ortamlarda çok iÅŸinize yarayacak bir durum. Böylelikle aynı iÅŸlem farklı makinelerde de olsa tekrar tekrar yapılmamış oluyor.
Projenin tamanına ise aşağıdaki adresten ulaşabilirsiniz.
- Git:Â Spring-Redis
End of Line