Dubbo过滤器开发要点与避坑指南

Dubbo过滤器开发要点与避坑指南

Dubbo过滤器是dubbo提供的常见扩展机制之一,用于支持用户在dubbo的生命周期里面进行统一的拦截处理和特色定制,而不需要对上层业务代码大动干戈。

从原理和设计理念上来讲,Dubbo的过滤器是必须要能够做到上层业务无感和免配置的,否则咋进行统一拦截,咋偷偷摸摸干坏事呢。但是常见的DubboFilter相关文档和实践说明时,往往只说了半截和最初级的Filter开发知识,包括Dubbo官网的Filter教程。

官网Filter教程:https://cn.dubbo.apache.org/zh-cn/overview/tasks/extensibility/filter/

导致大家出现误解,只能在比如provider或者consumer的配置中,或者service的配置中,配置了具体的filter,才能生效。这真是个天大的误解。要是这样的话,dubbo自己的内置的一堆filter怎么串进去的?奇迹么?

这里就要提到一个注解:@Activate,官方的一个使用例子如下:

https://cn.dubbo.apache.org/zh-cn/blog/2018/07/01/%E7%AC%AC%E4%B8%80%E4%B8%AA-dubbo-filter

但是使用@Activate的时候千万要注意几个属性的用法,一不小心就不是自动激活了。

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.dubbo.common.extension;

import org.apache.dubbo.common.URL;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Activate. This annotation is useful for automatically activate certain extensions with the given criteria,
 * for examples: <code>@Activate</code> can be used to load certain <code>Filter</code> extension when there are
 * multiple implementations.
 * <ol>
 * <li>{@link Activate#group()} specifies group criteria. Framework SPI defines the valid group values.
 * <li>{@link Activate#value()} specifies parameter key in {@link URL} criteria.
 * </ol>
 * SPI provider can call {@link ExtensionLoader#getActivateExtension(URL, String, String)} to find out all activated
 * extensions with the given criteria.
 *
 * @see SPI
 * @see URL
 * @see ExtensionLoader
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {
    /**
     * Activate the current extension when one of the groups matches. The group passed into
     * {@link ExtensionLoader#getActivateExtension(URL, String, String)} will be used for matching.
     *
     * @return group names to match
     * @see ExtensionLoader#getActivateExtension(URL, String, String)
     */
    String[] group() default {};

    /**
     * Activate the current extension when the specified keys appear in the URL's parameters.
     * <p>
     * For example, given <code>@Activate("cache, validation")</code>, the current extension will be return only when
     * there's either <code>cache</code> or <code>validation</code> key appeared in the URL's parameters.
     * </p>
     *
     * @return URL parameter keys
     * @see ExtensionLoader#getActivateExtension(URL, String)
     * @see ExtensionLoader#getActivateExtension(URL, String, String)
     */
    String[] value() default {};

    /**
     * Relative ordering info, optional
     * Deprecated since 2.7.0
     *
     * @return extension list which should be put before the current one
     */
    @Deprecated
    String[] before() default {};

    /**
     * Relative ordering info, optional
     * Deprecated since 2.7.0
     *
     * @return extension list which should be put after the current one
     */
    @Deprecated
    String[] after() default {};

    /**
     * Absolute ordering info, optional
     *
     * Ascending order, smaller values will be in the front o the list.
     *
     * @return absolute ordering info
     */
    int order() default 0;

    /**
     * Activate loadClass when the current extension when the specified className all match
     * @return className names to all match
     */
    String[] onClass() default {};
}

最后一个属性,在老版本中没有,先不用管。

两个过期的before,after也不要管。order用来排序的。group使用来进行分组匹配的,常见的分组有

org.apache.dubbo.common.constants.CommonConstants.CONSUMER

org.apache.dubbo.common.constants.CommonConstants.PROVIDER

public interface CommonConstants {

    String DUBBO = "dubbo";

    String TRIPLE = "tri";

    String PROVIDER = "provider";

    String CONSUMER = "consumer";

在Filter的进行如下注解

@Activate(group = {CONSUMER, PROVIDER})
public class CacheFilter implements Filter {

看一下dubbo代码中的是否激活的匹配条件,可以看到group要进行匹配,然后value也会进行匹配。如果不符合条件则不激活。

所以,如果你加了这个注解,还不生效,看看是不是自己画蛇添足,写了个value吧?如果不了解value的用法,不要随便写。写上就无法激活。

if (isMatchGroup(group, activateGroup)
                        && !namesSet.contains(name)
                        && !namesSet.contains(REMOVE_VALUE_PREFIX + name)
                        && isActive(cachedActivateValues.get(name), url)) {

                    activateExtensionsMap.put(getExtensionClass(name), getExtension(name));
                }

另外提示Filter开发过程避坑

1、确保META-INF/dubbo这两级是用创建目录的方式建立起来的,别用创建包的方式建立,不确定的话,就去文件系统上老实创建。

2、确保以上资源文件,被正确打包进了jar包中。通常需要注意maven的resource配置,确认包含了如上目录。

3、Filter的异常处理,一定要充分,自己代码写得不好报错不要紧,别把整体带崩了。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注