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:
dockerbuild
-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:
kubectlapply
-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.
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:
-
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
()
-
Iterar sobre la clave
nextUri
hasta que esté ausente. Utilizatime.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
()
-
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:
pipinstall
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
:
npminstall
-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-getinstall
-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
:
pipinstall
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
()
(
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
- Pandas
-
Una popular biblioteca de Python para la manipulación de conjuntos de datos
Para configurar el cliente, realiza los siguientes pasos:
-
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
streamlit0
.0.0.0 -
Construye la imagen Docker ejecutando el siguiente comando desde el directorio donde se encuentra el archivo Dockerfile:
docker
-
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
-
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. -
Implementa el nodo de la aplicación cliente en el clúster
presto
:kubectl apply -f client-app.yaml --namespace presto
-
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.
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.
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.