Presto客户端是一个用于查询Presto并显示查询结果的进程,用于各种目的,例如数据分析、即席查询等。Presto提供了各种客户端,使用不同的编程语言编写,包括REST API、R、Python、JDBC、Node.js和ODBC。
这一章分为两个部分。在第一部分中,您将学习如何部署Presto客户端。虽然您可以将Presto客户端部署为在现有集群节点上运行的单独进程,但在本章中,您将在不同的节点上实现客户端,以模拟外部应用程序访问集群。我们将重点介绍如何在REST API、Python、R、JDBC、Node.js和ODBC中实现客户端。您可以轻松扩展所描述的过程以支持Presto支持的其他编程语言。
在第二部分中,您将使用Python实现一个实用的Web服务,查询Presto并在简单的仪表板中显示结果。这个例子的目标是展示Presto客户端的潜力。
搭建环境
为将Presto客户端添加为Presto集群的节点,首先需要设置环境。部署以下在第3章中已实现的三个组件:
- Presto客户端
- 包含Presto客户端的Docker镜像
- 在Presto集群中运行Docker镜像的Kubernetes节点
本节使用的代码可在书籍存储库的04目录中找到,其中包含了所有描述的Presto客户端的配置文件。
Presto客户端
Presto客户端连接到Presto,运行查询并显示结果。您可以使用自己喜欢的编程语言编写Presto客户端代码,并将其保存到将作为独立卷挂载在Kubernetes节点上的目录中。您还可以选择编写一个通过HTTP提供访问权限的Web应用程序或服务。我们将在本章后面看到如何部署一个客户端Web服务。或者,您也可以使用终端访问Presto客户端。
Docker镜像
Docker镜像包含运行Presto客户端所需的环境。使用以下Dockerfile模板:
sql
FROM <basic_image>
RUN apt-get update && \
<install required libraries>
例如,要构建R客户端的镜像,请编写以下Dockerfile:
sql
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')"
要构建您的Docker镜像,请在终端中运行以下命令:
erlang
docker build -t client-presto .
Kubernetes节点
要在Presto集群中部署Kubernetes节点,请编写一个.yaml配置文件,简单地包装Docker镜像:
yaml
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" ]
命令 sleep infinity
确保Kubernetes保持您的客户端处于活动状态。或者,如果您的客户端运行服务,请修改配置代码以部署服务。
要在Docker容器中运行自定义脚本,请挂载本地卷:
yaml
spec:
containers:
- name: client
# 根据客户端类型添加详细信息
volumeMounts:
- name: scripts
mountPath: "/path/in/the/container"
volumes:
- name: scripts
hostPath:
path: "/path/to/scripts/in/the/host"
通过运行以下命令部署客户端:
arduino
kubectl apply -f client.yaml --namespace presto
部署客户端后,登录到客户端并在挂载的目录中运行脚本。
Presto的可用客户端
这一部分概述了如何使用现有的客户端库来建立与 Presto 集群的连接。我们将使用 Presto 支持的不同编程语言。从图书的 GitHub 存储库中下载您偏好的语言中的运行示例,位于目录 04/<language>_client
。如果您对某些语言不感兴趣,可以随意跳过。
REST API
Presto协调器包含支持REST API端点的HTTP服务器。客户端与Presto协调器之间的所有通信都使用HTTP协议。要使用REST API运行Presto客户端,至少需要指定Presto协调器的URL、查询和一个包含要查询的目录和模式的最小标头:
lua
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"
使用-H "X-Presto-User: client"
指定用户。默认情况下,Presto接受任何用户,因此可以写入任何名称。在第7章中,您将看到如何在Presto中配置特定用户。
如果对REST API的调用成功,Presto将返回一个JSON对象,其中nextUri
键已设置。使用与nextUri
键相对应的值作为URL再次调用Presto。稍后在本节中将为此提供一个示例。
REST API为您提供许多端点,例如:
/v1/node
:获取有关节点的信息。/v1/statement
:运行查询。/v1/query
:获取有关最近查询的指标。可选择指定查询ID以获取有关特定查询的信息。/v1/thread
:获取有关线程的信息。/v1/task
:获取有关任务的信息。
迭代对Presto的先前调用,直到响应JSON对象中不存在nextUri
键为止。如果最后一个调用成功,返回的对象将包含data
键的查询结果值。
arduino
curl -s "URL contained in nextUri"
以下代码显示了在Python中调用REST API的完整示例:
运行对REST API的第一个调用:
makefile
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()
迭代nextUri
键,直到其不存在。使用time.sleep(0.5)
等待数据。如果您的客户端没有收到任何数据,请增加此值。
ini
while 'nextUri' in json_resp:
time.sleep(0.5)
new_url = json_resp['nextUri']
resp = requests.get(new_url)
json_resp = resp.json()
获取最终数据:
scss
data = json_resp['data']
for i in range(0, len(data)):
print(data[i])
Presto REST API提供许多其他参数。阅读Presto文档以获取更多详细信息。
Python
安装presto-python-client
库。在Dockerfile中运行以下命令:
pip install presto-python-client
然后,按以下方式连接到Presto:
ini
import prestodb
conn = prestodb.dbapi.connect(
host='presto-coordinator',
port=8080,
user='client',
catalog='tpch',
schema='sf1',
)
要运行查询,请检索游标:
ini
cur = conn.cursor()
cur.execute('SELECT * FROM customer LIMIT 5')
rows = cur.fetchall()
R
安装RPresto库。在Dockerfile中运行以下命令:
arduino
RUN R -e "install.packages('RPresto')"
要在R中查询Presto,请首先导入所需的库:
scss
library(RPresto)
library(DBI)
然后连接到Presto服务器:
ini
con <- DBI::dbConnect(
drv = RPresto::Presto(),
host = "presto-coordinator",
port = 8080,
user = "r-client",
catalog = "tpch",
schema = "sf1"
)
要运行查询,请使用DBI::dbGetQuery()
函数:
arduino
DBI::dbGetQuery(con, "SELECT * FROM customer LIMIT 5")
JDBC
使用Presto JDBC驱动程序com.facebook.presto.jdbc.PrestoDriver
。如果使用Maven,请在pom.xml文件中添加以下依赖项:
xml
<dependencies>
<dependency>
<groupId>com.facebook.presto</groupId>
<artifactId>presto-jdbc</artifactId>
<version>0.276</version>
</dependency>
</dependencies>
如果不使用Maven,请从Presto网站下载presto-jdbc-0.276.jar
文件,并将其添加到Java应用程序的类路径中。请将Presto的版本替换为最新版本(在撰写本章时为0.276)。
在主Java应用程序中,定义连接到Presto的URL,并通过Connection连接到Presto:
java
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();
}
在URL字符串中,指定目录(tpch)和模式(sf1)。然后运行您的查询:
ini
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();
}
该示例打印查询结果集中每个结果的第一列。
Node.js
安装presto-client库:
npm install -g presto-client
在主JavaScript应用程序中,导入库并创建一个新的Client()
对象:
php
var presto = require('presto-client');
var client = new presto.Client({
user: 'myuser',
host: 'http://presto-coordinator',
port: '8080'
});
然后执行任何查询:
lua
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) 是一种标准的数据库访问方法,它使应用程序能够访问各种数据库格式的数据。许多数据库供应商,如Microsoft、Oracle和IBM,提供了ODBC专有驱动程序,以连接到任何符合ODBC标准的数据库,而不考虑数据库的底层结构或格式。
要实现Presto ODBC客户端,首先必须安装一个ODBC驱动程序。在本书中,我们使用了CData。
为了使驱动程序工作,首先安装unixodbc、unixodbc-dev和tdsodbc库。例如,在Ubuntu中,运行以下命令:
arduino
apt-get install -y unixodbc unixodbc-dev tdsodbc
接下来,下载CData Presto驱动程序(Unix版本),安装软件包并激活许可证:
bash
dpkg -i PrestoODBCDriverforUnix.deb
cd /opt/cdata/cdata-odbc-driver-for-presto/bin/
./install-license.sh
然后,在你喜欢的编程语言中安装用于通过ODBC驱动程序访问Presto的客户端库。例如,在Python中,安装pyodbc:
pip install pyodbc
在你的ODBC客户端应用程序中,连接到Presto数据库:
ini
conn = pyodbc.connect("DRIVER=CData ODBC Driver for Presto;\
server=presto-coordinator;\
Port=8080;user=client;\
catalog=tpch;schema=sf1")
然后执行任何查询:
scss
cur = conn.cursor()
cur.execute('SELECT * FROM customer LIMIT 5')
rows = cur.fetchall()
print(rows)
其他Presto客户端库
Presto支持许多其他语言,包括C、Go、PHP和Ruby。在这些语言中构建客户端的过程基本相同。请参考Presto GitHub配置文件,下载特定客户端的模板。例如,在Presto GitHub配置文件的Go存储库中使用Go客户端。
构建Python中的客户端仪表板
这一部分描述了一个实际示例,演示了如何从头开始使用Python实现一个简单的仪表板,该仪表板查询Presto。该仪表板是一个Web应用程序,连接到Presto,运行一个示例查询,并在两个独立的图表中显示查询的输出。在本节中描述的代码在书的GitHub存储库中,位于目录04/client-app。
设置客户端
该仪表板作为一个 Kubernetes 节点运行,暴露一个 HTTP 服务以访问仪表板。我们将使用以下 Python 库实现仪表板:
- Streamlit:将 Python 脚本迅速转换为 Web 应用程序。
- Altair:用于数据可视化的 Python 库。
- Pandas:用于数据集操作的流行 Python 库。
为了配置客户端,请执行以下步骤:
- 编写 Dockerfile,将应用代码复制到 Docker 镜像中,安装所需的库,暴露 Streamlit 的监听端口,并运行应用程序:
sql
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"]
run.sh 脚本简单地启动 Streamlit 服务器:
bash
#!/bin/bash
streamlit run app.py --browser.serverAddress 0.0.0.0
- 通过在包含 Dockerfile 的目录中运行以下命令构建 Docker 镜像:
erlang
docker build -t client-app .
- 使用您已经为 Python 客户端执行的过程将 client-app Docker 镜像包装成 Kubernetes 节点。由于应用程序已经在 Docker 镜像中可用,因此不需要挂载外部卷。此外,请将 Web 服务器端口暴露给外部访问:
yaml
spec:
containers:
- name: client-app
image: client-app:latest
...
ports:
- containerPort: 8501
- 为 Web 应用程序构建一个服务:
yaml
---
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
该服务暴露与原始 Web 服务器相同的端口。此外,它使用 LoadBalancer 将服务提供给 Kubernetes 集群之外的环境。
- 在 presto 集群中部署 client-app 节点:
arduino
kubectl apply -f client-app.yaml --namespace presto
最后,打开浏览器并指向 http://localhost:8501 访问 Web 应用程序。
构建仪表板
客户端仪表板是一个连接到 Presto 的 Web 服务器,运行一个示例查询,并将输出显示为图形。此应用的目标是演示构建客户端应用的潜力,而不是构建数据分析的完整示例。 客户端仪表板的代码可在书籍的 GitHub 存储库的 04/client-app/app 目录下找到。该代码组织成一个名为 app.py 的单个脚本,该脚本将作为 Streamlit Web 服务器运行。具体而言,该应用执行以下操作:
- 连接和查询 Presto
- 准备查询结果
- 构建第一个图表
- 构建第二个图表
连接到 Presto 并查询
首先,应用程序导入所有必需的库:
javascript
import prestodb
import altair as alt
import pandas as pd
import streamlit as st
然后,应用程序使用 prestodb 库连接到 Presto。使用"Python"中描述的代码执行连接。
应用程序运行一个查询,该查询列出了按最早发货日期排序的 TPC-H 目录中按日期发货的物品数量:
vbnet
SELECT
count(*) as nitems,
shipdate
FROM
lineitem
GROUP BY shipdate
ORDER BY shipdate ASC
准备查询结果
应用程序将查询结果存储在一个Pandas DataFrame中:
scss
cur.execute(query)
df = pd.DataFrame(cur.fetchall(), columns=['nitems', 'shipdate'])
为了构建最终的图表,应用程序从'shipdate'中提取了一些信息:
scss
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
提取的信息包括一周中的日期(从星期日到星期六)、年份、月份和一年中的周数(从1到53的递增数字)。
构建第一个图表
第一个图表聚焦于1992年,显示了与一周内每天的发货数量相关的每周一条线:
bash
df_1992 = df[df['year'] == 1992 ]
图表由两个部分组成。图表的第一部分显示了一年中每周的一条线:
ini
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
)
)
图表的第二部分显示了所有周的发货数量的平均值:
ini
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']
)
最后,应用程序将这两个部分组合在一起构建最终的图表:
ini
chart1 = (bar + mean).properties(
width=300,
height=300,
title='Number of shipped items in 1992')
)
st.altair_chart(chart1)
我们使用 st.altair_chart()
函数构建图表。图 4-1 显示了生成的图表。
构建第二个图表
第二个图表展示了一个日历热图,其中年份位于 x 轴,月份位于 y 轴:
ini
pythonCopy code
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,
title='发货总数的年度月历热图'
)
st.altair_chart(chart2)
此图表使用 alt.Chart()
构建,并使用 st.altair_chart()
函数将其显示在 Streamlit 应用程序中。图表显示了发货总数的年度月历热图。
总结
在本章中,您学习了如何使用不同的语言(包括 REST API、Python、R、JDBC 和 Node.js)部署 Presto 客户端。在所有情况下,该过程都相同:构建 Docker 镜像,将其封装到 Kubernetes 节点中,然后执行该节点以向 Presto 发送查询。
您还实现了一个用于 Presto 的客户端 Web 服务,显示了一个简单的摘要报告。该 Web 服务查询了由 Presto 提供的 TPC-H 目录,并显示了与发货日期相关的一些项目数量的统计信息。
在第 5 章中,您将学习如何执行开放式数据湖分析。