Добавление кирпичей в Dubbo: реализация пользовательской точки расширения фильтра

Dubbo
Добавление кирпичей в Dubbo: реализация пользовательской точки расширения фильтра

🌹🌹Если вы считаете, что моя статья вам полезна, не забудьте поставить звездочку на GitHub 🌹🌹

🌹🌹GitHub_awesome-it-blog🌹🌹


В последнее время я работаю со своими коллегами над созданием небольшой и красивой системы мониторинга производительности приложений (Pepper-Metrics).

Одним из требований является сбор данных о производительности отклика интерфейса Dubbo на стороне поставщика и на стороне потребителя, чтобы сохранить их в источнике данных или предоставить на принтер.

В этом контексте нам необходимо отслеживать каждый запрос и ответ от провайдера и потребителя. В Dubbo можно расширитьorg.apache.dubbo.rpc.Filterреализация интерфейса.

0 org.apache.dubbo.rpc.Filter введение

Фильтр можно понимать как перехват вызывающего процесса.Перехватчик будет действовать каждый раз при вызове метода.При расширении нужно обратить внимание на влияние на производительность.

Пользовательские фильтры по умолчанию выполняются после существующих фильтров.

1 Реализация фильтра Pepper-Metrics-Dubbo

В нашем проекте этот подмодуль называетсяPepper-Metrics-Dubbo, структура этого модуля следующая:

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()Общая реализация метода, обусловленная спецификой профиля сбора, дляProviderа такжеConsumerТребуется другой сборщик, здесь через его подклассDubboProviderProfilerFilterа такжеDubboConsumerProfilerFilterреализованы отдельно.

Вышеупомянутые отношения классов можно описать следующей диаграммой:

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 Использование пользовательского фильтра

Далее вам нужно настроить пользовательскую точку расширения в Dubbo и сообщить Dubbo, что я хочу использовать этот фильтр, соответственно вProviderа такжеConsumerСредняя конфигурация:

Первый взгляд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Аннотация, если вы не настроите ее свойства, автоматически активирует все точки расширения безоговорочно. В нашем проекте он будет активирован одновременноDubboConsumerProfilerFilterа такжеDubboProviderProfilerFilter.

Но в наших требованиях нет возможности активировать две точки расширения одновременно. При одновременной активации и поставщик услуг, и вызывающая сторона вызовут обе точки расширения. И что нам нужно, это вызов провайдераProvider, звонит звонящийConsumer.

Это можно сделать с помощьюgroupреализовать. ОпределенныйgroupПосле этого он активируется только для определенной группы.

В фильтре есть две группы:

String PROVIDER = "provider";
String CONSUMER = "consumer";

определяется какPROVIDERдействует только для провайдера, определенного какCONSUMERОн действует только на вызывающего абонента или может быть определен одновременно, и он вступит в силу в то же время.

Таким образом, необходимо полагаться только наpepper-metrics-dubboПакет может активировать точку расширения.

Ссылаться на


Добро пожаловать в мой публичный аккаунт WeChat

公众号