Kapitel 4. Client-Konnektivität

Diese Arbeit wurde mithilfe von KI übersetzt. Wir freuen uns über dein Feedback und deine Kommentare: translation-feedback@oreilly.com

Ein Presto-Client ist ein Prozess, der Presto abfragt und die Abfrageergebnisse für eine Reihe von Zwecken anzeigt, z. B. für Datenanalysen, Ad-hoc-Abfragen und vieles mehr. Presto bietet eine Vielzahl von Clients, die in verschiedenen Programmiersprachen geschrieben sind, darunter REST API, R, Python, JDBC, Node.js und ODBC.

Dieses Kapitel ist in zwei Teile gegliedert. Im ersten Teil erfährst du, wie du einen Presto-Client einrichtest. Obwohl du einen Presto-Client als separaten Prozess auf einem bestehenden Clusterknoten einsetzen kannst, wirst du in diesem Kapitel den Client auf einem anderen Knoten implementieren, um eine externe Anwendung zu simulieren, die auf den Cluster zugreift. Wir werden uns auf die Implementierung eines Clients in der REST-API, Python, R, JDBC, Node.js und ODBC konzentrieren. Du kannst das beschriebene Verfahren leicht auf die anderen von Presto unterstützten Programmiersprachen ausweiten.

Im zweiten Teil wirst du einen praktischen Webservice in Python implementieren, der Presto abfragt und die Ergebnisse in einem einfachen Dashboard anzeigt. Das Ziel dieses Beispiels ist es, die Möglichkeiten eines Presto-Clients zu veranschaulichen.

Einrichten der Umgebung

Um einen Presto-Client als Knoten des Presto-Clusters hinzuzufügen, musst du zunächst die Umgebung einrichten. Setze die folgenden drei Komponenten ein, die bereits in Kapitel 3 implementiert wurden:

  • Presto Kunde

  • Docker-Image, das deinen Presto-Client enthält

  • Kubernetes-Knoten, auf dem das Docker-Image im Presto-Cluster läuft

Der Code, der in diesem Abschnitt verwendet wird, ist im Verzeichnis 04 im Repository des Buches verfügbar, das die Konfigurationsdateien für alle beschriebenen Presto-Clients enthält.

Presto Kunde

Der Presto-Client verbindet sich mit Presto, führt Abfragen aus und zeigt die Ergebnisse an. Du kannst deinen Presto-Client in deiner bevorzugten Sprache schreiben. Speichere den Code in einem Verzeichnis, das als separates Volume in den Kubernetes-Knoten eingebunden wird. Optional kannst du auch eine Webanwendung oder einen Dienst schreiben, der den Zugriff über HTTP erlaubt. Wir werden später in diesem Kapitel sehen, wie du einen Client-Webdienst bereitstellst. Alternativ kannst du auch das Terminal verwenden, um auf deinen Presto-Client zuzugreifen.

Docker Image

Das Docker-Image enthält die Umgebung, die du brauchst, um deinen Presto-Client auszuführen. Verwende die folgende Vorlage für das Dockerfile:

FROM <basic_image>
RUN apt-get update && \
<install required libraries>

Um zum Beispiel das Image für den R-Client zu erstellen, schreibst du das folgende Dockerfile:

FROM r-base
RUN apt-get update && \
apt install -y build-essential libcurl4-gnutls-dev libxml2-dev libssl-dev
RUN R -e "install.packages('RPresto')"

Um dein Docker-Image zu erstellen, führe den folgenden Befehl in einem Terminal aus:

docker build -t client-presto .

Kubernetes-Knoten

Um einen Kubernetes-Knoten im Presto-Cluster einzusetzen, schreibst du eine .yaml-Konfigurationsdatei, die einfach das Docker-Image umhüllt:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-client
  labels:
    app: my-client
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-client
  template:
    metadata:
      labels:
        app: my-client
    spec:
      containers:
      - name: my-client
        image:  client-presto:latest
        imagePullPolicy: Never
        command: [ "sleep" ]
        args: [ "infinity" ]

Der Befehl sleep infinity sorgt dafür, dass Kubernetes deinen Client am Leben erhält. Wenn dein Client einen Dienst ausführt, kannst du auch den Konfigurationscode ändern, um den Dienst bereitzustellen.

Um benutzerdefinierte Skripte in deinem Docker-Container auszuführen, mounte ein lokales Volume:

spec:
 containers:
 - name: client
   # add details based on the client type
   volumeMounts:
    - name: scripts
      mountPath: "/path/in/the/container"

 volumes:
  - name: scripts
    hostPath:
      path: "/path/to/scripts/in/the/host"

Setze den Client ein, indem du den folgenden Befehl ausführst:

kubectl apply -f client.yaml --namespace presto

Sobald du deinen Client bereitgestellt hast, melde dich an und führe die Skripte im gemounteten Verzeichnis aus.

Montage-Warnung

Verwende beim Schreiben deines Codes die Option volumeMount. In einer Produktionsumgebung solltest du die implementierte App direkt in das Docker-Image einbinden, anstatt sie in einem separaten Verzeichnis in deiner Kubernetes-.yaml-Datei zu speichern.

Konnektivität mit Presto

Dieser Abschnitt gibt einen Überblick darüber, wie du die vorhandenen Client-Bibliotheken nutzt, um eine Verbindung zum Presto-Cluster herzustellen. Wir werden die verschiedenen von Presto unterstützten Programmiersprachen verwenden. Lade das laufende Beispiel in deiner bevorzugten Sprache aus dem GitHub-Repository des Buches herunter, und zwar unter dem Verzeichnis 04/<Sprache>_client. Die Sprachen, die dich nicht interessieren, kannst du getrost auslassen.

REST-API

Der Presto-Koordinator enthält einen HTTP-Server, der einen REST-API-Endpunkt unterstützt. Die gesamte Kommunikation zwischen dem Client und dem Presto-Koordinator erfolgt über das HTTP-Protokoll. Um einen Presto-Client über eine REST-API auszuführen, gibst du mindestens die URL zum Presto-Koordinator, die Abfrage und eine minimale Kopfzeile an, die den Katalog und das abzufragende Schema enthält:

curl -d "SELECT * FROM customer LIMIT 5" \
 --request POST \
 -s "presto-coordinator:8080/<endpoint>" \
 -H "X-Presto-User: client" \
 -H "X-Presto-Catalog: tpch" \
 -H "X-Presto-Schema: sf1"

Gib den Benutzer mit -H "X-Presto-User: client" an. Standardmäßig akzeptiert Presto jeden Benutzer, also schreibe einen beliebigen Namen. In Kapitel 7 erfährst du, wie du bestimmte Benutzer in Presto konfigurieren kannst.

Wenn der Aufruf der REST-API erfolgreich ist, gibt Presto ein JSON-Objekt zurück, in dem der nextUri -Schlüssel festgelegt ist. Rufe Presto erneut auf und verwende den Wert, der dem Schlüssel nextUri entspricht, als URL. Ein Beispiel dafür siehst du später in diesem Abschnitt.

Die REST-API bietet dir viele Endpunkte, wie zum Beispiel die folgenden:

/v1/node

Um Informationen über einen Knoten zu erhalten.

/v1/statement

Um Abfragen durchzuführen.

/v1/query

Um Metriken zu den letzten Abfragen zu erhalten. Optional kannst du die Abfrage-ID angeben, um Informationen über eine bestimmte Abfrage zu erhalten.

/v1/thread

Um Informationen über einen Thread zu erhalten.

/v1/task

Um Informationen über eine Aufgabe zu erhalten.

Iteriert den vorherigen Aufruf an Presto, bis der Schlüssel nextUri im JSON-Objekt der Antwort fehlt. Wenn der letzte Aufruf erfolgreich war, enthält das zurückgegebene Objekt die Abfrageergebnisse als Wert des data Schlüssels.

curl -s "URL contained in nextUri"

Der folgende Code zeigt ein vollständiges Beispiel für einen Aufruf der REST-API in Python:

  1. Führe den ersten Aufruf der REST-API aus:

    import requests
    import time
    
    url = "http://presto-coordinator:8080/v1/statement"
    
    headers = {
        "X-Presto-User" : "client", \
        "X-Presto-Catalog": "tpch", \
        "X-Presto-Schema": "sf1"
        }
    
    sql = "SELECT * FROM customer LIMIT 5"
    
    resp = requests.post(url, headers=headers, data=sql)
    json_resp = resp.json()
  2. Iteriere über den Schlüssel nextUri, bis er nicht mehr vorhanden ist. Verwende time.sleep(0.5), um auf Daten zu warten. Erhöhe diesen Wert, wenn dein Client keine Daten erhält.

    while 'nextUri' in json_resp:
     time.sleep(0.5)
     new_url = json_resp['nextUri']
     resp = requests.get(new_url)
     json_resp = resp.json()
  3. Erhalte die endgültigen Daten:

    data = json_resp['data']
    for i in range(0, len(data)):
     print(data[i])

Die Presto REST API bietet viele zusätzliche Parameter. Lies die Presto-Dokumentation für weitere Details.

Python

Installiere die presto-python-client Bibliothek. Führe in deinem Dockerfile den folgendenBefehl aus:

pip install presto-python-client

Verbinde dich dann wie folgt mit Presto:

import prestodb
conn=prestodb.dbapi.connect(
    host='presto-coordinator',
    port=8080,
    user='client',
    catalog='tpch',
    schema='sf1',
)

Um eine Abfrage auszuführen, rufe den Cursor ab:

cur = conn.cursor()
cur.execute('SELECT * FROM customer LIMIT 5')
rows = cur.fetchall()

R

Installiere die Bibliothek RPresto.Führe in deinem Dockerfile den folgenden Befehl aus:

RUN R -e "install.packages('RPresto')"

Um Presto in R abzufragen, importiere zunächst die benötigten Bibliotheken:

library(RPresto)
library(DBI)

Verbinde dich dann mit dem Presto-Server:

con <- DBI::dbConnect(
  drv = RPresto::Presto(),
  host = "presto-coordinator",
  port = 8080,
  user = "r-client",
  catalog = "tpch",
  schema = "sf1"
)

Um eine Abfrage durchzuführen, verwende die Funktion DBI::dbGetQuery():

DBI::dbGetQuery(con, "SELECT * FROM customer LIMIT 5")

JDBC

Verwende den Presto JDBC-Treiber com.facebook.presto.jdbc.PrestoDriver. Wenn du Maven verwendest, füge die folgenden Abhängigkeiten zu deiner pom.xml-Datei hinzu:

<dependencies>
 <dependency>
  <groupId>com.facebook.presto</groupId>
  <artifactId>presto-jdbc</artifactId>
  <version>0.276</version>
 </dependency>
</dependencies>

Wenn du kein Maven verwendest, lade die Datei presto-jdbc-0.276.jar von der Presto-Website herunter und füge sie dem Klassenpfad deiner Java-Anwendung hinzu.Ersetze die Presto-Version durch die neueste Version von Presto (0.276 zum Zeitpunkt der Erstellung dieses Kapitels).

Definiere in deiner Haupt-Java-Anwendung die URL zum Presto-Server und verbinde dich mit Presto über eine Connection:

import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.SQLException;

String url = "jdbc:presto://presto-coordinator:8080/tpch/sf1";

try {
 Class.forName("com.facebook.presto.jdbc.PrestoDriver");

 Connection connection =
          DriverManager.getConnection(url, "test", null);
}
catch(SQLException e){
 e.printStackTrace();
}
catch(ClassNotFoundException e){
 e.printStackTrace();
}

Gib in der URL-Zeichenkette den Katalog (tpch) und das Schema (sf1) an. Dann führe deine Abfragen aus:

String sql = "SELECT * FROM customer LIMIT 5";

try{
 Statement statement = connection.createStatement();
 ResultSet rs = statement.executeQuery(sql);
 while (rs.next()) {
     System.out.println(rs.getString(1));
 }
}
catch(SQLException e){
 e.printStackTrace();
}

Das Beispiel gibt die erste Spalte für jedes Ergebnis in der Ergebnismenge der Abfrage aus.

Node.js

Installiere die presto-client Bibliothek:

npm install -g presto-client

Importiere in deiner Haupt-JavaScript-Anwendung die Bibliothek und erstelle ein neues Client() Objekt:

var presto = require('presto-client');
var client = new presto.Client({
 user: 'myuser',
 'host': 'http://presto-coordinator',
 port: '8080'
});

Führe dann eine beliebige Abfrage aus:

client.execute({
  query:   'SELECT * FROM customer LIMIT 5',
  catalog: 'tpch',
  schema:  'sf1',
  source:  'nodejs-client',
  data:    function(error, data, columns, stats){ console.log(data); },
  success: function(error, stats){console.log(stats);},
  error:   function(error){console.log(error);}
});

ODBC

Open Database Connectivity (ODBC) ist eine Standardmethode für den Datenbankzugriff, mit der Anwendungen auf Daten in einer Vielzahl von Datenbankformaten zugreifen können. Viele Datenbankhersteller wie Microsoft, Oracle und IBM bieten proprietäre ODBC-Treiber an, um eine Verbindung zu jeder ODBC-kompatiblen Datenbank herzustellen, unabhängig von der zugrunde liegenden Struktur oder dem Format der Datenbank.

Um einen Presto ODBC-Client zu implementieren, musst du zunächst einen ODBC-Treiber installieren. In diesem Buch verwenden wir CData.

Damit der Treiber funktioniert, musst du zuerst die Bibliotheken unixodbc, unixodbc-dev und tdsodbc installieren. Unter Ubuntu führst du zum Beispiel den folgenden Befehl aus:

apt-get install -y unixodbc unixodbc-dev tdsodbc

Als nächstes lädst du den CData-Treiber für Presto (Unix-Version) herunter, installierst das Paket und aktivierst die Lizenz:

dpkg -i PrestoODBCDriverforUnix.deb
cd /opt/cdata/cdata-odbc-driver-for-presto/bin/
./install-license.sh

Installiere dann die Client-Bibliothek in deiner bevorzugten Sprache, um über den ODBC-Treiber auf Presto zuzugreifen. In Python installierst du zum Beispiel pyodbc:

pip install pyodbc

Verbinde in deiner ODBC-Client-Anwendung mit der Presto-Datenbank:

conn=pyodbc.connect("DRIVER=CData ODBC Driver for Presto;\
server=presto-coordinator;\
Port=8080;user=client;\
catalog=tpch;schema=sf1")

Führe dann eine beliebige Abfrage aus:

cur = conn.cursor()
cur.execute('SELECT * FROM customer LIMIT 5')
rows = cur.fetchall()
print(rows)

Andere Presto Client-Bibliotheken

Presto unterstützt viele andere Sprachen, darunter C, Go, PHP und Ruby. Die Vorgehensweise zur Erstellung deines Clients in einer dieser Sprachen ist im Wesentlichen dieselbe.Im Presto GitHub-Profil kannst du die Vorlage für einen bestimmten Client herunterladen. Verwende zum Beispiel den Go-Client aus dem Go-Repository des Presto GitHub-Profils.

Erstellung eines Kunden-Dashboards in Python

In diesem Abschnitt wird ein praktisches Beispiel dafür beschrieben, wie man ein einfaches Dashboard für Presto in Python von Grund auf implementiert. Das Dashboard ist eine Webanwendung, die sich mit Presto verbindet, eine Beispielabfrage ausführt und die Ergebnisse der Abfrage in zwei separaten Diagrammen anzeigt.

Der in diesem Abschnitt beschriebene Code ist im GitHub-Repository des Buches unter dem Verzeichnis 04/client-app verfügbar.

Den Client einrichten

Das Dashboard läuft als Kubernetes-Knoten, der einen HTTP-Dienst für den Zugriff auf das Dashboard bereitstellt. Wir werden das Dashboard mit den folgenden Python-Bibliotheken implementieren:

Streamlit

Verwandelt Python-Skripte sehr schnell in Webanwendungen

Altair

Eine Python-Bibliothek für die Visualisierung von Daten

Pandas

Eine beliebte Python-Bibliothek für die Bearbeitung von Datensätzen

Um den Client zu konfigurieren, führst du die folgenden Schritte durch:

  1. Schreibe das Dockerfile, das den Code der App in das Docker-Image kopiert, die erforderlichen Bibliotheken installiert, den Streamlit-Listening-Port freigibt und die App ausführt:

    FROM python:3.8.6
    WORKDIR /app
    
    COPY app .
    
    RUN pip install --upgrade pip
    RUN pip install presto-python-client
    RUN pip install streamlit altair pandas
    
    EXPOSE 8501
    
    CMD ["./run.sh"]

    Das run.sh-Skript startet einfach den Streamlit-Server:

    #!/bin/bash
    streamlit run app.py --browser.serverAddress 0.0.0.0
  2. Erstelle das Docker-Image, indem du den folgenden Befehl in dem Verzeichnis ausführst, in dem sich die Dockerdatei befindet:

    docker build -t client-app .
  3. Binde das client-app Docker-Image in einen Kubernetes-Knoten ein. Verwende dazu das Verfahren , das du bereits für den Python-Client befolgt hast. Hänge kein externes Volume ein, da die Anwendung bereits über das Docker-Image verfügbar ist. Außerdem musst du den Port des Webservers für den externen Zugriff freigeben.

    spec:
     containers:
     - name: client-app
       image:  client-app:latest
       ...
       ports:
         - containerPort: 8501
  4. Erstelle einen Dienst für die Webanwendung:

    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: client-service
      labels:
        app: client-service
    spec:
      ports:
        - port: 8501
          protocol: TCP
          targetPort: 8501
      type: LoadBalancer
      selector:
        app: client-app

    Der Dienst stellt denselben Port wie der ursprüngliche Webserver zur Verfügung. Außerdem verwendet er eine LoadBalancer, um den Dienst außerhalb des Kubernetes-Clusters verfügbar zu machen.

  5. Setze den Client-App-Knoten im presto Cluster ein:

    kubectl apply -f client-app.yaml --namespace presto
  6. Schließlich öffnest du deinen Browser und zeigst auf http://localhost:8501, um auf die Webanwendung zuzugreifen.

Aufbau des Dashboards

Das Client-Dashboard ist ein Webserver, der eine Verbindung zu Presto herstellt, eine Beispielabfrage durchführt und die Ergebnisse als Diagramme anzeigt. Das Ziel dieser App ist es, die Möglichkeiten des Aufbaus einer Client-App zu demonstrieren und nicht ein komplettes Beispiel für Datenanalyse zu erstellen.

Der Code des Client-Dashboards ist unter 04/client-app/app im GitHub-Repository für das Buch verfügbar. Der Code ist in einem einzigen Skript namens app.py organisiert, das den Webserver als Streamlit-Webserver ausführt. Im Einzelnen führt die App die folgenden Vorgänge aus:

  • Verbindung mit Presto herstellen und abfragen

  • Aufbereitung der Ergebnisse der Abfrage

  • Aufbau der ersten Grafik

  • Aufbau des zweiten Diagramms

Verbindung mit Presto herstellen und abfragen

Zuerst importiert die App alle benötigten Bibliotheken:

import prestodb
import altair as alt
import pandas as pd
import streamlit as st

Dann verbindet sich die App über die Bibliothek prestodb mit Presto. Verwende den in "Python" beschriebenen Code, um die Verbindung herzustellen.

Ressourcen Warnung

Wenn dein lokaler Rechner nicht über genügend Ressourcen verfügt, um einen großen Datensatz zu verwalten, verwende das Schema tiny aus dem TPC-H-Katalog, wenn du die Verbindungsparameter zu Presto angibst. Das tiny Schema enthält etwa 10.000 Beispieldaten.

Die App führt eine Abfrage aus, die die Anzahl der versendeten Artikel nach Datum aus dem TPC-H-Katalog auflistet und sie nach dem ältesten Versanddatum ordnet:

query = """SELECT
     count(*) as nitems,
     shipdate
 FROM
     lineitem
 GROUP BY shipdate
 ORDER BY shipdate ASC
        """

Aufbereitung der Ergebnisse der Abfrage

Die App speichert das Ergebnis der Abfrage in einem Pandas DataFrame:

cur.execute(query)
df = pd.DataFrame(cur.fetchall(), columns=['nitems', 'shipdate'])

Um die endgültigen Diagramme zu erstellen, extrahiert die App einige Informationen von shipdate:

df['shipdate'] = pd.to_datetime(df['shipdate'])
df['dayofweek'] = df['shipdate'].dt.day_name()
df['year'] = df['shipdate'].dt.year
df['month'] = df['shipdate'].dt.month_name()
df['weekofyear'] = df['shipdate'].dt.isocalendar().week

Zu den extrahierten Informationen gehören der Wochentag (von Sonntag bis Samstag), das Jahr, der Monat und die Woche des Jahres (eine fortlaufende Zahl von 1 bis 53).

Aufbau der ersten Grafik

Die erste Grafik konzentriert sich auf das Jahr 1992 und zeigt eine Linie für jede Woche des Jahres, bezogen auf die Anzahl der versendeten Artikel, sortiert nach Wochentagen:

df_1992 = df[df['year'] == 1992 ]

Das Diagramm besteht aus zwei Teilen. Der erste Teil des Diagramms zeigt eine Linie für jede Woche des Jahres:

days_of_weeks = ['Sunday', ... ,'Saturday']
bar = alt.Chart(df_1992).mark_line().encode(
    x = alt.X('dayofweek:O',
      sort=days_of_weeks,title=''
    ),
    y = alt.Y('nitems:Q', title='nitems'),
    color = alt.Color('weekofyear:N',
      scale=alt.Scale(range=['#C8C8C8']),
      legend=None
    )
)

Der zweite Teil des Diagramms zeigt den Durchschnittswert der Anzahl der versendeten Artikel über alle Wochen hinweg:

mean = alt.Chart(df_1992).mark_line(color='black').encode(
    x = alt.X('dayofweek:O',
      sort=days_of_week,
      title=''
    ),
    y = alt.Y('weekly_count:Q', title=''),
).transform_aggregate(
    weekly_count = 'mean(nitems)',
    groupby=['dayofweek']
)

Schließlich kombiniert die App die beiden Teile, um das endgültige Diagramm zu erstellen:

chart1 = (bar + mean).properties(
    width=300,
    height=300,
    title='Number of shipped items in 1992')
)
st.altair_chart(chart1)

Wir verwenden die Funktion st.altair_chart(), um das Diagramm zu erstellen. Abbildung 4-1 zeigt den resultierenden Graphen.

Number of shipped items in 1992 organized by week
Abbildung 4-1. Anzahl der verschickten Sendungen im Jahr 1992, geordnet nach Wochen

Aufbau des zweiten Diagramms

Das zweite Diagramm zeigt eine Kalender-Heatmap, mit Jahren auf der x-Achse und Monaten auf der y-Achse:

chart2 = alt.Chart(df).mark_rect().encode(
     x = alt.X('year:O'),
     y = alt.Y('month:O',
     sort=days_of_weeks
    ),
    color = alt.Color(
     'nitems:Q',
      scale=alt.Scale(range=['#F5F5F5','#000000'])
    )
).properties(
    width=300,
    height=300
)

st.altair_chart(chart2)

Abbildung 4-2 zeigt das resultierende Diagramm.

A calendar heatmap of number of shipped items
Abbildung 4-2. Eine kalendarische Heatmap der Anzahl der versendeten Artikel

Fazit

In diesem Kapitel hast du gelernt, wie du einen Presto-Client mit verschiedenen Sprachen bereitstellst, darunter REST API, Python, R, JDBC und Node.js. Die Vorgehensweise ist in allen Fällen gleich: Erstelle das Docker-Image, verpacke es in einen Kubernetes-Knoten und führe den Knoten aus, um Abfragen an Presto zu starten.

Du hast auch einen Client-Webdienst für Presto implementiert, der einen einfachen zusammenfassenden Bericht anzeigt. Der Webservice hat den von Presto zur Verfügung gestellten TPC-H-Katalog abgefragt und einige Statistiken über die Anzahl der Artikel nach Versanddatum angezeigt.

In Kapitel 5 erfährst du, wie du Open Data Lakehouse-Analysen durchführen kannst.

Get Presto lernen und bedienen now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.