JavaLand 2022, März, Brühl, Dirk Kröhan, Software Architect bei QAware).
== Dokument bitte herunterladen, falls unscharf! Please download slides if blurred! ==
“Kann man das nicht einfach cachen?” - So oder so ähnlich hat es bestimmt schon jeder mal gehört. Hin und wieder auch mit einer Prise Sarkasmus, wenn Caching als das schnelle Pflaster für schlecht geschriebene Datenbankabfragen dienen soll.
Es gibt aber natürlich genügend Anwendungsfälle wo Caching nicht nur sinnvoll, sondern auch notwendig sein kann. Wenn niedrige Latenzen oder die Entlastung der Datenbank gefordert ist führt kaum ein Weg an Caching vorbei.
In unserem Projekt ist eine flexible Konfiguration und niedrige Latenz eine Kernanforderung und somit begann eine Reise von local caches zu distributed caches. Klingt einfach? Ist es auch, schließlich tauscht man durch die Spring Cache Abstraction nur eine Dependency aus - oder etwa nicht?
Packt die Kamera ein und geht mit mir auf Spring-Caching-Safari - wilde Invalidations und unangenehme Deployments inklusive! Erfrischung gibt es an der Learnings- und Good-Practices-Oase.
Seid gespannt auf:
- Local Caches vs. Distributed Caches
- Spring Caching Abstraction
- Resilient Cache Manager
- Caching Stats & Latency Metrics
- Self-made @CollectionCachable Annotation
next bullet point is loading... Cache is cold.
11. Local Caches
QAware | 11
Cluster
my-service
Local Cache
DB
GET /my-service/api/config
Cache Miss
30ms
12. Local Caches
QAware | 12
Cluster
my-service
Local Cache
DB
GET /my-service/api/config 30 ms
GET /my-service/api/config
Cache Hit
5ms
13. Local Caches
QAware | 13
Let’s scale!!
Cluster
my-service
Local Cache
my-service
Local Cache
my-service
Local Cache
Load
Balancer
DB
14. Local Caches
QAware | 14
Cluster
my-service
Local Cache
my-service
Local Cache
my-service
Local Cache
Load
Balancer
DB
GET /my-service/api/config
PUT /my-service/api/config
DELETE /my-service/api/config
Cache Inkonsistenz
15. Spring Cache Redis
QAware | 15
@Configuration
@EnableCaching
@ConditionalOnProperty(name = "features.distributed-cache", havingValue = "true")
@RequiredArgsConstructor
public class CachingConfiguration {
private final RedisCachingConfigurationProperties redisCachingConfigurationProperties;
@Bean
public CacheManager redisCacheManager() {
return RedisConnectionFactoryBuilder.build(redisCachingConfigurationProperties).create();
}
}
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
</dependency>
</dependencies>
16. Welcome on Stage: NotSerializableException 😱
Vorher hat unser Local Cache einfach Referenzen auf die Java Objekte gehalten, jetzt müssen wir uns
plötzlich um Objekt Serialisierung kümmern… 😒
Mögliche Lösungen:
■ Zu cachende Objekte implementieren Serializable
■ RedisCacheConfiguration -> serializeKeysWith / serializeValuesWith
– serializeKeysWith -> StringRedisSerializer
– serializeValuesWith -> Your Choice!
• Jackson (GenericJackson2JsonRedisSerializer)
• Kryo (Custom RedisSerializer)
❭ https://github.com/EsotericSoftware/kryo
• …
QAware | 16
18. Unreliable network
QAware | 18
■ Verbindungsabbrüche zum Cache
■ Applikation startet nicht, wenn Verbindung zu Redis nicht möglich ist
■ Lettuce (Redis client) -> Hoher default Timeout
■ Langsame Cache Zugriffe -> Timeouts
Lösung: Fail fast!
1. Netzwerk Probleme erkennen
2. Cache durch No-Op/In-Memory ersetzen
3. Asynchron im Hintergrund die Verbindung neu aufbauen
19. Dafür gibt es doch schon etwas: Circuit Breaker
QAware | 19
my-service
Circuit-Breaker
Half-
Open
Closed
Open
Complex Logic
Tipp: Verschieden Circuit-Breaker je Cache Operation
■ Z.B. geringere Timeouts für Cache GET
21. Deploying a new version
QAware | 21
Cluster
my-service
v2
my-service
v1
Load
Balancer
Cache PUT
Serialized Object v2
Cache GET
Expect Object v1
Vorsicht bei Änderungen an Klassen, die gecached werden!
22. ■ Performance Tweak: Pre-Register zu cachende Klassen
– Kryo speichert dann nur einen int Wert für jede Klasse
■ Achtung: Reihenfolge der Klassen muss immer gleich bleiben -> List statt Set
– Hinzufügen von neuen Klassen nur ans Ende der Liste
■ Bei Missachtung: Wilde Exceptions nach Deployment
– z.B.: Kryo versucht Objekt A zu deserialisieren, obwohl Objekt B angefragt wird
Kryo pre-registered classes
QAware | 22
protected KryoSerDes create() {
Kryo kryo = new Kryo();
KRYO_REGISTERED_CLASSES.forEach(kryo::register);
23. ■ Cache-Prefix das vom Deployment abhängt
– z.B. Environment Variable, die eine Application Property überschreibt
– siehe:
https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.exter
nal-config
■ Nachteil: Cold Cache nach einem Deployment
– Canary deployment wärmt den Cache vor
Lösung
QAware | 23
private RedisCacheConfiguration getDefaultCacheConfiguration() {
return RedisCacheConfiguration.defaultCacheConfig()
.computePrefixWith(cacheName -> redisCachingConfigurationProperties.getCachePrefix() + cacheName)
…
}
25. Metriken das Must-Have!
QAware | 25
■ Klassische Cache Metriken: Hit-/Miss-Rate
■ Für uns speziell relevant: Metriken zur Latenz
– Separate Metriken für Cache Hit/Miss/Put/Evict
– Am besten direkt als RED-Metric (Rate, Error, Duration)
■ Dedizierte Metriken für spezielle Usecases
– Wie viele Cache / DB Zugriffe für Usecase X
– Summe der Cache / DB Latenzen für Usecase X
• RequestScope Beans als Datencollector
35. Was haben wir gelernt?
QAware | 35
■ Local Caches
– Schnell und einfach eingebaut
– Dateninkonsistenz bei > 1 Instanz
• Reduzieren durch passende TTLs
■ Distributed Caching mit Spring Data Redis
– Umstieg kaum Aufwand: Dependency austauschen + CacheManager konfigurieren
– Aber: Objekt (De-)Serialisierung ist jetzt ein Thema
■ Unreliable network
– Resilient Cache Manager: Circuit Breaker um den Cache packen
■ Cache Poisoning
– Konfigurierbarer Cache-Prefix bei Bedarf setzen
– oder automatisiert im Deployment
■ Metrics
– In der Praxis geht es einfach nicht ohne!
– Keine Metriken = Blindflug
37. KOMM ZU UNS! Stand #208
■ Genieße einen Cloud Fitness Espresso mit uns.
■ gRPC vs. REST
Sei bei unserem Cloud Native Battle dabei.
■ Gewinne 1 von 10 Cloud Native Nerd Swag-Paketen.
38. qaware.de
QAware GmbH
Aschauer Straße 32
81549 München
Tel. +49 89 232315-0
info@qaware.de
twitter.com/qaware
linkedin.com/company/qaware-gmbh
xing.com/companies/qawaregmbh
slideshare.net/qaware
github.com/qaware