当前位置:网站首页>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]所创,转载请带上原文链接,感谢
边栏推荐
- 零基础打造一款属于自己的网页搜索引擎
- 如何玩转sortablejs-vuedraggable实现表单嵌套拖拽功能
- [actual combat of flutter] pubspec.yaml Configuration file details
- Wiremock: a powerful tool for API testing
- Classical dynamic programming: complete knapsack problem
- Installing the consult cluster
- 一篇文章带你了解CSS3图片边框
- Summary of common string algorithms
- git rebase的時候捅婁子了,怎麼辦?線上等……
- ES6学习笔记(二):教你玩转类的继承和类的对象
猜你喜欢
Mongodb (from 0 to 1), 11 days mongodb primary to intermediate advanced secret
Python saves the list data
比特币一度突破14000美元,即将面临美国大选考验
这个项目可以让你在几分钟快速了解某个编程语言
ES6学习笔记(四):教你轻松搞懂ES6的新增语法
The road of C + + Learning: from introduction to mastery
I've been rejected by the product manager. Why don't you know
It's so embarrassing, fans broke ten thousand, used for a year!
[C / C + + 1] clion configuration and running C language
钻石标准--Diamond Standard
随机推荐
一篇文章教会你使用Python网络爬虫下载酷狗音乐
Natural language processing - wrong word recognition (based on Python) kenlm, pycorrector
Windows 10 tensorflow (2) regression analysis of principles, deep learning framework (gradient descent method to solve regression parameters)
ES6 essence:
一篇文章带你了解CSS对齐方式
前端工程师需要懂的前端面试题(c s s方面)总结(二)
5.5 controlleradvice notes - SSM in depth analysis and project practice
Network security engineer Demo: the original * * is to get your computer administrator rights! 【***】
Solve the problem of database insert data garbled in PL / SQL developer
Python + appium automatic operation wechat is enough
Vue.js Mobile end left slide delete component
Building and visualizing decision tree with Python
Wechat applet: prevent multiple click jump (function throttling)
Advanced Vue component pattern (3)
How long does it take you to work out an object-oriented programming interview question from Ali school?
前端都应懂的入门基础-github基础
一篇文章带你了解CSS 渐变知识
TensorFlow中的Tensor是什么?
[event center azure event hub] interpretation of error information found in event hub logs
How to become a data scientist? - kdnuggets