Dubbo Filter

前言

Dubbo的Filter實現入口是在ProtocolFilterWrapper,由於ProtocolFilterWrapper是Protocol的包裝類,因此會在加載的Extension的時候被自動包裝進來,實如今ProtocolFilterWrapper.buildInvokerChain方法數組

源碼解析

private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
        Invoker<T> last = invoker;
        //得到全部符合條件的Filter(已經排好序的)
        List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
        if (!filters.isEmpty()) {
            //構建filter調用鏈
            for (int i = filters.size() - 1; i >= 0; i--) {
                final Filter filter = filters.get(i);
                final Invoker<T> next = last;
                last = new Invoker<T>() {

                    @Override
                    public Class<T> getInterface() {
                        return invoker.getInterface();
                    }

                    @Override
                    public URL getUrl() {
                        return invoker.getUrl();
                    }

                    @Override
                    public boolean isAvailable() {
                        return invoker.isAvailable();
                    }

                    @Override
                    public Result invoke(Invocation invocation) throws RpcException {
                        return filter.invoke(next, invocation);
                    }

                    @Override
                    public void destroy() {
                        invoker.destroy();
                    }

                    @Override
                    public String toString() {
                        return invoker.toString();
                    }
                };
            }
        }
        return last;
    }
複製代碼
  1. 得到全部符合條件的Filter(已經排好序的)
  2. 構建filter調用鏈

List filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group); 緩存

public List<T> getActivateExtension(URL url, String key, String group) {
        //key service.filter
        //獲取該service自定義filter,以逗號隔開
        String value = url.getParameter(key);
        return getActivateExtension(url, value == null || value.length() == 0 ? null : Constants.COMMA_SPLIT_PATTERN.split(value), group);
    }
複製代碼
public List<T> getActivateExtension(URL url, String[] values, String group) {
        List<T> exts = new ArrayList<T>();
        //自定義filter數組
        List<String> names = values == null ? new ArrayList<String>(0) : Arrays.asList(values);
        //若是自定義filter包含"-default"就不會加載dubbo自帶的filter
        if (!names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {
            //經過ExtensionLoader獲取全部的filter
            getExtensionClasses();
            //遍歷具備Activate註解的,這個cachedActivates是在getExtensionClasses()中放入的
            //dubbo自帶的filter都是Activate註解的,不須要手動在配置文件中加入這些filter,
            //若是本身也想寫全局的,也能夠加上Activate註解,這樣也會出如今cachedActivates中
            for (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) {
                String name = entry.getKey();
                Activate activate = entry.getValue();
                //判斷是否符合條件 是否在該組中,分爲provider和consumer
                if (isMatchGroup(group, activate.group())) {
                    //獲得該filter實例
                    T ext = getExtension(name);
                    if (!names.contains(name)
                            && !names.contains(Constants.REMOVE_VALUE_PREFIX + name)
                            && isActive(activate, url)) {
                        //若是自定義filter不包含該filter,
                        //而且自定義filter不包含「-」+該filter,而且isActive就放入exts中
                        //這裏若是不想用dubbo自帶的某個filter就能夠用「-」+filter名稱去除掉
                        exts.add(ext);
                    }
                }
            }
            //對這些filter進行排序
            Collections.sort(exts, ActivateComparator.COMPARATOR);
        }
        List<T> usrs = new ArrayList<T>();
        //遍歷自定義filter數組
        for (int i = 0; i < names.size(); i++) {
            String name = names.get(i);
            //若是filter名稱不以-開頭而且filter數組不包含「-」+該filter名稱
            if (!name.startsWith(Constants.REMOVE_VALUE_PREFIX)
                    && !names.contains(Constants.REMOVE_VALUE_PREFIX + name)) {
                //若是該名稱爲default    
                if (Constants.DEFAULT_KEY.equals(name)) {
                    //若是exts不爲空
                    if (!usrs.isEmpty()) {
                        //把usrs加入到exts中並放到前面,並清空usrs,
                        //這裏和後面實現了自定義filter和dubbo自帶的filter排序功能
                        //由於看後面最終返回的是exts(有序的)
                        //舉例 filter="filter1,filter2,filter3,default,filter4"
                        //最終exts順序就爲filter1,filter2,filter3,dubbo符合條件的filter,filter4
                        //若是沒有寫default,dubbo自帶的會在自定義的前面
                        exts.addAll(0, usrs);
        ,                usrs.clear();
                    }
                } else {
                    //若是不包含就放入usrs中
                    T ext = getExtension(name);
                    usrs.add(ext);
                }
            }
        }
        //變量完後,把usrs剩下的都放到exts中
        if (!usrs.isEmpty()) {
            exts.addAll(usrs);
        }
        
        return exts;
    }
複製代碼
private Map<String, Class<?>> getExtensionClasses() {
        //從緩存中獲取
        Map<String, Class<?>> classes = cachedClasses.get();
        if (classes == null) {
            synchronized (cachedClasses) {
                classes = cachedClasses.get();
                if (classes == null) {
                    //若是沒有就從文件中加載
                    classes = loadExtensionClasses();
                    //放到緩存中
                    cachedClasses.set(classes);
                }
            }
        }
        return classes;
    }
    
private Map<String, Class<?>> loadExtensionClasses() {
        //獲取該類型的SPI默認值
        final SPI defaultAnnotation = type.getAnnotation(SPI.class);
        if (defaultAnnotation != null) {
            String value = defaultAnnotation.value();
            if ((value = value.trim()).length() > 0) {
                String[] names = NAME_SEPARATOR.split(value);
                if (names.length > 1) {
                    throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
                            + ": " + Arrays.toString(names));
                }
                //設置默認值
                if (names.length == 1) cachedDefaultName = names[0];
            }
        }
        
        Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
        //META-INF/dubbo/internal/ 該文件下查找 com.alibaba.dubbo.rpc.Filter並加載
        loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
        //META-INF/dubbo/ 該文件下查找 com.alibaba.dubbo.rpc.Filter
        loadDirectory(extensionClasses, DUBBO_DIRECTORY);
        //META-INF/services/ 該文件下查找 com.alibaba.dubbo.rpc.Filter
        loadDirectory(extensionClasses, SERVICES_DIRECTORY);
        //最終Map<String, Class<?>> extensionClasses
        return extensionClasses;
    }
複製代碼

使用和小結

綜上源碼解析,dubbo是採用本身的SPI機制,一個serviceBean會被不少層東西包裝,其中包含ProtocolFilterWrapper
在轉換爲export的時候,會調用bash

public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
            return protocol.export(invoker);
        }
        return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
    }
複製代碼

構建filter鏈
dubbo的filter也是基於SPI機制,看上面代碼能夠看到他會去 META-INF/dubbo/internal/、META-INF/dubbo/、META-INF/services/ 該文件下查找 com.alibaba.dubbo.rpc.Filter並加載app

  1. 咱們要自定義filter 能夠在resources下建立對應文件夾和文件,而後在對應的service上面加上filter="hystrixFilter"
  2. 若是想把本身的filter變成dubbo自帶的filter同樣,能夠在本身的filter類上加上@Activate註解,這樣你就能夠認爲他是全局的filter了,就不須要在每一個service中引入了
  3. 全局的filter能夠用註解值 group區分是提供者和調用者這樣就更加靈活使用全局的filter了,還能夠用order進行排序
  4. 若是不想用全部dubbo自帶的filter或者說全局的filter 能夠用-default去除,若是不想用單個dubbo自帶的filter,能夠用「-」+filter名稱去除
  5. filter排序,通常狀況下自定義filter會在dubbo自帶的filter後面,能夠用filter="filter1,default,filter3"進行排序
相關文章
相關標籤/搜索