InfluxDB – Waar Java en time series databases samenkomen.

About

InfluxDB – Waar Java en time series databases samenkomen.

Traditioneel sloegen we alle data op in relationele databases. De laatste jaren maakt NoSQL echter een grote opmars. In dit artikel zullen we zien hoe een specifieke NoSQL variant gebruikt kan worden om op tijd gebaseerde data op te slaan.

Auteur: Johan Janssen

Heb je haast? Gebruik een time series database

Het komt regelmatig voor dat we data willen opslaan en vervolgens de waardes in een grafiek willen tonen. Denk bijvoorbeeld aan data van sensoren, zoals een temperatuursensor. In mijn geval om de oogst uit mijn tuin te verhogen en de luchtvochtigheid van mijn kelder te reguleren. Maar ook bijvoorbeeld hoe lang het duurt om een bepaalde methode, service of query uit te voeren. Het is allemaal data met een tijdstip en één of meerdere waardes. Dat zouden we in een traditionele SQL-database kunnen opslaan. We kunnen er ook voor kiezen om het in een database op te slaan die daar speciaal voor gemaakt is. Er bestaan verschillende zogenaamde time series databases [Link 1] waarvan we in dit artikel InfluxDB als voorbeeld nemen. In de rest van dit artikel gaan we sensordata uitlezen met een Raspberry Pi, opslaan in InfluxDB en vervolgens tonen in een dashboard.

Set-up

Afbeelding 1 geeft de opstelling weer. De basis is een Raspberry Pi, in dit geval een 3 B+ met een quadcore processor. Andere modellen zullen naar verwachting ook prima werken. Op de Raspberry Pi draait een Spring Boot applicatie. Dit is slechts een voorbeeld. Je kan een vergelijkbare opstelling maken zonder Spring (Boot). Een link [Link 5] naar de code van de voorbeeldapplicatie is op het eind van het artikel te vinden.

De applicatie haalt de sensordata op, zoals bijvoorbeeld de temperatuur en de luchtvochtigheid van een aantal sensoren:

–        BME280 met kabels verbonden aan de GPIO pinnen van de Raspberry Pi. Meet de temperatuur, luchtvochtigheid en druk.

–        Raspberry Pi sensor die de CPU temperatuur meet.

–        Xiaomi hygrometer verbonden via bluetooth. Meet de temperatuur en luchtvochtigheid.

–        Xiaomi flora plantensensor verbonden via bluetooth. Meet de temperatuur, vochtigheid, licht en hoe goed de grond is.

Deze opstelling heb ik gebruikt om een aantal dingen uit te proberen. Je kunt er natuurlijk ook voor kiezen om maar één sensor te gebruiken. De sensors worden om de zoveel seconden uitgelezen en vervolgens wordt de data naar de InfluxDB database gestuurd. Uiteindelijk wordt Chronograf gebruikt als dashboard om de data uit InfluxDB te tonen. Zowel InfluxDB als Chronograf draaien in Docker containers op de Raspberry Pi. Het is natuurlijk ook mogelijk om de containers op een andere machine te draaien.

Afbeelding 1: Opstelling

 

InfluxDB

De InfluxDB Docker container is één van de gemakkelijkste manieren om met InfluxDB te beginnen. Meer informatie daarover vind je op de Docker Hub [Link 2]. Nadat de InfluxDB container draait, kan je de InfluxDB client starten met het volgende commando: docker exec -it influxdb /usr/bin/influx. Dit Docker commando zorgt ervoor dat je in de Docker container een commandline interface (CLI) voor InfluxDB krijgt.

Vervolgens kan je een database aanmaken, bijvoorbeeld met het commando CREATE DATABASE SensorData. Met het commando SHOW DATABASES kun je de beschikbare databases zien. Daarna gebruiken we USE SensorData om onze database te kunnen gebruiken. Nu kunnen we eindelijk onze queries uitvoeren, bijvoorbeeld om de temperatuur op te vragen van de BME280 sensor: SELECT * FROM ambient_temperature WHERE sensor = ‘BME280SensorData’.

time Host Sensor value
1546941124275000000 192.168.2.102 BME280SensorData 22.3
Tabel 1: InfluxDB Query resultaat

 

Dit geeft de data uit Tabel 1 terug. Hier kunnen we dus zien dat de BME280Sensor die verbonden was aan de Raspberry Pi met dat specifieke IP-adres een temperatuur gaf van 22,3 graden. Dit alles gebeurde op een tijdstip dat in ieder geval voor mij lastig te interpreteren is. Gebruik het volgende commando in de InfluxDB CLI om de tijdstippen leesbaarder te maken: precision rfc3339. Als de query opnieuw uitgevoerd wordt dan ziet het tijdstip er als volgt uit: 2019-01-08T09:52:04.275Z.

Het is ook mogelijk om zelf data op te slaan via de InfluxDB CLI. Bijvoorbeeld met de volgende query: INSERT ambient_temperature,host=192.168.40.72,sensor=BME280SensorData value=21. Maar aangezien dit het Java Magazine is, gaan we dat natuurlijk vanuit Java doen.

Data versturen vanuit Java naar InfluxDB

Vanuit Java is het mogelijk om op verschillende manieren de sensordata naar een InfluxDB database te sturen, namelijk:

  • InfluxDB Java client;
  • Spring Data InfluxDB;
  • REST.

Deze verschillende manieren worden in dit artikel één voor één behandeld. Hetzelfde geldt ook voor andere programmeertalen. Als er REST calls gedaan kunnen worden, dan kan de data opgeslagen worden. Daarnaast is er voor veel talen ook een InfluxDB client beschikbaar.

Let wel op hoe je de data opslaat in InfluxDB, want dat heeft invloed op de performance van je queries. Data kan als ‘Tag’ of als ‘Field’ opgeslagen worden. Het grote verschil is dat Tags worden geïndexeerd en velden niet. Tags lijken op geïndexeerde kolommen in SQL. Zorg er dus voor dat je tags goed gebruikt.

InfluxDB Java client

We maken gebruik van de influxdb-java dependency. Deze dependency kan bijvoorbeeld in de POM worden opgenomen zoals in Listing 1.

<dependency>

   <groupId>org.influxdb</groupId>

   <artifactId>influxdb-java</artifactId>

</dependency>
Listing 1

 

Als je ermee aan de slag gaat, dan is het handig om even de -zeer duidelijke- voorbeelden op de website [Link 3] van het project te bekijken. Een connectie maken naar de InfluxDB database is eenvoudig te realiseren, zoals te zien is in Listing 2. Het aanmaken en verwijderen van databases wordt ook door middel van queries gedaan. Aangezien we Spring gebruiken, kan deze configuratie ook netjes worden weggewerkt door bijvoorbeeld de @Bean annotatie te gebruiken.

InfluxDB influxDB = InfluxDBFactory.connect("http://localhost:8086", "root", "root");

Query createDatabaseQuery = new Query("CREATE DATABASE SensorData", " SensorData");

influxDB.query(createDatabaseQuery);

influxDB.setDatabase("SensorData");
Listing 2

 

De InfluxDB Java client gebruikt de Point class om data op te slaan, zoals in Listing 3. Allereerst wordt opgenomen wat de naam van de meting is, in dit geval ‘ambient_temperature’. Daarnaast geven we het tijdstip van de meting op. Als laatste kunnen we alle tags en fields opgeven die we willen gebruiken.

Point temperaturePoint = Point.measurement("ambient_temperature")

      .time(System.currentTimeMillis(), TimeUnit.MILLISECONDS)

      .tag("host", hostIpAddress)

      .tag("sensor", sensorName)

      .addField("value", ambientTemperature)

      .build();
Listing 3

 

Vervolgens kunnen de datapunten verstuurd worden naar InfluxDB. De class InfluxDB bevat daar een aantal methoden voor, zoals te zien is in Listing 4. Als de database al goed geconfigureerd is, dan kunnen we simpelweg de write methode met één argument, namelijk Point, gebruiken. Op deze manier kunnen we één voor één alle Points, oftewel sensordata, versturen.

public void write(final Point point);

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

 

Het versturen van de data naar InfluxDB kan nog meer geoptimaliseerd worden door de Points in batches te versturen naar InfluxDB. Daarvoor dient batch aangezet en geconfigureerd te worden, bijvoorbeeld zoals in Listing 5. Deze code zorgt ervoor dat de Points per 10 verstuurd worden minimaal iedere 1.000 milliseconden.

influxDB.enableBatch(BatchOptions.DEFAULTS.actions(10).flushDuration(1000));
Listing 5

 

Spring Data InfluxDB

Aangezien we Spring gebruiken, kunnen we het onszelf nog iets gemakkelijker maken door gebruik te maken van Spring Data InfluxDB [Link 4]. Deze maakt onder water gebruik van het influxdb-java project dat we hiervoor zagen. Het biedt echter weer een mooie abstractie laag, zoals we dat gewend zijn bij Spring, in de vorm van een InfluxDBTemplate. Allereerst configureren we de dependency, zoals in Listing 6.

<dependency>

   <groupId>com.github.miwurster</groupId>

   <artifactId>spring-data-influxdb</artifactId>

</dependency>
Listing 6

 

Daarna configureren we de database in Spring. Dat kan bijvoorbeeld door een application.properties file aan te maken met de benodigde velden zoals in Listing 7. Dankzij Spring en deze configuratie hoeven we de connectie naar InfluxDB niet meer zelf te maken.

spring.influxdb.url=http://localhost:8086

spring.influxdb.username=root

spring.influxdb.password=root

spring.influxdb.database=SensorData

spring.influxdb.retention-policy=autogen
Listing 7

 

We kunnen namelijk meteen gebruik maken van de InfluxDBTemplate, zoals in Listing 8. Deze template bevat write methodes om één Point of een lijst van Points te versturen naar InfluxDB.

@Autowired

private InfluxDBTemplate<Point> influxDBTemplate;
Listing 8

 

REST

De vorige twee manieren werken prima. Wellicht wil of kan je die oplossingen niet gebruiken. Bijvoorbeeld, omdat het in je project niet is toegestaan om allerlei libraries op te nemen. In dat geval is het altijd nog mogelijk om met behulp van REST data naar InfluxDB te sturen. In Listing 9 is te zien hoe de sensordata met behulp van de Spring RestTemplate verstuurd wordt. Natuurlijk is het ook mogelijk om de data met een andere REST client te versturen.

String restURL = "http://localhost:8086/write?db=SensorData";




String measurement = "ambient_temperature,host=" + hostIpAddress + ",sensor=" + sensorName + " value=" + sensorData.getTemperature();




restTemplate.postForLocation(restURL, measurement);
Listing 9

 

Data tonen in een dashboard

Meerdere dashboardsystemen bieden ondersteuning voor InfluxDB. Grafana is wellicht één van de bekendere. Ik heb echter Chronograf gebruikt, aangezien het onderdeel is van dezelfde stack als InfluxDB. Hiervoor open je het Chronograf dashboard dat draait op poort 8888. Vervolgens voeg je een connectie toe, zoals in afbeelding 2. De standaard username en wachtwoord van InfluxDB is ‘root’.

 

Nadat je een connectie hebt, kan je een nieuw dashboard maken waar de data op getoond gaat worden. Op het dashboard kan je meerdere cellen met daarin data maken. Voor iedere cell kunnen we een query opgeven om de juiste data op te halen, zoals in afbeelding 3. Behalve de query kan ook de zogenaamde ‘visualization’ aangepast worden. Standaard wordt de data in een grafiek getoond, maar het is ook mogelijk om staafdiagrammen, tabellen en andere visualiseringen te gebruiken. Uiteindelijk zal op het dashboard alleen de visualisatie getoond worden zonder alle configuratie.

 

 

Data ophalen uit InfluxDB vanuit Java

Wellicht is het genoeg om de data in een dashboard te tonen, maar het kan ook zo zijn dat je zelf nog iets met de data wilt doen. Het ophalen van data uit InfluxDB kan op dezelfde manieren als het versturen: met de InfluxDB Java client, met Spring Data InfluxDB of met REST.

In Listing 10 zien we een voorbeeld query, waarvan de syntax erg lijkt op SQL.

final Query q = new Query("SELECT * FROM ambient_temperature GROUP BY host, sensor", influxDBTemplate.getDatabase());

QueryResult queryResult = influxDBTemplate.query(q);
Listing 10

 

De inhoud van QueryResult ziet eruit als in Listing 11.

QueryResult [results=[Result [series=[Series [name=ambient_temperature, tags={host=192.168.40.72, sensor=BME280SensorData}, columns=[time, value], values=[[2019-01-17T09:00:37.694Z, 23.0]]]], error=null]], error=null]
Listing 11

 

Vervolgens kunnen we uit de QueryResult de data halen die we willen testen, zoals in Listing 12 te zien is.

QueryResult.Series series = queryResult.getResults().get(0).getSeries().get(0);




assertEquals("ambient_temperature", series.getName());

assertEquals("192.168.40.72", series.getTags().get("host"));

assertEquals("BME280SensorData", series.getTags().get("sensor"));




assertEquals("time", series.getColumns().get(0));

assertEquals("value", series.getColumns().get(1));




assertEquals(23.0, series.getValues().get(0).get(1));
Listing 12

 

Het werken met kolommen en indexen binnen het QueryResult is vrij foutgevoelig en irritant. Gelukkig bestaat een makkelijkere manier. We kunnen de data namelijk met een InfluxDBResultMapper mappen op een Java class. Daarvoor maken we eerst een soort containerclass waarin we de tags en andere waarden specificeren Listing 13.

@Measurement(name="ambient_temperature")

public class AmbientTemperature {

    @Column(name = "host", tag = true)

    private String host;




    @Column(name = "sensor", tag = true)

    private String sensor;




    @Column(name="value")

    private Double value;
Listing 13

 

Vervolgens gebruiken we deze nieuwe class om het resultaat van de query op te slaan. Tenslotte kunnen we nu de testen herschrijven, zoals in Listing 14. Dit zorgt ervoor dat de testen een stuk beter leesbaar worden.

final Query q = new Query("SELECT * FROM ambient_temperature GROUP BY host, sensor", influxDBTemplate.getDatabase());

QueryResult queryResult = influxDBTemplate.query(q);

InfluxDBResultMapper resultMapper = new InfluxDBResultMapper();

List<AmbientTemperature> tempList = resultMapper.toPOJO(queryResult, AmbientTemperature.class);

AmbientTemperature ambientTemperature = tempList.get(0);




assertEquals("192.168.40.72", ambientTemperature.getHost());

assertEquals("BME280SensorData", ambientTemperature.getSensor());

assertEquals(Double.valueOf(23.0), ambientTemperature.getValue());
Listing 14

 

Conclusie

InfluxDB werkt erg gemakkelijk samen met Java: je slaat simpelweg data op met een tijdstip. Vervolgens kan de data in een dashboard getoond worden. Het is ook mogelijk om de data met queries uit InfluxDB te halen. Vanuit Java zijn verschillende mogelijkheden om te communiceren met InfluxDB. REST is redelijk technologie-onafhankelijk, maar iets lastiger in het gebruik. De InfluxDB Java client en Spring Data InfluxDB werken beide erg gemakkelijk. Ik kan het zeker aanraden om eens een time series database te proberen!

Share
May 2024
June 2024
No event found!

Related Topics