InfluxDB-Java 对Point类时间属性的扩展

InfluxDB-Java 对Point类时间属性的扩展

在使用influxdb-java-2.7函数库写InfluxDB数据库的时候,发现调用

public void write(final String database, final String retentionPolicy, final Point point);

函数时,写入到数据库中的time字段不是数据库自动生成的时间,导致在分布式环境中的数据错位的现象,

例如:本该是A主机的数据早于B主机写入数据库,但实际却是B主机先于A主机写入了。

于是在代码中通过设置断点,惊奇的发现,确确实实是A主机的数据先于B主机到达,但为什么InfluxDB数据库中的数据却是B主机先于A主机到达?于是怀疑是A B两台主机的时间不同步导致的。查看分布式环境中的A B两台主机的时间,发现B主机系统时间比A主机尽快了2秒种,所以上述数据错位的现象得到了证实。

但由此引出另外一个问题,在用write函数写point的时候并没有设置time,本应该采用数据库自动生成的时间索引,但为什么采用的却是当前主机的本地时间,而不是数据库时间。

带着这个问题查看了influxdb-java-2.7的源码,发现如下的代码实现:

public Point build() {
      ...

      if (this.time != null) {
          point.setTime(this.time);
          point.setPrecision(this.precision);
      } else {
          point.setTime(System.currentTimeMillis());
          point.setPrecision(TimeUnit.MILLISECONDS);
      }
      ...
}

问题很明显,当我们没有设置时间的时候,自动为我们添加了当前的系统时间。

为了实现当前业务的数据同步问题,对Point类进行了扩展,如下是实现的原代码,命名为PointExt:

package org.influxdb.dto;

import java.text.NumberFormat;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;

import org.influxdb.InfluxDB;
import org.influxdb.InfluxDBFactory;

import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import com.google.common.collect.Ordering;
import com.google.common.escape.Escaper;
import com.google.common.escape.Escapers;

/**
 * Representation of a InfluxDB database PointExt.
 * 
 * @author stefan.majer [at] gmail.com
 * 
 */
public class PointExt {
    private static Builder Builder;
    private String measurement;
    private Map<String, String> tags;
    private Map<String, Object> fields;
    private boolean autoTime = false;
    private Long time;
    private TimeUnit precision = TimeUnit.NANOSECONDS;


    private static final Escaper FIELD_ESCAPER = Escapers.builder().addEscape('"', "\\\"").build();
    private static final Escaper KEY_ESCAPER = Escapers.builder().addEscape(' ', "\\ ").addEscape(',', "\\,").build();

    PointExt() {
        this.autoTime = false;
    }

    PointExt(boolean autoTime) {
        this.autoTime = autoTime;
    }

    /**
     * Create a new PointExt Build build to create a new PointExt in a fluent manner-
     *
     * @param measurement
     *            the name of the measurement.
     * @return the Builder to be able to add further Builder calls.
     */

    public static Builder measurement(final String measurement) {
        return new Builder(measurement);
    }

    /**
     * Builder for a new PointExt.
     *
     * @author stefan.majer [at] gmail.com
     *
     */
    public static final class Builder {
        private final String measurement;
        private final Map<String, String> tags = Maps.newTreeMap(Ordering.natural());
        private Long time;
        private TimeUnit precision = TimeUnit.NANOSECONDS;
        private final Map<String, Object> fields = Maps.newTreeMap(Ordering.natural());

        /**
         * @param measurement
         */
        Builder(final String measurement) {
            this.measurement = measurement;
        }

        /**
         * Add a tag to this point.
         *
         * @param tagName
         *            the tag name
         * @param value
         *            the tag value
         * @return the Builder instance.
         */
        public Builder tag(final String tagName, final String value) {
            this.tags.put(tagName, value);
            return this;
        }

        /**
         * Add a Map of tags to add to this point.
         *
         * @param tagsToAdd
         *            the Map of tags to add
         * @return the Builder instance.
         */
        public Builder tags(final Map<String, String> tagsToAdd) {
            this.tags.putAll(tagsToAdd);
            return this;
        }

        /**
         * Add a field to this point.
         *
         * @param field
         *            the field name
         * @param value
         *            the value of this field
         * @return the Builder instance.
         */
        public Builder field(final String field, final Object value) {
            this.fields.put(field, value);
            return this;
        }

        /**
         * Add a Map of fields to this point.
         *
         * @param fieldsToAdd
         *            the fields to add
         * @return the Builder instance.
         */
        public Builder fields(final Map<String, Object> fieldsToAdd) {
            this.fields.putAll(fieldsToAdd);
            return this;
        }

        /**
         * Add a time to this point
         *
         * @param precisionToSet
         * @param timeToSet
         * @return the Builder instance.
         */
        public Builder time(final long timeToSet, final TimeUnit precisionToSet) {
            Preconditions.checkNotNull(precisionToSet, "Precision must be not null!");
            this.time = timeToSet;
            this.precision = precisionToSet;
            return this;
        }

        /**
         * Create a new PointExt.
         *
         * @return the newly created PointExt.
         */
        public PointExt build() {
            Preconditions.checkArgument(!Strings.isNullOrEmpty(this.measurement), "PointExt name must not be null or empty.");
            Preconditions.checkArgument(this.fields.size() > 0, "PointExt must have at least one field specified.");
            PointExt point = new PointExt();
            point.setFields(this.fields);
            point.setMeasurement(this.measurement);
            if (this.time != 0) {
                point.setTime(this.time);
                point.setPrecision(this.precision);
            } else {
                point.setTime(System.currentTimeMillis());
                point.setPrecision(TimeUnit.MILLISECONDS);
            }
            point.setTags(this.tags);
            return point;
        }
    }

    PointExt autoTime() {
        this.autoTime = true;
        return this;
    }

    /**
     * @param measurement
     *            the measurement to set
     */
    void setMeasurement(final String measurement) {
        this.measurement = measurement;
    }

    /**
     * @param time
     *            the time to set
     */
    void setTime(final Long time) {
        this.time = time;
    }

    /**
     * @param tags
     *            the tags to set
     */
    void setTags(final Map<String, String> tags) {
        this.tags = tags;
    }

    /**
     * @return the tags
     */
    Map<String, String> getTags() {
        return this.tags;
    }

    /**
     * @param precision
     *            the precision to set
     */
    void setPrecision(final TimeUnit precision) {
        this.precision = precision;
    }

    /**
     * @param fields
     *            the fields to set
     */
    void setFields(final Map<String, Object> fields) {
        this.fields = fields;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("PointExt [name=");
        builder.append(this.measurement);
        builder.append(", autoTime=");
        builder.append(this.autoTime);
        builder.append(", time=");
        builder.append(this.time);
        builder.append(", tags=");
        builder.append(this.tags);
        builder.append(", precision=");
        builder.append(this.precision);
        builder.append(", fields=");
        builder.append(this.fields);
        builder.append("]");
        return builder.toString();
    }

    /**
     * calculate the lineprotocol entry for a single PointExt.
     * 
     * Documentation is WIP : https://github.com/influxdb/influxdb/pull/2997
     * 
     * https://github.com/influxdb/influxdb/blob/master/tsdb/README.md
     *
     * @return the String without newLine.
     */
    public String lineProtocol() {
        final StringBuilder sb = new StringBuilder();
        sb.append(KEY_ESCAPER.escape(this.measurement));
        sb.append(concatenatedTags());
        sb.append(concatenateFields());
        if(!this.autoTime) {
            sb.append(formatedTime());
        }
        return sb.toString();
    }

    private StringBuilder concatenatedTags() {
        final StringBuilder sb = new StringBuilder();
        for (Entry<String, String> tag : this.tags.entrySet()) {
            sb.append(",");
            sb.append(KEY_ESCAPER.escape(tag.getKey())).append("=").append(KEY_ESCAPER.escape(tag.getValue()));
        }
        sb.append(" ");
        return sb;
    }

    private StringBuilder concatenateFields() {
        final StringBuilder sb = new StringBuilder();
        final int fieldCount = this.fields.size();
        int loops = 0;

        NumberFormat numberFormat = NumberFormat.getInstance(Locale.ENGLISH);
        numberFormat.setMaximumFractionDigits(340);
        numberFormat.setGroupingUsed(false);
        numberFormat.setMinimumFractionDigits(1);

        for (Entry<String, Object> field : this.fields.entrySet()) {
            sb.append(KEY_ESCAPER.escape(field.getKey())).append("=");
            loops++;
            Object value = field.getValue();
            if (value instanceof String) {
                String stringValue = (String) value;
                sb.append("\"").append(FIELD_ESCAPER.escape(stringValue)).append("\"");
            } else if (value instanceof Number) {
                sb.append(numberFormat.format(value));
            } else {
                sb.append(value);
            }

            if (loops < fieldCount) {
                sb.append(",");
            }
        }
        return sb;
    }

    private StringBuilder formatedTime() {
        final StringBuilder sb = new StringBuilder();
        if (null == this.time) {
            this.time = System.nanoTime();
        }
        sb.append(" ").append(TimeUnit.NANOSECONDS.convert(this.time, this.precision));
        return sb;
    }


    public static void main(String[] args){
        Map<String, String> tags = new HashMap<>();
        Map<String, Object> fields = new HashMap<>();

        tags.put("host", "192.168.10.188");
        tags.put("region", "ShenZhen");
        tags.put("taskname", "luobenjihua");

        fields.put("length", 187.68);
        fields.put("width", 68.95);
        fields.put("height", 36.21);
        fields.put("desc", "this is a point about luobenjihua!");

        PointExt.Builder builder = new PointExt.Builder("usrAutoTime")
                .time(System.currentTimeMillis(), TimeUnit.MILLISECONDS)
                .tags(tags)
                .fields(fields);

        PointExt pointExt = builder.build();
        String noAutoTimeStr = pointExt.lineProtocol();
        System.out.println(String.format("noAutoTimeStr: %s", noAutoTimeStr));

        PointExt pointExt1 = builder.build().autoTime();
        String autoTimeStr = pointExt1.lineProtocol();
        System.out.println(String.format("autoTimeStr: %s", autoTimeStr));

        String ip = "127.0.0.1";
        InfluxDB influxDB = InfluxDBFactory.connect("http://" + ip + ":8086", "root", "root");
        influxDB.createDatabase("autotime");

        influxDB.write("autotime", "autogen", InfluxDB.ConsistencyLevel.ONE, pointExt.lineProtocol());
        influxDB.write("autotime", "autogen", InfluxDB.ConsistencyLevel.ONE, pointExt1.lineProtocol());

    }

}

[END]

猜你喜欢

转载自blog.csdn.net/qian_feifei/article/details/75453221