🌹🌹若是您以爲個人文章對您有幫助的話,記得在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
Filter能夠理解爲調用過程攔截,每次方法調用該攔截器都會生效,擴展時須要注意對性能的影響。spring
用戶定義的Filter默認在已有的Filter以後被執行。apache
在咱們的項目中,這個子模塊被命名爲 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時,針對 Provider
和 Consumer
須要不一樣的收集器,這裏經過其子類 DubboProviderProfilerFilter
和 DubboConsumerProfilerFilter
分別實現。併發
上述的類關係可經過下圖描述:
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就能夠掃描到自定義的擴展點。
接下來須要將自定義的擴展點配置到Dubbo中,告訴Dubbo我要使用這個Filter,分別在 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>
複製代碼
從上文得知,咱們自定義的擴展點必需要修改配置才能生效,這樣一來,是有代碼侵入的。那麼,能不能引入 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
以後,就只對特定的group激活了。
在Filter中,有兩個group:
String PROVIDER = "provider";
String CONSUMER = "consumer";
複製代碼
定義爲 PROVIDER
就只對提供方生效,定義爲 CONSUMER
就只對調用方生效,也能夠同時定義,那就同時生效。
這樣一來,只須要依賴 pepper-metrics-dubbo
包便可激活擴展點了。