dubbo 自定义filter 实现公用过滤器处理
Dubbo的Filter机制,是专门为服务提供方和服务消费方调用过程进行拦截设计的,每次远程方法执行,该拦截都会被执行。这样就为开发者提供了非常方便的扩展性,比如为dubbo接口实现ip白名单功能、监控功能等等。
怎样添加filter?
1. 基于xml配置文件配置filter
1、自定义Filter,必须继承com.alibaba.dubbo.rpc.Filter接口
2、在resources目录下添加纯文本文件META-INF/dubbo/com.alibaba.dubbo.rpc.Filter,内容写成 xxx=xxx.xxx.xxxFilter
3、在dubbo配置xml中添加 <dubbo:provider filter="xxxFilter" /> 或者 <dubbo:consumer filter="xxxFilter" /> 使Filter生效。
2、在resources目录下添加纯文本文件META-INF/dubbo/com.alibaba.dubbo.rpc.Filter,内容写成 xxx=xxx.xxx.xxxFilter
3、在dubbo配置xml中添加 <dubbo:provider filter="xxxFilter" /> 或者 <dubbo:consumer filter="xxxFilter" /> 使Filter生效。
使用xml配置有两个问题: 1. 显示配置,影响业务开发; 2. 配置麻烦,万一忘记添加则不生效;
有没有一种更简单的方法,可以公用的增加一个filter?
有的,dubbo提供了一个注解扫描: @Activate
2. 基于 @Activate 注解增加filter
@Activate 定义如下
/** * 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 * * @return extension list which should be put before the current one */ String[] before() default {}; /** * Relative ordering info, optional * * @return extension list which should be put after the current one */ String[] after() default {}; /** * Absolute ordering info, optional * * @return absolute ordering info */ int order() default 0; }
注解的意思也就是,自动扫描 extentions , filter 只是其中的一个!
操作步骤:
1. 添加 Filter 实现如下:
@Activate(group = { Constants.PROVIDER }) public class ProviderHelloFilter implements Filter { private static final Logger logger = LoggerFactory.getLogger(ProviderHelloFilter.class); private final TraceConf conf = TraceConfLoader.loadAppProperties(); @Override public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { logger.info("hello ok!"); return invoker.invoke(invocation); } }
2. 基于dubbo 的 spi 机制,添加一个配置文件: META-INF/(services|dubbo|dubbo/Interal)/com.alibaba.dubbo.rpc.Filter, 内容如下
helloFilter=com.xxx.ProviderHelloFilter
具体加载逻辑如下:
// 初次加载所有 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; } // synchronized in getExtensionClasses private Map<String, Class<?>> loadExtensionClasses() { 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<?>>(); loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY); loadDirectory(extensionClasses, DUBBO_DIRECTORY); loadDirectory(extensionClasses, SERVICES_DIRECTORY); return extensionClasses; }
自动扫描 @Activate 注解实现如下:(com.alibaba.dubbo.common.extension.ExtensionLoader)
/** * Get activate extensions. * * @param url url * @param values extension point names * @param group group * @return extension list which are activated * @see com.alibaba.dubbo.common.extension.Activate */ public List<T> getActivateExtension(URL url, String[] values, String group) { List<T> exts = new ArrayList<T>(); List<String> names = values == null ? new ArrayList<String>(0) : Arrays.asList(values); if (!names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) { getExtensionClasses(); for (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) { String name = entry.getKey(); Activate activate = entry.getValue(); if (isMatchGroup(group, activate.group())) { T ext = getExtension(name); if (!names.contains(name) && !names.contains(Constants.REMOVE_VALUE_PREFIX + name) && isActive(activate, url)) { exts.add(ext); } } } Collections.sort(exts, ActivateComparator.COMPARATOR); } List<T> usrs = new ArrayList<T>(); for (int i = 0; i < names.size(); i++) { String name = names.get(i); if (!name.startsWith(Constants.REMOVE_VALUE_PREFIX) && !names.contains(Constants.REMOVE_VALUE_PREFIX + name)) { if (Constants.DEFAULT_KEY.equals(name)) { if (!usrs.isEmpty()) { exts.addAll(0, usrs); usrs.clear(); } } else { T ext = getExtension(name); usrs.add(ext); } } } if (!usrs.isEmpty()) { exts.addAll(usrs); } return exts; }
而这个扫描的动作,则会在第一次调用远程服务或者在暴露远程服务时,进行操作!
不要害怕今日的苦,你要相信明天,更苦!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?