变通实现微服务的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问题,到缓存的释放,基本可以非常方便的使用请求级的缓存了。这里需要注意的是需要明确哪些方案是适合做请求级缓存的。比如查询用户,有些操作中先插入用户然后再查询,如果查询的是被标记了请求级缓存的方法就会有问题。

posted on 2016-12-02 15:00  min.jiang  阅读(1172)  评论(0编辑  收藏  举报