zoukankan      html  css  js  c++  java
  • 变通实现微服务的per request以提高IO效率(三)

    效率

    变通实现微服务的per request以提高IO效率(二)遗留一个问题,如何正确的释放存储在ThreadLocal中的缓存,最理由就是在我们请求的方法执行完成后去清除缓存。

    Filter

    由于我的项目是基于dubbo的,所以可以利用dubbo提供的Filter机制去完成这件事情,可以看下filter的地位:

    最终的效果:

    创建ThreadLocalCacheFilter

    创建一个类让其实现Filter接口,就一个方法invoke,这个invoke方法的功能类似于AOP的Around方法,我们想清除缓存就有地方操作了,只需要在return的前面,invoker.invoke方法后面添加相应的清除逻辑即可达到目的。由于缓存是线程独有的,所以直接清空就可以。

    由于Filter加载机制问题,在Filter中使用Spring的注解是有点问题的,暂时是通过手动获取Bean的方式来加载cacheManager,后面在看dubbo的filter加载机制时会有简单提到。大家如果有其它好的方案可以告诉我

    @Activate
    public class ThreadLocalCacheFilter implements Filter {
    
        private Logger logger = LoggerFactory.getLogger(getClass().getName());
    
        @Autowired
        private CacheManager cacheManager;
    
        private void clearCache(){
            if(null==cacheManager){
                ApplicationContext appCtx = ApplicationContextUtils.getApplicationContext();
                cacheManager= appCtx.getBean(ThreadLocalCacheManager.class);
            }
            Collection<String> cacheNames= this.cacheManager.getCacheNames();
            if(null!=cacheNames) {
                for(String cacheName :cacheNames) {
                    this.cacheManager.getCache(cacheName).clear();
                }
            }
        }
    
        @Override
        public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
            Result result=invoker.invoke(invocation);
            this.logger.info("release cache start");
            this.clearCache();
            this.logger.info("release cache end");
    
            return result;
        }
    }

    @Active注解
    要想激活filter,我们需要在创建的自定义filter类上加载@Active注解,看下它的相关参数,也可以不配置

    • group,条件之一,指定是服务端还是消费端
    • value,条件之一,一般就是这个filter的英文名称,在dubbo配置文件中使用的
    • before,排序的信息,比如排在哪些filter之前
    • after,排序的信息,比如排在哪些filter之后
    • order,排序的信息,应该是值越小排在最前面

    加载Filter

    编写的扩展filter,dubbo需要加载成功后才能使用,dubbo总共从resource下面的三个目录中加载filter

    • META-INF/services/
    • META-INF/dubbo/
    • META-INF/dubbo/internal/

    创建纯文件文件com.alibaba.dubbo.rpc.Filter放入对应的目录,然后写入需要使用的filter信息

    threadLocalCacheFilter=com.filter.ThreadLocalCacheFilter

    应用Filter

    在dubbo配置文件中增加如下内容:

    <dubbo:provider filter="threadLocalCacheFilter" />

    Dubbo Filter

    dubbo有这样一个类ProtocolFilterWrapper,它负责加载项目中所有的filter,并负责链式调用。

    想学习设计模式的可以看看这个类是如何使用职责链模式的
    这里只看一个方法就可以了:

    • ExtensionLoader加载所以实现了Filter接口的类
    • 根据过滤条件过滤filter,里面有排序
    • 循环调用所有符合条件且经过排序的filter

    注意变量next,当前方法在执行invoke方法时,将调用传递到了next。这里应该会有最后一个终结器来处理实际方法的执行。

     private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
            Invoker<T> last = invoker;
            List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
            if (filters.size() > 0) {
                for (int i = filters.size() - 1; i >= 0; i --) {
                    final Filter filter = filters.get(i);
                    final Invoker<T> next = last;
                    last = new Invoker<T>() {
    
                        public Class<T> getInterface() {
                            return invoker.getInterface();
                        }
    
                        public URL getUrl() {
                            return invoker.getUrl();
                        }
    
                        public boolean isAvailable() {
                            return invoker.isAvailable();
                        }
    
                        public Result invoke(Invocation invocation) throws RpcException {
                            return filter.invoke(next, invocation);
                        }
    
                        public void destroy() {
                            invoker.destroy();
                        }
    
                        @Override
                        public String toString() {
                            return invoker.toString();
                        }
                    };
                }
            }
            return last;
        }

    总结

    结过三篇笔记,从最初的Context问题,到缓存的释放,基本可以非常方便的使用请求级的缓存了。这里需要注意的是需要明确哪些方案是适合做请求级缓存的。比如查询用户,有些操作中先插入用户然后再查询,如果查询的是被标记了请求级缓存的方法就会有问题。

  • 相关阅读:
    初遇黑客
    第四周学习总结
    第三周学习总结
    关于base64编码的原理及如何在python中实现
    在python中如何将十进制小数转换成二进制
    《信息安全专业导论》第二周学习总结
    计算机科学概论速读问题
    刘谨铭的自我介绍
    师生关系
    20201318快速浏览教材提问
  • 原文地址:https://www.cnblogs.com/ASPNET2008/p/6125795.html
Copyright © 2011-2022 走看看