给Dubbo添砖加瓦:自定义Filter扩展点实现

🌹🌹若是您以为个人文章对您有帮助的话,记得在GitHub上star一波哈🌹🌹php

🌹🌹GitHub_awesome-it-blog 🌹🌹html


最近在与同事协同开发一款小而美的应用程序性能监控框架(Pepper-Metrics)。java

其中一个需求,须要收集Dubbo在Provider端和Consumer端的接口响应性能数据,以便存储到DataSource中或提供给Printer使用。git

在此背景下,咱们须要对Provider端和Consumer端的每一次请求和响应进行监控。在Dubbo中,能够经过扩展 org.apache.dubbo.rpc.Filter 接口实现。github

0 org.apache.dubbo.rpc.Filter介绍

Filter能够理解为调用过程拦截,每次方法调用该拦截器都会生效,扩展时须要注意对性能的影响。spring

用户定义的Filter默认在已有的Filter以后被执行。apache

1 Pepper-Metrics-Dubbo的Filter实现

在咱们的项目中,这个子模块被命名为 Pepper-Metrics-Dubbo,此模块的结构以下:api

Pepper-Metrics-Dubbo
    |-src/main/java
        |-com.pepper.metrics.integration.dubbo
            |-DubboProfilerFilterTemplate
            |-DubboProviderProfilerFilter
            |-DubboConsumerProfilerFilter
    |-src/main/resources
        |-META-INF
            |-dubbo
                |-org.apache.dubbo.rpc.Filter
复制代码

Pepper-Metrics-Dubbo 中,DubboProfilerFilterTemplate 类实现了 org.apache.dubbo.rpc.Filter 接口。微信

这是一个模板类,定义了 Filter.invoke() 方法的通用实现,因为具体收集profile时,针对 ProviderConsumer 须要不一样的收集器,这里经过其子类 DubboProviderProfilerFilterDubboConsumerProfilerFilter 分别实现。并发

上述的类关系可经过下图描述:

Pepper-Metrics-Dubbo类图

DubboProfilerFilterTemplate大体实现以下:

public abstract class DubboProfilerFilterTemplate implements Filter {

    // Provider的收集器
    static final Stats PROFILER_STAT_IN = Profiler.Builder
            .builder()
            .name("app.dubbo.request.in")
            .build();
    // Consumer的收集器
    static final Stats PROFILER_STAT_OUT = Profiler.Builder
            .builder()
            .name("app.dubbo.request.out")
            .build();

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        // 省略... 一些必要的准备工做

        // 模板方法,before trace...
        beforeInvoke(tags);
        try {
            Result result = invoker.invoke(invocation);
            // 记录是否调用报错
            if (result == null || result.hasException()) {
                isError = true;
            }

            specialException = false;

            return result;
        } finally {
            if (specialException) {
                isError = true;
            }
            // 模板方法,after trace...
            afterInvoke(tags, begin, isError);
        }
    }

    abstract void afterInvoke(String[] tags, long begin, boolean isError);

    abstract void beforeInvoke(String[] tags);
}
复制代码

两个实现类以下:

// Provider
@Activate(group = {PROVIDER})
public class DubboProviderProfilerFilter extends DubboProfilerFilterTemplate {
    @Override
    void afterInvoke(String[] tags, long begin, boolean isError) {
        // 记录响应实现
        PROFILER_STAT_IN.observe(System.nanoTime() - begin, TimeUnit.NANOSECONDS, tags);
        // 并发数递减
        PROFILER_STAT_IN.decConc(tags);
        // 记录错误数
        if (isError) {
            PROFILER_STAT_IN.error(tags);
        }
    }

    @Override
    void beforeInvoke(String[] tags) {
        // 并发数递增
        PROFILER_STAT_IN.incConc(tags);
    }
}
复制代码
// Consumer
@Activate(group = {CONSUMER})
public class DubboConsumerProfilerFilter extends DubboProfilerFilterTemplate {
    @Override
    void afterInvoke(String[] tags, long begin, boolean isError) {
        PROFILER_STAT_OUT.observe(System.nanoTime() - begin, TimeUnit.NANOSECONDS, tags);
        PROFILER_STAT_OUT.decConc(tags);
        if (isError) {
            PROFILER_STAT_OUT.error(tags);
        }
    }

    @Override
    void beforeInvoke(String[] tags) {
        PROFILER_STAT_OUT.incConc(tags);
    }
}
复制代码

写完实现类后,须要在项目的 resources 目录下配置Dubbo的扩展文件。

resources 下建立 META-INF/dubbo/org.apache.dubbo.rpc.Filter 文件,内容以下:

dubboProviderProfiler=com.pepper.metrics.integration.dubbo.DubboProviderProfilerFilter
dubboConsumerProfiler=com.pepper.metrics.integration.dubbo.DubboConsumerProfilerFilter
复制代码

这样Dubbo就能够扫描到自定义的扩展点。

2 自定义Filter的使用

接下来须要将自定义的扩展点配置到Dubbo中,告诉Dubbo我要使用这个Filter,分别在 ProviderConsumer 中配置:

首先看一下 Provider

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <context:property-placeholder/>

    <dubbo:application name="demo-provider"/>

    <dubbo:registry address="multicast://224.5.6.7:1234"/>

    <bean id="demoService" class="com.pepper.metrics.sample.dubbo.spring.provider.DemoServiceImpl"/>

    <!-- 在这里配置自定义的扩展点 -->
    <dubbo:service filter="default,dubboProviderProfiler" interface="com.pepper.metrics.sample.dubbo.spring.api.DemoService" ref="demoService" />

</beans>
复制代码

说明:default 表明已有的扩展点,dubboProviderProfiler 是咱们自定义的扩展点,这样配置表示咱们自定义的扩展点在已有的扩展点以后执行。

一样,在 Consumer 端配置自定义扩展点:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <context:property-placeholder/>

    <dubbo:application name="demo-consumer"/>

    <dubbo:registry address="multicast://224.5.6.7:1234"/>

    <!-- 在这里配置自定义的扩展点 -->
    <dubbo:reference filter="default,dubboConsumerProfiler" id="demoService" check="true" interface="com.pepper.metrics.sample.dubbo.spring.api.DemoService" />

</beans>
复制代码

3 自定义扩展点的自动激活

从上文得知,咱们自定义的扩展点必需要修改配置才能生效,这样一来,是有代码侵入的。那么,能不能引入 pepper-metrics-dubbo 的jar包后,不用修改配置,就直接生效呢?

答案是:固然能够!

pepper-metrics-dubbo 使用了Dubbo提供的 @Activate 机制。这个注解可用于类或方法上。其做用是可让Dubbo自动激活此扩展,从而简化配置。

Provider 为案例,看一下这个东西在 pepper-metrics-dubbo 里是咋用的。

@Activate(group = {PROVIDER}) // is here
public class DubboProviderProfilerFilter extends DubboProfilerFilterTemplate {
    @Override
    void afterInvoke(String[] tags, long begin, boolean isError) {
        // ...
    }

    @Override
    void beforeInvoke(String[] tags) {
        // ...
    }
}
复制代码

首先,若是只配置 @Activate 注解,不自定义其属性的话,会无条件自动激活全部扩展点。在咱们的项目中,就是会同时激活 DubboConsumerProfilerFilterDubboProviderProfilerFilter

但在咱们的需求中是不能同时激活两个扩展点的。若是同时激活,服务提供方和调用方都会同时调用两个扩展点。而咱们须要的是提供方调用 Provider,调用方调用 Consumer

这能够经过 group 来实现。定义了 group 以后,就只对特定的group激活了。

在Filter中,有两个group:

String PROVIDER = "provider";
String CONSUMER = "consumer";
复制代码

定义为 PROVIDER 就只对提供方生效,定义为 CONSUMER 就只对调用方生效,也能够同时定义,那就同时生效。

这样一来,只须要依赖 pepper-metrics-dubbo 包便可激活扩展点了。

参考


欢迎关注个人微信公众号

公众号
相关文章
相关标签/搜索