Capítulo 4. Conectividad de clientes

Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y comentarios: translation-feedback@oreilly.com

Un cliente de Presto es un proceso que consulta a Presto y muestra los resultados de la consulta para diversos fines, como análisis de datos, consultas ad hoc y muchos más. Presto proporciona una gran variedad de clientes, escritos en diferentes lenguajes de programación, como API REST, R, Python, JDBC, Node.js y ODBC.

Este capítulo está dividido en dos partes: en la primera aprenderás a implementar un cliente Presto. Aunque puedes desplegar un cliente Presto como un proceso independiente que se ejecuta en un nodo del clúster existente, en este capítulo, implementarás el cliente en un nodo diferente para simular que una aplicación externa accede al clúster. Nos centraremos en la implementación de un cliente en la API REST, Python, R, JDBC, Node.js y ODBC. Puedes ampliar fácilmente el procedimiento descrito a los demás lenguajes de programación admitidos por Presto.

En la segunda parte, implementarás un servicio web práctico en Python, consultando a Presto y mostrando los resultados en un sencillo panel de control. El objetivo de este ejemplo es ilustrar las potencialidades de un cliente Presto.

Configurar el entorno

Para añadir un cliente Presto como nodo del cluster Presto, primero debes configurar el entorno. Despliega los tres componentes siguientes, ya implementados en el Capítulo 3:

  • Cliente Presto

  • Imagen Docker que contiene tu cliente Presto

  • Nodo Kubernetes que ejecuta la imagen Docker en el clúster Presto

El código utilizado en esta sección está disponible en el directorio 04 del repositorio del libro, que contiene los archivos de configuración de todos los clientes Presto descritos.

Cliente Presto

El cliente Presto se conecta a Presto, ejecuta consultas y muestra los resultados. Puedes escribir tu cliente Presto en el idioma que prefieras. Guarda el código en un directorio que se montará en el nodo Kubernetes como un volumen independiente. Opcionalmente, escribe una aplicación web o un servicio que permita el acceso a través de HTTP. Veremos cómo implementar un servicio web cliente más adelante en este capítulo. Alternativamente, utiliza el terminal para acceder a tu cliente Presto.

Imagen Docker

La imagen Docker contiene el entorno necesario para ejecutar tu cliente Presto. Utiliza la siguiente plantilla para el archivo Dockerfile:

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

Por ejemplo, para construir la imagen del cliente R, escribe el siguiente archivo 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')"

Para construir tu imagen Docker, ejecuta el siguiente comando en un terminal:

docker build -t client-presto .

Nodo Kubernetes

Para implementar un nodo Kubernetes en el clúster Presto, escribe un archivo de configuración .yaml que simplemente envuelva la imagen Docker:

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" ]

El comando sleep infinity se asegura de que Kubernetes mantiene vivo a tu cliente. Alternativamente, si tu cliente ejecuta un servicio, modifica el código de configuración para implementar el servicio.

Para ejecutar scripts personalizados en tu contenedor Docker, monta un volumen local:

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"

Implementa el cliente ejecutando el siguiente comando:

kubectl apply -f client.yaml --namespace presto

Una vez que hayas desplegado tu cliente, conéctate a él y ejecuta las secuencias de comandos del directorio montado.

Advertencia de montaje

Utiliza la opción volumeMount al escribir tu código. En un entorno de producción, envuelve la aplicación implementada directamente en la imagen Docker en lugar de montarla en un directorio separado en tu archivo .yaml de Kubernetes.

Conectividad con Presto

Esta sección ofrece una visión general de cómo utilizar las bibliotecas cliente existentes para establecer una conexión con el clúster Presto. Utilizaremos los distintos lenguajes de programación soportados por Presto. Descarga el ejemplo en ejecución en tu idioma preferido desde el repositorio GitHub del libro, en el directorio 04/<idioma>_cliente. Siéntete libre de saltarte los lenguajes que no te interesen.

API REST

El coordinador Presto incluye un servidor HTTP que admite un punto final de la API REST. Todas las comunicaciones entre el cliente y el coordinador Presto utilizan el protocolo HTTP. Para ejecutar un cliente Presto utilizando una API REST, especifica al menos la URL al coordinador Presto, la consulta y una cabecera mínima que incluya el catálogo y el esquema a consultar:

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"

Especifica el usuario utilizando -H "X-Presto-User: client". Por defecto, Presto acepta cualquier usuario, así que escribe cualquier nombre. En el Capítulo 7, verás cómo configurar usuarios específicos en Presto.

Si la llamada a la API REST tiene éxito, Presto devuelve un objeto JSON en el que se establece la clave nextUri. Ejecuta otra llamada a Presto utilizando el valor correspondiente a la clave nextUri como URL. Verás un ejemplo de esto más adelante en esta sección.

La API REST proporciona muchos puntos finales, como los siguientes:

/v1/node

Para obtener información sobre un nodo.

/v1/statement

Para realizar consultas.

/v1/query

Para obtener métricas sobre las consultas más recientes. Opcionalmente, especifica el ID de la consulta para obtener información sobre una consulta concreta.

/v1/thread

Para obtener información sobre un hilo.

/v1/task

Para obtener información sobre una tarea.

Iterar la llamada anterior a Presto hasta que la clave nextUri esté ausente en el objeto JSON de respuesta. Si la última llamada tiene éxito, el objeto devuelto contiene los resultados de la consulta como valor de la clave data.

curl -s "URL contained in nextUri"

El código siguiente muestra a un ejemplo completo de llamada a la API REST en Python:

  1. Ejecuta la primera llamada a la API REST:

    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. Iterar sobre la clave nextUri hasta que esté ausente. Utiliza time.sleep(0.5) para esperar datos. Aumenta este valor si tu cliente no recibe ningún dato.

    while 'nextUri' in json_resp:
     time.sleep(0.5)
     new_url = json_resp['nextUri']
     resp = requests.get(new_url)
     json_resp = resp.json()
  3. Obtén los datos finales:

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

La API REST de Presto proporciona muchos parámetros adicionales. Lee la documentación de Presto para obtener más detalles.

Python

Instala la biblioteca presto-python-client. En tu Dockerfile, ejecuta el siguientecomando:

pip install presto-python-client

A continuación, conéctate a Presto como se indica a continuación:

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

Para ejecutar una consulta, recupera el cursor:

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

R

Instala la biblioteca RPresto.En tu Dockerfile, ejecuta el siguiente comando:

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

Para consultar Presto en R, importa primero las bibliotecas necesarias:

library(RPresto)
library(DBI)

A continuación, conéctate al servidor Presto:

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

Para ejecutar una consulta, utiliza la función DBI::dbGetQuery():

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

JDBC

Utiliza el controlador Presto JDBC com.facebook.presto.jdbc.PrestoDriver. Si utilizas Maven, añade las siguientes dependencias a tu archivo pom.xml:

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

Si no utilizas Maven, descarga el archivo presto-jdbc-0.276.jar del sitio web de Presto y añádelo al classpath de tu aplicación Java.Sustituye la versión de Presto por la versión más reciente de Presto (0.276 en el momento de escribir este capítulo).

En tu aplicación Java principal, define la URL al servidor Presto y conéctate a Presto a través de una dirección 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();
}

En la cadena URL, especifica en el catálogo (tpch) y el esquema (sf1). A continuación, ejecuta tus consultas:

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();
}

El ejemplo imprime la primera columna de cada resultado del conjunto de resultados de la consulta.

Node.js

Instala la biblioteca presto-client:

npm install -g presto-client

En tu aplicación principal de JavaScript, importa la biblioteca y crea un nuevo objeto Client():

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

A continuación, ejecuta cualquier consulta:

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

La Conectividad Abierta a Bases de Datos (ODBC) es un método estándar de acceso a bases de datos que permite a las aplicaciones acceder a datos en diversos formatos de bases de datos. Muchos proveedores de bases de datos, como Microsoft, Oracle e IBM, proporcionan controladores propios ODBC para conectarse a cualquier base de datos compatible con ODBC, independientemente de la estructura o el formato subyacentes de la base de datos.

Para implementar un cliente ODBC de Presto, primero debes instalar un controlador ODBC. En este libro, utilizamos CData.

Para que el controlador funcione, instala primero las bibliotecas unixodbc, unixodbc-dev y tdsodbc. Por ejemplo, en Ubuntu, ejecuta el siguiente comando:

apt-get install -y unixodbc unixodbc-dev tdsodbc

A continuación, descarga el controlador CData para Presto (versión Unix), instala el paquete y activa la licencia:

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

A continuación, instala la biblioteca cliente en tu lenguaje preferido para acceder a Presto a través del controlador ODBC. Por ejemplo, en Python, instala pyodbc:

pip install pyodbc

En tu aplicación cliente ODBC, conecta a la base de datos Presto:

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

A continuación, ejecuta cualquier consulta:

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

Otras bibliotecas de clientes Presto

Presto admite muchos otros lenguajes, como C, Go, PHP y Ruby. El procedimiento para construir tu cliente en cualquiera de estos lenguajes es esencialmente el mismo.Consulta el perfil de Presto en GitHub para descargar la plantilla de un cliente específico. Por ejemplo, utiliza el cliente Go en el repositorio Go del perfil Presto GitHub.

Construir un panel de control de cliente en Python

Esta sección describe un ejemplo práctico de cómo implementar desde cero un sencillo cuadro de mando de consulta a Presto en Python. El panel es una aplicación web que se conecta a Presto, ejecuta una consulta de ejemplo y muestra los resultados de la consulta en dos gráficos distintos.

El código descrito en esta sección está disponible en el repositorio GitHub del libro, en el directorio 04/client-app.

Configurar el cliente

El panel se ejecuta como un nodo de Kubernetes, que expone un servicio HTTP para acceder al panel. Implementaremos el panel de control utilizando las siguientes bibliotecas de Python:

Streamlit

Transforma scripts Python en aplicaciones web muy rápidamente

Altair

Una biblioteca Python para la visualización de datos

Pandas

Una popular biblioteca de Python para la manipulación de conjuntos de datos

Para configurar el cliente, realiza los siguientes pasos:

  1. Escribe el Dockerfile que copia el código de la app en la imagen Docker, instala las librerías necesarias, expone el puerto de escucha Streamlit y ejecuta la app:

    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"]

    El script run.sh simplemente lanza el servidor Streamlit:

    #!/bin/bash
    streamlit run app.py --browser.serverAddress 0.0.0.0
  2. Construye la imagen Docker ejecutando el siguiente comando desde el directorio donde se encuentra el archivo Dockerfile:

    docker build -t client-app .
  3. Envuelve la imagen Docker client-app en un nodo Kubernetes, siguiendo el procedimiento que ya has seguido para el cliente Python. No montes un volumen externo, puesto que la aplicación ya está disponible desde la imagen Docker. Además, haz que el puerto del servidor web esté disponible para el acceso externo.

    spec:
     containers:
     - name: client-app
       image:  client-app:latest
       ...
       ports:
         - containerPort: 8501
  4. Construye un servicio para la aplicación web:

    ---
    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

    El servicio expone el mismo puerto que el servidor web original. Además, utiliza un LoadBalancer para que el servicio esté disponible fuera del clúster de Kubernetes.

  5. Implementa el nodo de la aplicación cliente en el clúster presto:

    kubectl apply -f client-app.yaml --namespace presto
  6. Por último, abre tu navegador y apunta a http://localhost:8501 para acceder a la aplicación web.

Construir el Cuadro de Mando

El panel del cliente es un servidor web que se conecta a Presto, ejecuta una consulta de ejemplo y muestra los resultados en forma de gráficos. El objetivo de esta aplicación es demostrar las potencialidades de construir una aplicación cliente, más que construir un ejemplo completo de análisis de datos.

El código del panel de control del cliente está disponible en 04/client-app/app del repositorio GitHub del libro. El código está organizado en un único script llamado app.py, que se ejecuta el servidor web como servidor web Streamlit. En detalle, la app realiza las siguientes operaciones:

  • Conexión y consulta a Presto

  • Preparar los resultados de la consulta

  • Construir el primer gráfico

  • Construir el segundo gráfico

Conexión y consulta a Presto

En primer lugar, la aplicación importa todas las bibliotecas necesarias de :

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

A continuación, la aplicación se conecta a Presto utilizando la biblioteca prestodb. Utiliza el código descrito en "Python" para realizar la conexión.

Recursos Advertencia

Si tu máquina local no tiene recursos suficientes para gestionar un gran conjunto de datos, utiliza el esquema tiny del catálogo TPC-H cuando especifiques los parámetros de conexión a Presto. El esquema tiny contiene unos 10.000 elementos de datos de muestra.

La aplicación ejecuta una consulta que enumera el número de artículos enviados por fecha desde el catálogo TPC-H y los ordena por fecha de envío más antigua:

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

Preparar los resultados de la consulta

La app almacena el resultado de la consulta en un DataFrame Pandas:

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

Para construir los gráficos finales, la aplicación extrae cierta información de 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

La información extraída incluye el día de la semana (de domingo a sábado), el año, el mes y la semana del año (un número progresivo de 1 a 53).

Construir el primer gráfico

El primer gráfico se centra en 1992 y muestra una línea para cada semana del año, relacionada con el número de artículos enviados organizados por día de la semana:

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

El gráfico se compone de dos partes. La primera parte del gráfico muestra una línea para cada semana del año:

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
    )
)

La segunda parte del gráfico muestra el valor medio del número de artículos enviados a lo largo de todas las semanas:

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']
)

Por último, la aplicación combina las dos partes para construir el gráfico final:

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

Utilizamos la función st.altair_chart() para construir el gráfico. La Figura 4-1 muestra el gráfico resultante.

Number of shipped items in 1992 organized by week
Figura 4-1. Número de envíos en 1992 organizados por semanas

Construir el segundo gráfico

El segundo gráfico muestra un mapa de calor del calendario, con años en el eje x y meses en el eje y:

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)

La Figura 4-2 muestra el gráfico resultante.

A calendar heatmap of number of shipped items
Figura 4-2. Un mapa de calor de calendario del número de artículos enviados

Conclusión

En este capítulo has aprendido a implementar un cliente de Presto utilizando distintos lenguajes, como API REST, Python, R, JDBC y Node.js. El procedimiento es el mismo en todos los casos: construye la imagen Docker, envuélvela en un nodo Kubernetes y ejecuta el nodo para realizar consultas a Presto.

También implementaste un servicio web cliente para Presto, que mostraba un sencillo informe resumido. El servicio web consultaba el catálogo TPC-H puesto a disposición por Presto y mostraba algunas estadísticas relacionadas con el número de artículos por fecha de envío.

En el Capítulo 5, aprenderás a realizar análisis de lago de datos abiertos.

Get Aprender y utilizar Presto 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.