当前位置:网站首页>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*/@Aspectpublic 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 + Parametersreturn 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*/@Slf4jpublic class ThreadLocalCacheFilter implements Filter {@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {filterChain.doFilter(servletRequest, servletResponse);// Clear cache after executionThreadLocalCacheManager.removeCache();}}
Automatic configuration class
@Configurationpublic class ThreadLocalCacheAutoConfiguration {@Beanpublic 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;}@Beanpublic ThreadLocalCacheAspect threadLocalCacheAspect() {return new ThreadLocalCacheAspect();}}
Use cases
@Servicepublic class TestService {/*** ThreadLocalCache Will cache , Only valid for the current thread* @return*/@ThreadLocalCachepublic 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]所创,转载请带上原文链接,感谢
边栏推荐
- Skywalking series blog 5-apm-customize-enhance-plugin
- Common algorithm interview has been out! Machine learning algorithm interview - KDnuggets
- Interface pressure test: installation, use and instruction of siege pressure test
- 华为云“四个可靠”的方法论
- Architecture article collection
- JVM memory area and garbage collection
- 6.6.1 localeresolver internationalization parser (1) (in-depth analysis of SSM and project practice)
- IPFS/Filecoin合法性:保护个人隐私不被泄露
- Our best practices for writing react components
- 至联云解析:IPFS/Filecoin挖矿为什么这么难?
猜你喜欢

ES6学习笔记(四):教你轻松搞懂ES6的新增语法

What is the side effect free method? How to name it? - Mario

助力金融科技创新发展,ATFX走在行业最前列

比特币一度突破14000美元,即将面临美国大选考验

This article will introduce you to jest unit test

PHP应用对接Justswap专用开发包【JustSwap.PHP】

How long does it take you to work out an object-oriented programming interview question from Ali school?

It's so embarrassing, fans broke ten thousand, used for a year!

Python Jieba segmentation (stuttering segmentation), extracting words, loading words, modifying word frequency, defining thesaurus

全球疫情加速互联网企业转型,区块链会是解药吗?
随机推荐
Five vuex plug-ins for your next vuejs project
如何玩转sortablejs-vuedraggable实现表单嵌套拖拽功能
Calculation script for time series data
6.1.1 handlermapping mapping processor (1) (in-depth analysis of SSM and project practice)
Skywalking series blog 1 - install stand-alone skywalking
Python基础数据类型——tuple浅析
Multi classification of unbalanced text using AWS sagemaker blazingtext
Not long after graduation, he earned 20000 yuan from private work!
keras model.compile Loss function and optimizer
一篇文章带你了解SVG 渐变知识
Wechat applet: prevent multiple click jump (function throttling)
The data of pandas was scrambled and the training machine and testing machine set were selected
PHP应用对接Justswap专用开发包【JustSwap.PHP】
Synchronous configuration from git to consult with git 2consul
[JMeter] two ways to realize interface Association: regular representation extractor and JSON extractor
ES6学习笔记(二):教你玩转类的继承和类的对象
小程序入门到精通(二):了解小程序开发4个重要文件
Filecoin的经济模型与未来价值是如何支撑FIL币价格破千的
6.6.1 localeresolver internationalization parser (1) (in-depth analysis of SSM and project practice)
Natural language processing - BM25 commonly used in search