千分尺 - 普罗米修斯:有些仪表显示,有些不显示

Micrometer - Prometheus: Some meters are showing, others are not

我有一个 rabbitmq 消息队列,许多其他服务在该队列上报告所谓的 Point 的状态更新。现在,在一个单独的服务(用 SpringBoot 编写)上,我需要监听那些 Point 更新并将其转换为 Prometheus 可抓取的端点。

所以我的计划是将传入的 Point 对象转换为 Meter 并在 MeterRegistry 中注册它们。这行得通,但仅适用于某些要点。我还没有弄清楚,哪些是可见的,哪些不是,因为它看起来取决于它们在服务重启后进入的顺序。我还没有找出任何模式,但这有助于故障排除。

根据我阅读千分尺文档的理解,Meter 创建了一次,我们给它一个对象和一个函数,允许它从该对象检索度量的双精度值。因为,我每隔几秒就会有 Point 的新实例出现,该值不会因为 Meter 引用旧的 Point.

而更新

假设这是正确的,我在 PointPointWrapper)周围添加了一个包装器,我自己将其传递给 MeterPointWrapper 的缓存实例.现在,当一个新的 Point 进来时,我检查我是否已经有那个 PointPointWrapper,如果是,我将包装器中的 Point 实例替换为新的一.

@Service
public class PointSubscriber {
    
    private final MetricsService metrics;

    public PointSubscriber(@Autowired MetricsService metrics) {
        this.metrics = metrics;
    }

    @Bean
    public Consumer<PointUpdate> processPoint() {
        return (update) -> {
            metrics.update(update.getPoint());
        };
    }

@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class MetricsService {

    private Logger logger = LoggerFactory.getLogger(getClass());

    private final MeterRegistry meterRegistry;

    private Map<String, PointWrapper> cache = new HashMap<>();

    public void update(Point point) {

        // Check if wrapper already in cache
        String pointId = point.getId();
        PointWrapper cached = cache.get(pointId);
        
        // Replace the point in the wrapper to update the value
        if (cached != null) {
            logger.debug("Updating value for {}", point.getId());
            cached.setPoint(point);
            
        // Create the wrapper, cache it and register Meter
        } else {
            PointWrapper pointMeter = PointWrapper.from(point.getId(), point);
            // Don't register Meters that will return null
            if (pointMeter.getMetricValue() == null) {
                logger.debug("Not going to register point with null value: {}", point.getId());
                return;
            }
            logger.debug("Registering point {}", point.getId());
            register(pointMeter, meterRegistry);
            cache.put(pointId, pointMeter);
        }
    }
    
    public Meter register(PointWrapper pointMeter, MeterRegistry registry) {
        Set<Tag> tags = new HashSet<>();
        tags.add(Tag.of("pointId", pointMeter.getPoint().getId()));
        tags.addAll(pointMeter.getPoint().getLabels().entrySet().stream()
            .map (e -> Tag.of(e.getKey(),e.getValue()))
            .collect(Collectors.toSet()));
        
        return Gauge.builder(pointMeter.getMetricName(), pointMeter, PointWrapper::getMetricValue)
            .tags(tags)
            .register(registry);
    }
    
}
@Data
@Builder
public class PointWrapper {
    
    public static PointWrapper from(String id, Point point) {       
        return PointWrapper.builder()
            .id(id)
            .metricName("symphony_point")
            .point(point)
            .build();
    }
        
    private String id;
    
    private String metricName;
     
    @EqualsAndHashCode.Exclude
    private Point point; 
    
    public Double getMetricValue() {
        if (point == null)
            return null;
        if (point instanceof QuantityPoint) {
            return ((QuantityPoint) point).getValue();
        } else if (point instanceof StatePoint<?>) {
            StatePoint<?> s = (StatePoint<?>) point;
            if (s.getState() == null)
                return null;
            return Double.valueOf(s.getState().asNumber());
        }
        return null;
    }
        
}

正如我所提到的,这会导致普罗米修斯端点中出现大量缺失的数据点。我读到 Meter 由它们的 nametags 唯一标识。名称始终为 symphony_point,但我将 Point 的 ID 添加为名为 pointIdtag。正因为如此,每个 Meter.Id 都是独一无二的。

我可以看到像

这样的日志
Registering point outdoor_brightness_north

但 Prometheus 端点中缺少该点。

有什么想法吗?

更新 @checketts 指出具有相同名称的指标必须具有相同的标签集。我快速检查可以确认,我使用的数据不是这样的:

symphony.point area pointId device property floor room
symphony.point area pointId device property floor room
symphony.point area pointId property room floor device
symphony.point area pointId property room floor device
symphony.point area room pointId device property floor
symphony.point pointId area room device property floor
symphony.point area room pointId device property floor
symphony.point area room property pointId floor device
symphony.point pointId area property device
symphony.point area device property pointId
symphony.point area room pointId floor device property
symphony.point area pointId device property floor room
symphony.point area pointId device property room floor
symphony.point area pointId property floor device room
symphony.point area room property pointId floor device
symphony.point area property room floor pointId device
symphony.point pointId area room property floor device
symphony.point area device pointId property
symphony.point area device property pointId floor room
symphony.point area pointId room device property floor
symphony.point area pointId room device property floor
symphony.point area room pointId device property floor
symphony.point area pointId room floor device property
symphony.point pointId area device property
symphony.point area property room floor device pointId
symphony.point area pointId device room property floor
symphony.point area room device property floor pointId
symphony.point area device pointId property floor room
symphony.point area pointId property floor device room
symphony.point pointId area device property
symphony.point area pointId device property floor room
symphony.point area pointId property room floor device
symphony.point area pointId room device property floor
symphony.point pointId property area device
symphony.point area property pointId floor device room
symphony.point area room property pointId floor device
symphony.point area room pointId property floor device
symphony.point area pointId floor device property room
symphony.point area room device pointId property floor
symphony.point pointId property area device
symphony.point area room device property pointId floor
symphony.point area device property floor pointId room
symphony.point area room pointId floor device property
symphony.point area pointId property room floor device
symphony.point area room device property floor pointId
symphony.point area room device pointId property floor
symphony.point pointId area device property
symphony.point area property floor pointId device room
symphony.point area pointId device property floor room
symphony.point area property pointId device
symphony.point pointId area property floor device room
symphony.point area pointId floor device property room
symphony.point area property pointId floor device room
symphony.point area room pointId floor device property
symphony.point pointId area device property
symphony.point area room pointId property floor device
symphony.point area room pointId floor device property
symphony.point area room device property pointId floor
symphony.point area pointId room property floor device
symphony.point area room device property floor pointId
symphony.point area pointId property room floor device
symphony.point pointId area property device
symphony.point area pointId device property floor room
symphony.point area device pointId property floor room
symphony.point area room pointId property floor device
symphony.point area pointId device property floor room
symphony.point area pointId device room property floor
symphony.point area room pointId device property floor
symphony.point area property room pointId floor device
symphony.point pointId area device property

太可惜了,因为来自 Points 的标签(这就是我从中构建 Tags 的标签)定义不明确。我仍然需要能够基于它们进行查询。我可以将它们全部添加到名称中,但随后“显示所有室内温度”之类的查询变得非常不愉快。

无论如何,我会尝试验证这是我问题的根本原因。

这一行可疑:

tags.addAll(pointMeter.getPoint().getLabels().entrySet().stream()
            .map (e -> Tag.of(e.getKey(),e.getValue()))
            .collect(Collectors.toSet()));

是否所有点都具有相同的标签?使用 Prometheus,所有具有相同名称的仪表都需要具有相同的标签名称(也称为标签)。带有标签名称的第一个点将成为默认点,所有其他点都将被拒绝。