Skip to content

Commit

Permalink
feat(client): Add more observability in apollo config client
Browse files Browse the repository at this point in the history
feat(client): add unit test
  • Loading branch information
Rawven committed Aug 25, 2024
1 parent 0f73102 commit 749c034
Show file tree
Hide file tree
Showing 10 changed files with 687 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright 2022 Apollo Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.ctrip.framework.apollo.monitor.internal.event;

import java.util.HashMap;
import java.util.Map;

/**
* @author Rawven
*/
public class ApolloConfigMetricsEvent {

private final String name;
private String tag;
private final Map<String, Object> attachments;

public ApolloConfigMetricsEvent(String name, String tag, Map<String, Object> attachments) {
this.name = name;
this.tag = tag;
this.attachments = attachments != null ? new HashMap<>(attachments) : new HashMap<>();
}

public ApolloConfigMetricsEvent withTag(String tag) {
this.tag = tag;
return this;
}

public String getTag() {
return tag;
}
public String getName() {
return name;
}

public ApolloConfigMetricsEvent putAttachment(String key, Object value) {
this.attachments.put(key, value);
return this;
}


@SuppressWarnings("unchecked")
public <T> T getAttachmentValue(String key) {
Object value = attachments.get(key);
if (value == null) {
return null;
}
try {
return (T) value;
} catch (ClassCastException e) {
throw new IllegalArgumentException("Value for key " + key + " is not of expected type", e);
}
}


public void publish() {
ApolloConfigMetricsEventPublisher.publish(this);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2022 Apollo Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.ctrip.framework.apollo.monitor.internal.event;

import java.util.HashMap;

/**
* @author Rawven
* @date 2024/08/08
*/
public class ApolloConfigMetricsEventFactory {

public static volatile ApolloConfigMetricsEventFactory INSTANCE;

private ApolloConfigMetricsEventFactory() {
}

public static ApolloConfigMetricsEventFactory getInstance() {
if (INSTANCE == null) {
synchronized (ApolloConfigMetricsEventFactory.class) {
if (INSTANCE == null) {
INSTANCE = new ApolloConfigMetricsEventFactory();
}
}
}
return INSTANCE;
}

public ApolloConfigMetricsEvent createEvent(String name) {
return new ApolloConfigMetricsEvent(name, null, new HashMap<>(2));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright 2022 Apollo Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.ctrip.framework.apollo.monitor.internal.event;

import com.ctrip.framework.apollo.build.ApolloInjector;
import com.ctrip.framework.apollo.monitor.internal.listener.ApolloClientMetricsEventListener;
import com.ctrip.framework.apollo.monitor.internal.listener.ApolloClientMetricsEventListenerManager;
import com.ctrip.framework.apollo.util.ConfigUtil;

/**
* @author Rawven
*/
public class ApolloConfigMetricsEventPublisher {

private static final ApolloClientMetricsEventListenerManager COLLECTOR_MANAGER = ApolloInjector.getInstance(
ApolloClientMetricsEventListenerManager.class);
private static final ConfigUtil m_configUtil = ApolloInjector.getInstance(ConfigUtil.class);

public static void publish(ApolloConfigMetricsEvent event) {
if (m_configUtil.getClientMonitorEnabled()) {
for (ApolloClientMetricsEventListener collector : COLLECTOR_MANAGER.getCollectors()) {
if (collector.isSupport(event)) {
collector.collect(event);
return;
}
}
}
}
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Copyright 2022 Apollo Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.ctrip.framework.apollo.monitor.internal.listener;

import com.ctrip.framework.apollo.monitor.internal.event.ApolloConfigMetricsEvent;
import com.ctrip.framework.apollo.monitor.internal.model.CounterModel;
import com.ctrip.framework.apollo.monitor.internal.model.GaugeModel;
import com.ctrip.framework.apollo.monitor.internal.model.SampleModel;
import com.google.common.collect.Maps;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

/**
* 抽象的 Metrics 收集器 用于收集数据和导出指标样本
*
* @author Rawven
*/
public abstract class AbstractApolloClientMetricsEventListener implements
ApolloClientMetricsEventListener {

private final Map<String, CounterModel> counterSamples = Maps.newConcurrentMap();
private final Map<String, GaugeModel> gaugeSamples = Maps.newConcurrentMap();
private final AtomicBoolean isUpdated = new AtomicBoolean();
private final String tag;

public AbstractApolloClientMetricsEventListener(String tag) {
this.tag = tag;
}

/**
* Specific collection logic
*/
protected abstract void collect0(ApolloConfigMetricsEvent event);

/**
* Convenient for indicators that can only be obtained from the status object
*/
protected abstract void export0();

@Override
public String mBeanName() {
return tag;
}

@Override
public boolean isSupport(ApolloConfigMetricsEvent event) {
return tag.equals(event.getTag());
}

@Override
public void collect(ApolloConfigMetricsEvent event) {
collect0(event);
isUpdated.set(true);
}

/**
* Whether the sample data has been updated
*/
@Override
public boolean isMetricsSampleUpdated() {
return isUpdated.getAndSet(false);
}

@Override
public List<SampleModel> export() {
export0();
List<SampleModel> samples = new ArrayList<>(counterSamples.values());
samples.addAll(gaugeSamples.values());
return samples;
}


/**
* tool method for updating indicator model
*/
public void createOrUpdateGaugeSample(String mapKey, String metricsName, Map<String, String> tags,
double value) {
if (!gaugeSamples.containsKey(mapKey)) {
GaugeModel builder = (GaugeModel) GaugeModel.create(metricsName, 0).putTags(tags);
gaugeSamples.put(mapKey, builder);
}
gaugeSamples.get(mapKey).setValue(value);
}

/**
* tool method for updating indicator model
*/
public void createOrUpdateCounterSample(String mapKey, String metricsName,
Map<String, String> tags,
double increaseValue) {
if (!counterSamples.containsKey(mapKey)) {
CounterModel builder = (CounterModel) CounterModel.create(metricsName, 0).putTags(tags);
counterSamples.put(mapKey, builder);
}
counterSamples.get(mapKey).increase(increaseValue);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright 2022 Apollo Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.ctrip.framework.apollo.monitor.internal.listener;

import com.ctrip.framework.apollo.monitor.internal.event.ApolloConfigMetricsEvent;
import com.ctrip.framework.apollo.monitor.internal.model.SampleModel;
import java.util.List;

/**
* @author Rawven
*/
public interface ApolloClientMetricsEventListener {


/**
* mbean name
*/
String mBeanName();

/**
* is support the event
*/
boolean isSupport(ApolloConfigMetricsEvent event);

/**
* collect metrics from event
*/
void collect(ApolloConfigMetricsEvent event);

/**
* is samples updated
*/
boolean isMetricsSampleUpdated();

/**
* export to a format recognized by the monitoring system
*/
List<SampleModel> export();


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2022 Apollo Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.ctrip.framework.apollo.monitor.internal.listener;

import com.ctrip.framework.apollo.core.spi.Ordered;
import java.util.List;

/**
* @author Rawven
*/
public interface ApolloClientMetricsEventListenerManager extends Ordered {

/**
* get collectors
*/
List<ApolloClientMetricsEventListener> getCollectors();

@Override
default int getOrder() {
return 0;
}
}
Loading

0 comments on commit 749c034

Please sign in to comment.