naver / arcus-spring Goto Github PK
View Code? Open in Web Editor NEWARCUS as a caching provider for the Spring Cache Abstraction
License: Apache License 2.0
ARCUS as a caching provider for the Spring Cache Abstraction
License: Apache License 2.0
현재 코드 구현 상 아래와 같이 ArcusCache 객체 생성 시 setter로 데이터를 주입해주고 있다.
이 때 사용자가 configuration으로 설정하지 않은 필드는 null값을 가지게 되는데, 이를 그대로 setter에 넣어주어 null 값을 setter에 넣는 형태가 된다.
setter에 null을 입력하는 상황은 혼란을 줄 수 있으므로 setter에 값을 입력하기 전 null인지 검사하고 입력하거나, ArcusCache의 생성자에 ArcusCacheConfiguration 객체를 입력받도록 하는 방안이 괜찮아보인다.
@SuppressWarnings("deprecation")
protected Cache createCache(String name, ArcusCacheConfiguration configuration) {
ArcusCache cache = new ArcusCache();
cache.setName(name);
cache.setServiceId(configuration.getServiceId());
cache.setPrefix(configuration.getPrefix());
cache.setArcusClient(client);
cache.setArcusFrontCache(configuration.getArcusFrontCache());
cache.setExpireSeconds(configuration.getExpireSeconds());
cache.setFrontExpireSeconds(configuration.getFrontExpireSeconds());
cache.setTimeoutMilliSeconds(configuration.getTimeoutMilliSeconds());
cache.setOperationTranscoder(configuration.getOperationTranscoder());
cache.setForceFrontCaching(configuration.isForceFrontCaching());
cache.setWantToGetException(true);
return cache;
}
현재 createArcusKey() 메소드에서 arcusKey 생성 부분이 아래와 같습니다.
public String createArcusKey(final Object key) {
String keyString, arcusKey;
. . .
arcusKey = serviceId + name + ":" + keyString;
if (this.prefix != null) {
arcusKey = serviceId + prefix + ":" + keyString;
}
. . .
return arcusKey;
}
위의 코드는 불필요하게 GC 부담을 가중시킬 수 있으므로, 아래와 변경하는 것이 좋겠습니다.
if (this.prefix != null) {
arcusKey = serviceId + prefix + ":" + keyString;
} else {
arcusKey = serviceId + name + ":" + keyString;
}
1.13.4 버전 기준의 README의 내용을 포함하여 사용자 가이드 문서를 작성한다.
안녕하세요.
사내에서 스프링 latest 버전으로의 bump up을 준비하고 있는 프로젝트가 있습니다.
#63 PR이 반영된 다음 release 일정이 언제인지 알 수 있을까요?
가능하시다면 minor fix 버전으로라도 패치 해 주신다면 감사하겠습니다!
현재 KeyGenerator의 유일한 인터페이스는 다음과 같습니다.
Object generate(Object target, Method method, Object... params);
단, 현재 구현 상 매개변수 target과 method는 사용하지 않습니다.
public class StringKeyGenerator implements KeyGenerator {
@Override
public Object generate(Object target, Method method, Object... params) {
int hash = 0;
StringBuilder keyBuilder = new StringBuilder();
for (int i = 0, n = params.length; i < n; i++) {
if (i > 0) {
keyBuilder.append(DEFAULT_SEPARTOR);
}
if (params[i] != null) {
keyBuilder.append(params[i]);
hash ^= ArcusStringKey.light_hash(params[i].toString());
}
}
return new ArcusStringKey(keyBuilder.toString().replace(' ', '_') + hash);
}
}
public class SimpleStringKeyGenerator implements KeyGenerator {
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuilder keyBuilder = new StringBuilder();
for (int i = 0, n = params.length; i < n; i++) {
if (i > 0) {
keyBuilder.append(DEFAULT_SEPARTOR);
}
if (params[i] != null) {
keyBuilder.append(params[i]);
}
}
return new ArcusStringKey(keyBuilder.toString());
}
}
따라서 CacheManager를 직접 주입 받아 사용할 경우, KeyGenerator를 다음과 같이 사용해야 합니다.
@Service
public class ProductService {
@Autowired
private CacheManager cacheManager;
@Autowired
private KeyGenerator keyGenerator;
/*
using the "testCache" cache with 60 expire seconds and "TEST-PRODUCT" prefix.
*/
public Product getProduct_TestCache(int id) {
Product product = cacheManager.getCache("testCache")
.get(keyGenerator.generate(null, null, id), Product.class);
if (product == null) {
return new Product(id);
}
return product;
}
/*
using the "devCache" cache with 120 expire seconds and "DEV-PRODUCT" prefix.
*/
public Product getProduct_DevCache(int id) {
Product product = cacheManager.getCache("devCache")
.get(keyGenerator.generate(null, null, id), Product.class);
if (product == null) {
return new Product(id);
}
return product;
}
/*
using the "missingCache" cache with 60 expire seconds and "DEFAULT" prefix.
*/
public Product getProduct_DefaultCache(int id) {
Product product = cacheManager.getCache("missingCache")
.get(keyGenerator.generate(null, null, id), Product.class);
if (product == null) {
return new Product(id);
}
return product;
}
}
두 매개변수를 null로 주어도 내부적으로 사용하지 않으니 올바르게 동작하지만, 얼핏 코드를 보면 KeyGenerator를 사용하는 인터페이스가 잘못되었단 느낌을 받기 쉽습니다.
따라서 다음과 같은 클래스를 추가하여 매개변수에 null을 넣을 필요 없는 인터페이스를 오버로딩으로 제공하면 좋을 것 같습니다.
public abstract class ArcusKeyGenerator implements KeyGenerator {
public Object generate(Object... params) {
return generate(null, null, params);
}
}
public class StringKeyGenerator extends ArcusKeyGenerator { // 상속 관계 변경
@Override
public Object generate(Object target, Method method, Object... params) {
int hash = 0;
StringBuilder keyBuilder = new StringBuilder();
for (int i = 0, n = params.length; i < n; i++) {
if (i > 0) {
keyBuilder.append(DEFAULT_SEPARTOR);
}
if (params[i] != null) {
keyBuilder.append(params[i]);
hash ^= ArcusStringKey.light_hash(params[i].toString());
}
}
return new ArcusStringKey(keyBuilder.toString().replace(' ', '_') + hash);
}
}
public class SimpleStringKeyGenerator extends ArcusKeyGenerator { // 상속 관계 변경
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuilder keyBuilder = new StringBuilder();
for (int i = 0, n = params.length; i < n; i++) {
if (i > 0) {
keyBuilder.append(DEFAULT_SEPARTOR);
}
if (params[i] != null) {
keyBuilder.append(params[i]);
}
}
return new ArcusStringKey(keyBuilder.toString());
}
}
@Service
public class ProductService {
@Autowired
private CacheManager cacheManager;
@Autowired
private ArcusKeyGenerator keyGenerator;
/*
using the "testCache" cache with 60 expire seconds and "TEST-PRODUCT" prefix.
*/
public Product getProduct_TestCache(int id) {
Product product = cacheManager.getCache("testCache")
.get(keyGenerator.generate(id), Product.class);
if (product == null) {
return new Product(id);
}
return product;
}
/*
using the "devCache" cache with 120 expire seconds and "DEV-PRODUCT" prefix.
*/
public Product getProduct_DevCache(int id) {
Product product = cacheManager.getCache("devCache")
.get(keyGenerator.generate(id), Product.class);
if (product == null) {
return new Product(id);
}
return product;
}
/*
using the "missingCache" cache with 60 expire seconds and "DEFAULT" prefix.
*/
public Product getProduct_DefaultCache(int id) {
Product product = cacheManager.getCache("missingCache")
.get(keyGenerator.generate(id), Product.class);
if (product == null) {
return new Product(id);
}
return product;
}
}
Spring 5 Webflux 모델에서는 Spring Cache의 annotation을 사용할 수 없다.
WebFlux 모델에서는 실제 Value Object가 아닌 Flux, Mono가 리턴이 되는 구조이기 때문이다.
public Mono<Object> mono() {
...
}
Spring 5의 Webflux 모델에 대해 이해하고, WebFlux 구조에서 Cache를 쉽게 적용할 수 있는 방법에 대해 고민하고 arcus-spring에서 이를 지원할 수 있도록 한다.
https://dreamchaser3.tistory.com/17
https://stackoverflow.com/questions/48156424/spring-webflux-and-cacheable-proper-way-of-caching-result-of-mono-flux-type
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.