🌹🌹 If you find my article helpful, remember to star on GitHub 🌹🌹
🌹 🌹 GitHub_awesome – it – blog 🌹 🌹
I recently collaborated with colleagues to develop a small but beautiful application performance monitoring framework (Pepper-metrics).
One requirement is to collect the performance data of Dubbo’s interface responses on the Provider and Consumer sides for storage in the DataSource or for Printer use.
In this context, we need to monitor every request and response on both the Provider and Consumer side. Can be extended in Dubbo org. Apache. Dubbo. RPC. The Filter interface implementation.
0 org. The apache. Dubbo. RPC. The Filter is introduced
Filter can be understood as a call procedure interceptor, which takes effect every time a method calls the interceptor.
User-defined filters are executed after existing filters by default.
1 Pepper-Metrics-Dubbo Filter implementation
In our project, this submodule is named pepper-metrics-Dubbo, and the structure of this module is as follows:
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
Copy the code
In Pepper – Metrics – Dubbo, DubboProfilerFilterTemplate class implements the org. Apache. Dubbo. RPC. The Filter interface.
This is a template class that defines a generic implementation of the filter.invoke () method. Since different collectors are required for providers and consumers when collecting profiles specifically, Through its subclasses DubboProviderProfilerFilter here and DubboConsumerProfilerFilter implementation respectively.
The above class relationship can be described in the following figure:
DubboProfilerFilterTemplate outlined implementation is as follows:
public abstract class DubboProfilerFilterTemplate implements Filter {
// Provider collector
static final Stats PROFILER_STAT_IN = Profiler.Builder
.builder()
.name("app.dubbo.request.in")
.build();
// Collector for 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 {
/ / to omit... Some necessary preparations
// Template method, before trace...
beforeInvoke(tags);
try {
Result result = invoker.invoke(invocation);
// Record whether the call reported an error
if (result == null || result.hasException()) {
isError = true;
}
specialException = false;
return result;
} finally {
if (specialException) {
isError = true;
}
// Template method, after trace...afterInvoke(tags, begin, isError); }}abstract void afterInvoke(String[] tags, long begin, boolean isError);
abstract void beforeInvoke(String[] tags);
}
Copy the code
The two implementation classes are as follows:
// Provider
@Activate(group = {PROVIDER})
public class DubboProviderProfilerFilter extends DubboProfilerFilterTemplate {
@Override
void afterInvoke(String[] tags, long begin, boolean isError) {
// Record the response implementation
PROFILER_STAT_IN.observe(System.nanoTime() - begin, TimeUnit.NANOSECONDS, tags);
// The number of concurrent requests decreases
PROFILER_STAT_IN.decConc(tags);
// Record the number of errors
if(isError) { PROFILER_STAT_IN.error(tags); }}@Override
void beforeInvoke(String[] tags) {
// The number of concurrent requests increasesPROFILER_STAT_IN.incConc(tags); }}Copy the code
// 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); }}Copy the code
After writing the implementation class, you need to configure the extension file for Dubbo in the Resources directory of your project.
Under the resources to create a meta-inf/dubbo/org. Apache. Dubbo. RPC. The Filter files, content is as follows:
dubboProviderProfiler=com.pepper.metrics.integration.dubbo.DubboProviderProfilerFilter
dubboConsumerProfiler=com.pepper.metrics.integration.dubbo.DubboConsumerProfilerFilter
Copy the code
This allows Dubbo to scan for custom extension points.
2 Use custom Filter
Next, I need to configure the custom extension point to Dubbo, and tell Dubbo I want to use this Filter, and configure it in Provider and Consumer respectively:
First look at the 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"/>
<! -- Configure custom extension points here -->
<dubbo:service filter="default,dubboProviderProfiler" interface="com.pepper.metrics.sample.dubbo.spring.api.DemoService" ref="demoService" />
</beans>
Copy the code
Note: Default represents the existing extension point, and dubboProviderProfiler is our custom extension point. This configuration means that our custom extension point is executed after the existing extension point.
Also, configure custom extension points on the Consumer side:
<?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"/>
<! -- Configure custom extension points here -->
<dubbo:reference filter="default,dubboConsumerProfiler" id="demoService" check="true" interface="com.pepper.metrics.sample.dubbo.spring.api.DemoService" />
</beans>
Copy the code
3 Customize automatic activation of extension points
As we know from the above, our custom extension point has to be modified to take effect, which is code intrusion. Can we introduce the pepper-metrics- Dubbo jar without modifying the configuration?
The answer is: yes!
Pepper -metrics- Dubbo uses the @activate mechanism provided by Dubbo. This annotation can be used on a class or method. This allows Dubbo to activate this extension automatically, simplifying configuration.
Using Provider as an example, see how this thing works in 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) {
// ...}}Copy the code
First, if you configure only the @Activate annotation without customizing its attributes, all extension points will automatically be activated unconditionally. In our project, just will activate DubboConsumerProfilerFilter and DubboProviderProfilerFilter at the same time.
However, it is not possible to activate two extension points at the same time in our requirements. If activated at the same time, both service providers and callers invoke both extension points. What we need is for the Provider to call Provider and the caller to call Consumer.
This can be done through groups. Once groups are defined, only specific groups are activated.
In Filter, there are two groups:
String PROVIDER = "provider";
String CONSUMER = "consumer";
Copy the code
Defining it as PROVIDER only applies to the PROVIDER, defining it as CONSUMER only applies to the caller, or both.
This way, you only need to rely on the pepper-metrics-Dubbo package to activate the extension point.
reference
- Dubbo.apache.org/zh-cn/docs/…
- Dubbo.apache.org/zh-cn/docs/…