当前位置:网站首页>It's easy to operate. ThreadLocal can also be used as a cache
It's easy to operate. ThreadLocal can also be used as a cache
2020-11-06 01:28:00 【Yin Jihuan】
The background that
A friend asked me a question about interface optimization , His optimization point is very clear , Because there are many internal calls in the interface service To form a completed business function . Every service The logic in is independent , As a result, many queries are duplicated , Look at the picture below and you will see .
The upper level query is passed on
For this scenario, the best thing is to query the required data from the upper layer , And then pass it on to the lower levels to consume . So you don't have to repeat the query .
If you start writing code like this, no problem , But a lot of times , I was independent when I wrote before , Or the old logic of reuse , There are independent queries in it .
If you want to do optimization, you can only overload one of the old methods , Pass on the information you need directly .
public void xxx(int goodsId) {
Goods goods = goodsService.get(goodsId);
.....
}
public void xxx(Goods goods) {
.....
}
Cache
If your business scenario allows for a certain delay in data , So you can solve the problem of repeated calls by adding cache directly . This has the advantage of not repeatedly querying the database , Instead, it takes data directly from the cache .
The bigger advantage is that it has the least impact on the optimization class , The original code logic does not need to be changed , You can only add annotations to the query .
public void xxx(int goodsId) {
Goods goods = goodsService.get(goodsId);
.....
}
public void xxx(Goods goods) {
Goods goods = goodsService.get(goodsId);
.....
}
class GoodsService {
@Cached(expire = 10, timeUnit = TimeUnit.SECONDS)
public Goods get(int goodsId) {
return dao.findById(goodsId);
}
}
If your business scenario doesn't allow caching , The above method can't be used . So is it necessary to change the code , Pass on the required information one layer at a time ?
Customize the cache within the thread
Let's summarize the current problems :
- In the same request , Get the same query many times RPC And so on .
- High demand for real-time data , Not suitable for caching , The main reason is that it is not easy to set the expiration time by adding cache , Unless data changes are used to actively update the cache .
- Just cache in this request , It doesn't affect other places .
- Don't want to change existing code .
After summing up, it is found that this scene is suitable for use ThreadLocal To transfer data , Minimal changes to existing code , And it only works for the current thread , Does not affect other threads .
public void xxx(int goodsId) {
Goods goods = ThreadLocal.get();
if (goods == null) {
goods = goodsService.get(goodsId);
}
.....
}
The code above uses ThreadLocal To get data , If there is one, use it directly , You don't have to search again , If you don't have it, you can check it out , It doesn't affect the old logic .
Although it can achieve the effect , But not so good , Not elegant enough . It's not universal enough , If you want to cache multiple types of data in one request, how to handle it ? ThreadLocal You can't store fixed types . What's more, the old logic has to be changed , Add a judgment .
Here's a more elegant way :
- Custom cache annotations , Add to the query method .
- Define facet cuts to methods with cache annotations , Get the return value for the first time ThreadLocal. The second time directly from ThreadLocal Return the value of .
- ThreadLocal Storage in Map,Key For a certain identification of a method , This can cache multiple types of results .
- stay Filter Lieutenant general ThreadLocal Conduct remove operation , Because threads are reusable , It needs to be emptied after use .
Be careful :ThreadLocal Can't cross thread , If there is a cross thread requirement , Please use Ali's ttl To decorate .
Annotation definition
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ThreadLocalCache {
/**
* cache key, Support SPEL expression
* @return
*/
String key() default "";
}
Storage definition
/**
* In thread cache management
*
* @ author Yin Jihuan
* @ Time 2020-07-12 10:47
*/
public class ThreadLocalCacheManager {
private static ThreadLocal<Map> threadLocalCache = new ThreadLocal<>();
public static void setCache(Map value) {
threadLocalCache.set(value);
}
public static Map getCache() {
return threadLocalCache.get();
}
public static void removeCache() {
threadLocalCache.remove();
}
public static void removeCache(String key) {
Map cache = threadLocalCache.get();
if (cache != null) {
cache.remove(key);
}
}
}
Section definition
/**
* In thread caching
*
* @ author Yin Jihuan
* @ Time 2020-07-12 10:48
*/
@Aspect
public class ThreadLocalCacheAspect {
@Around(value = "@annotation(localCache)")
public Object aroundAdvice(ProceedingJoinPoint joinpoint, ThreadLocalCache localCache) throws Throwable {
Object[] args = joinpoint.getArgs();
Method method = ((MethodSignature) joinpoint.getSignature()).getMethod();
String className = joinpoint.getTarget().getClass().getName();
String methodName = method.getName();
String key = parseKey(localCache.key(), method, args, getDefaultKey(className, methodName, args));
Map cache = ThreadLocalCacheManager.getCache();
if (cache == null) {
cache = new HashMap();
}
Map finalCache = cache;
Map<String, Object> data = new HashMap<>();
data.put("methodName", className + "." + methodName);
Object cacheResult = CatTransactionManager.newTransaction(() -> {
if (finalCache.containsKey(key)) {
return finalCache.get(key);
}
return null;
}, "ThreadLocalCache", "CacheGet", data);
if (cacheResult != null) {
return cacheResult;
}
return CatTransactionManager.newTransaction(() -> {
Object result = null;
try {
result = joinpoint.proceed();
} catch (Throwable throwable) {
throw new RuntimeException(throwable);
}
finalCache.put(key, result);
ThreadLocalCacheManager.setCache(finalCache);
return result;
}, "ThreadLocalCache", "CachePut", data);
}
private String getDefaultKey(String className, String methodName, Object[] args) {
String defaultKey = className + "." + methodName;
if (args != null) {
defaultKey = defaultKey + "." + JsonUtils.toJson(args);
}
return defaultKey;
}
private String parseKey(String key, Method method, Object[] args, String defaultKey){
if (!StringUtils.hasText(key)) {
return defaultKey;
}
LocalVariableTableParameterNameDiscoverer nameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
String[] paraNameArr = nameDiscoverer.getParameterNames(method);
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
for(int i = 0;i < paraNameArr.length; i++){
context.setVariable(paraNameArr[i], args[i]);
}
try {
return parser.parseExpression(key).getValue(context, String.class);
} catch (SpelEvaluationException e) {
// I can't figure out SPEL Default to class name + Method name + Parameters
return defaultKey;
}
}
}
Filter definition
/**
* Thread cache filter
*
* @ author Yin Jihuan
* @ Personal wechat jihuan900
* @ WeChat official account Ape world
* @GitHub https://github.com/yinjihuan
* @ The authors introduce http://cxytiandi.com/about
* @ Time 2020-07-12 19:46
*/
@Slf4j
public class ThreadLocalCacheFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
filterChain.doFilter(servletRequest, servletResponse);
// Clear cache after execution
ThreadLocalCacheManager.removeCache();
}
}
Automatic configuration class
@Configuration
public class ThreadLocalCacheAutoConfiguration {
@Bean
public FilterRegistrationBean idempotentParamtFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
ThreadLocalCacheFilter filter = new ThreadLocalCacheFilter();
registration.setFilter(filter);
registration.addUrlPatterns("/*");
registration.setName("thread-local-cache-filter");
registration.setOrder(1);
return registration;
}
@Bean
public ThreadLocalCacheAspect threadLocalCacheAspect() {
return new ThreadLocalCacheAspect();
}
}
Use cases
@Service
public class TestService {
/**
* ThreadLocalCache Will cache , Only valid for the current thread
* @return
*/
@ThreadLocalCache
public String getName() {
System.out.println(" It's time to check ");
return "yinjihaun";
}
/**
* Support SPEL expression
* @param id
* @return
*/
@ThreadLocalCache(key = "#id")
public String getName(String id) {
System.out.println(" It's time to check ");
return "yinjihaun" + id;
}
}
Function code : https://github.com/yinjihuan/kitty
Case code : https://github.com/yinjihuan/kitty-samples
About author : Yin Jihuan , Simple technology enthusiasts ,《Spring Cloud Microservices - Full stack technology and case analysis 》, 《Spring Cloud Microservices introduction Actual combat and advanced 》 author , official account Ape world Originator . Personal wechat jihuan900 , Welcome to hook up with .
I have compiled a complete set of learning materials , Those who are interested can search through wechat 「 Ape world 」, Reply key 「 Learning materials 」 Get what I've sorted out Spring Cloud,Spring Cloud Alibaba,Sharding-JDBC Sub database and sub table , Task scheduling framework XXL-JOB,MongoDB, Reptiles and other related information .
版权声明
本文为[Yin Jihuan]所创,转载请带上原文链接,感谢
边栏推荐
- Elasticsearch数据库 | Elasticsearch-7.5.0应用搭建实战
- Thoughts on interview of Ali CCO project team
- Working principle of gradient descent algorithm in machine learning
- Programmer introspection checklist
- 助力金融科技创新发展,ATFX走在行业最前列
- 零基础打造一款属于自己的网页搜索引擎
- Brief introduction and advantages and disadvantages of deepwalk model
- Skywalking series blog 1 - install stand-alone skywalking
- From zero learning artificial intelligence, open the road of career planning!
- How to use parameters in ES6
猜你喜欢
至联云解析:IPFS/Filecoin挖矿为什么这么难?
有了这个神器,快速告别垃圾短信邮件
Working principle of gradient descent algorithm in machine learning
Interface pressure test: installation, use and instruction of siege pressure test
The road of C + + Learning: from introduction to mastery
Just now, I popularized two unique skills of login to Xuemei
速看!互联网、电商离线大数据分析最佳实践!(附网盘链接)
Summary of common algorithms of binary tree
Calculation script for time series data
如何玩转sortablejs-vuedraggable实现表单嵌套拖拽功能
随机推荐
Multi classification of unbalanced text using AWS sagemaker blazingtext
Five vuex plug-ins for your next vuejs project
NLP model Bert: from introduction to mastery (1)
MeterSphere开发者手册
一篇文章带你了解CSS 渐变知识
TensorFlow中的Tensor是什么?
Mac installation hanlp, and win installation and use
Using consult to realize service discovery: instance ID customization
Elasticsearch数据库 | Elasticsearch-7.5.0应用搭建实战
vue-codemirror基本用法:实现搜索功能、代码折叠功能、获取编辑器值及时验证
Analysis of partial source codes of qthread
Python基础数据类型——tuple浅析
6.5 request to view name translator (in-depth analysis of SSM and project practice)
Pattern matching: The gestalt approach一种序列的文本相似度方法
I've been rejected by the product manager. Why don't you know
Filecoin的经济模型与未来价值是如何支撑FIL币价格破千的
速看!互联网、电商离线大数据分析最佳实践!(附网盘链接)
Python基础变量类型——List浅析
keras model.compile Loss function and optimizer
在大规模 Kubernetes 集群上实现高 SLO 的方法