Cache Redis con Spring Boot

Spring Boot Redis Cache

In questo post, installeremo un’applicazione di esempio Spring Boot e la integreremo con la Cache Redis. Mentre Redis è uno store di strutture dati in memoria open source, utilizzato come database, cache e message broker, questa lezione dimostrerà solo l’integrazione della cache. Faremo uso dello strumento Spring Initializr per configurare rapidamente il progetto.

Configurazione del Progetto Spring Boot Redis

Faremo uso dello strumento Spring Initializr per configurare rapidamente il progetto. Utilizzeremo 3 dipendenze come mostrato di seguito: Scaricare il progetto e decomprimerlo. Abbiamo utilizzato la dipendenza del database H2 poiché utilizzeremo un database integrato che perde tutti i dati una volta che l’applicazione viene arrestata.

Spring Boot Redis Cache Dipendenze Maven

Anche se abbiamo già completato la configurazione con lo strumento, se desideri configurarlo manualmente, utilizziamo il sistema di build Maven per questo progetto e ecco le dipendenze che abbiamo utilizzato:

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>1.5.9.RELEASE</version>
  <relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  <java.version>1.8</java.version>
</properties>
<dependencies>
  <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-redis</artifactId>
  </dependency>

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

  <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-test</artifactId>
     <scope>test</scope>
  </dependency>

  <!-- for JPA support -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>

  <!-- for embedded database support -->
  <dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
  </dependency>

</dependencies>

<build>
  <plugins>
     <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
     </plugin>
  </plugins>
</build>

Assicurati di utilizzare la versione stabile di Spring Boot dal repository maven centrale.

Definizione del Modello

Per salvare un oggetto nel database Redis, definiamo un oggetto modello Person con campi di base:

package com.journaldev.rediscachedemo;

import javax.persistence.*;
import java.io.Serializable;

@Entity
public class User implements Serializable {

    private static final long serialVersionUID = 7156526077883281623L;

    @Id
    @SequenceGenerator(name = "SEQ_GEN", sequenceName = "SEQ_USER", allocationSize = 1)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_GEN")
    private Long id;
    private String name;
    private long followers;

    public User() {
    }

    public User(String name, long followers) {
        this.name = name;
        this.followers = followers;
    }

    // getter e setter standard

    @Override
    public String toString() {
        return String.format("User{id=%d, name='%s', followers=%d}", id, name, followers);
    }
}

Si tratta di un POJO standard con getter e setter.

Configurazione della Cache Redis

Con Spring Boot e la dipendenza richiesta già funzionante con Maven, possiamo configurare un’istanza locale di Redis con soli tre righe nel nostro file application.properties come segue:

# Configurazione Redis
spring.cache.type=redis
spring.redis.host=localhost
spring.redis.port=6379

Usa anche l’annotazione @EnableCaching sulla classe principale di Spring Boot:

package com.journaldev.rediscachedemo;

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.annotation.EnableCaching;

@SpringBootApplication
@EnableCaching
public class Application implements CommandLineRunner {

  private final Logger LOG = LoggerFactory.getLogger(getClass());
  private final UserRepository userRepository;

  @Autowired
  public Application(UserRepository userRepository) {
    this.userRepository = userRepository;
  }

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }

  @Override
  public void run(String... strings) {
    // Popolamento del database integrato qui
    LOG.info("Saving users. Current user count is {}.", userRepository.count());
    User shubham = new User("Shubham", 2000);
    User pankaj = new User("Pankaj", 29000);
    User lewis = new User("Lewis", 550);

    userRepository.save(shubham);
    userRepository.save(pankaj);
    userRepository.save(lewis);
    LOG.info("Done saving users. Data: {}.", userRepository.findAll());
  }
}

Abbiamo aggiunto un CommandLineRunner poiché desideriamo popolare alcuni dati di esempio nel database H2 integrato.

Definizione del Repository

Prima di mostrare come funziona Redis, definiremo un Repository per la funzionalità correlata a JPA:

package com.journaldev.rediscachedemo;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository { }

Attualmente non contiene chiamate a metodi poiché non ne abbiamo bisogno.

Definizione del Controller

I controller sono il luogo in cui viene richiamata l’azione della cache di Redis. In realtà, questo è il posto migliore per farlo perché, essendo la cache direttamente associata ad esso, la richiesta non dovrà nemmeno entrare nel codice del servizio per attendere i risultati memorizzati nella cache. Ecco lo scheletro del controller:

package com.journaldev.rediscachedemo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.*;

@RestController
public class UserController {

  private final Logger LOG = LoggerFactory.getLogger(getClass());

  private final UserRepository userRepository;

  @Autowired
  public UserController(UserRepository userRepository) {
    this.userRepository = userRepository;
  }
   ...
}

Ora, per inserire qualcosa nella cache, utilizziamo l’annotazione @Cacheable:

@Cacheable(value = "users", key = "#userId", unless = "#result.followers < 12000")
@RequestMapping(value = "/{userId}", method = RequestMethod.GET)
public User getUser(@PathVariable String userId) {
  LOG.info("Getting user with ID {}.", userId);
  return userRepository.findOne(Long.valueOf(userId));
}

Nella mappatura sopra, il metodo getUser inserirà una persona in una cache chiamata ‘users’, identificherà quella persona con la chiave ‘userId’ e memorizzerà solo un utente con più di 12000 follower. Questo garantisce che la cache sia popolata da utenti molto popolari e spesso interrogati. Inoltre, abbiamo aggiunto intenzionalmente un’istruzione di log nella chiamata API. Facciamo alcune chiamate API da Postman in questo momento. Queste sono le chiamate che abbiamo effettuato:

localhost:8090/1
localhost:8090/1
localhost:8090/2
localhost:8090/2

Se notiamo i log, saranno così:

... : Getting user with ID 1.
... : Getting user with ID 1.
... : Getting user with ID 2.

Nota qualcosa? Abbiamo effettuato quattro chiamate API ma sono presenti solo tre log. Ciò è dovuto al fatto che l’utente con ID 2 ha 29000 follower e quindi i suoi dati sono stati memorizzati nella cache. Ciò significa che quando è stata effettuata una chiamata API per esso, i dati sono stati restituiti dalla cache e non è stata effettuata alcuna chiamata al database per questo!

Aggiornamento della cache

I valori della cache dovrebbero essere aggiornati anche quando vengono aggiornati i valori effettivi degli oggetti. Questo può essere fatto utilizzando l’annotazione @CachePut:

@CachePut(value = "users", key = "#user.id")
@PutMapping("/update")
public User updatePersonByID(@RequestBody User user) {
  userRepository.save(user);
  return user;
}

Con questo, una persona è nuovamente identificata dal suo ID e viene aggiornata con i risultati.

Cancellazione della cache

Se alcuni dati devono essere eliminati dal database effettivo, non avrebbe senso tenerli in cache. Possiamo cancellare i dati della cache utilizzando l’annotazione @CacheEvict:

@CacheEvict(value = "users", allEntries=true)
@DeleteMapping("/{id}")
public void deleteUserByID(@PathVariable Long id) {
  LOG.info("deleting person with id {}", id);
  userRepository.delete(id);
}

Nella mappatura precedente, abbiamo solo eliminato le voci della cache e non abbiamo fatto altro.

Esecuzione dell’applicazione Spring Boot Redis Cache

Possiamo eseguire questa app semplicemente usando un singolo comando:

mvn spring-boot:run

Limiti della Cache Redis

Anche se Redis è molto veloce, non ha limiti per memorizzare qualsiasi quantità di dati su un sistema a 64 bit. Può memorizzare solo 3 GB di dati su un sistema a 32 bit. Più memoria disponibile può portare a un maggiore rapporto di successo, ma questo tende a cessare una volta che troppa memoria è occupata da Redis. Quando la dimensione della cache raggiunge il limite di memoria, i dati vecchi vengono rimossi per fare spazio a quelli nuovi.

Riassunto

In questa lezione, abbiamo esaminato quale potenza ci fornisce la Cache Redis con un’interazione rapida dei dati e come possiamo integrarla con Spring Boot con una configurazione minima eppure potente. Sentitevi liberi di lasciare commenti qui sotto.

Scarica il Progetto Spring Boot Redis Cache

Source:
https://www.digitalocean.com/community/tutorials/spring-boot-redis-cache