简介

同Hive Hook一样,Presto也支持自定义实现Event Listener,用于侦听Presto引擎执行查询时发生的事件,并作出相应的处理。我们可以利用该功能实现诸如自定义日志记录、调试和性能分析插件,帮助我们更好的运维Presto集群。但是不同于Hive Hook的是,在Presto集群中,一次只能有一个Event Listener处于活动状态。

Event Listener作为Plugin监听以下事件:

  • Query Creation(查询建立相关信息)
  • Query completion (success or failure)(查询执行相关信息,包含成功查询的细节信息,失败查询的错误码等信息)
  • Split completion (success or failure)(split执行信息,同理包含成功和失败的细节信息)

了解Hook及Listener模式的朋友对于其步骤应该很清楚了,我们只需要:

  1. 实现Presto Event Listener和EventListenerFactory接口。
  2. 正确的打包我们的jar。
  3. 部署,放到Presto指定目录,修改配置文件。

接口

  1. 实现EventListener,该类是我们的核心逻辑所在,供包含上面所说的三个事件:
public interface EventListener
{
//query创建的详细信息
default void queryCreated(QueryCreatedEvent queryCreatedEvent)
{
}
//query执行的详细信息
default void queryCompleted(QueryCompletedEvent queryCompletedEvent)
{
}
//split执行的详细信息
default void splitCompleted(SplitCompletedEvent splitCompletedEvent)
{
}
}
  1. 实现EventListenerFactory创建我们自己实现的EventListener
  2. 实现Plugin接口,实现getEventListenerFactories()方法,获取我们自己实现的EventListenerFactory
  3. 添加配置信息,为etc/event-listener.properties。其中event-listener.name为必备属性,其他属性为我们plugin所需要的信息。

示例

由于集群运维的需要,先需要将用户的查询历史、查询花费的时间等信息进行统计,以便于后续对各个业务的查询进行优先级分级和评分,方便后续Presto集群稳定性易用性的维护。这里给出一个简单的将这些信息存储到Mysql数据库的样例。

Maven Pom

<dependency>
<groupId>com.facebook.presto</groupId>
<artifactId>presto-spi</artifactId>
<version>0.220</version>
<scope>compile</scope>
</dependency>

QueryEventListenerFactory

public class QueryEventListenerFactory implements EventListenerFactory {

@Override

public String getName() {

return "query-event-listener";

} @Override

public EventListener create(Map<String, String> config) {

if (!config.containsKey("jdbc.uri")) {

throw new RuntimeException("/etc/event-listener.properties file missing jdbc.uri");

}

if (!config.containsKey("jdbc.user")) {

throw new RuntimeException("/etc/event-listener.properties file missing jdbc.user");

}

if (!config.containsKey("jdbc.pwd")) {

throw new RuntimeException("/etc/event-listener.properties file missing jdbc.pwd");

}
return new QueryEventListener(config);

}

}

QueryEventPlugin

public class QueryEventPlugin implements Plugin {

@Override

public Iterable<EventListenerFactory> getEventListenerFactories() {

EventListenerFactory listenerFactory = new QueryEventListenerFactory();

return Arrays.asList(listenerFactory);

}

}

QueryEventListener

public class QueryEventListener implements EventListener {

private Map<String, String> config;

private Connection connection; public QueryEventListener(Map<String, String> config) {

this.config = new HashMap<>();

this.config.putAll(config);

init();

} private void init() {

try {

if (connection == null || !connection.isValid(10)) {

Class.forName("com.mysql.jdbc.Driver");

connection = DriverManager

.getConnection(config.get("jdbc.uri"), config.get("jdbc.user"), config.get("jdbc.pwd"));

}

} catch (SQLException | ClassNotFoundException e) {

e.printStackTrace();

}

} @Override

public void queryCreated(QueryCreatedEvent queryCreatedEvent) {

} @Override

public void queryCompleted(QueryCompletedEvent queryCompletedEvent) {

String queryId = queryCompletedEvent.getMetadata().getQueryId();

String querySql = queryCompletedEvent.getMetadata().getQuery();

String queryState = queryCompletedEvent.getMetadata().getQueryState();

String queryUser = queryCompletedEvent.getContext().getUser();

long createTime = queryCompletedEvent.getCreateTime().toEpochMilli();

long endTime = queryCompletedEvent.getEndTime().toEpochMilli();

long startTime = queryCompletedEvent.getExecutionStartTime().toEpochMilli();

//insert into query execution table
long analysisTime = queryCompletedEvent.getStatistics().getAnalysisTime().orElse(Duration.ZERO)
.toMillis();
long cpuTime = queryCompletedEvent.getStatistics().getCpuTime().toMillis();
long queuedTime = queryCompletedEvent.getStatistics().getQueuedTime().toMillis();
long wallTime = queryCompletedEvent.getStatistics().getWallTime().toMillis();
int completedSplits = queryCompletedEvent.getStatistics().getCompletedSplits();
double cumulativeMemory = queryCompletedEvent.getStatistics().getCumulativeMemory();
long outputBytes = queryCompletedEvent.getStatistics().getOutputBytes();
long outputRows = queryCompletedEvent.getStatistics().getOutputRows();
long totalBytes = queryCompletedEvent.getStatistics().getTotalBytes();
long totalRows = queryCompletedEvent.getStatistics().getTotalRows();
long writtenBytes = queryCompletedEvent.getStatistics().getWrittenBytes();
long writtenRows = queryCompletedEvent.getStatistics().getWrittenRows();
//insert into query info table queryCompletedEvent.getFailureInfo().ifPresent(queryFailureInfo -&gt; {
int code = queryFailureInfo.getErrorCode().getCode();
String name = queryFailureInfo.getErrorCode().getName();
String failureType = queryFailureInfo.getFailureType().orElse("").toUpperCase();
String failureHost = queryFailureInfo.getFailureHost().orElse("").toUpperCase();
String failureMessage = queryFailureInfo.getFailureMessage().orElse("").toUpperCase();
String failureTask = queryFailureInfo.getFailureTask().orElse("").toUpperCase();
String failuresJson = queryFailureInfo.getFailuresJson();
// insert into failed query table
});

}

@Override

public void splitCompleted(SplitCompletedEvent splitCompletedEvent) {

long createTime = splitCompletedEvent.getCreateTime().toEpochMilli();

long endTime = splitCompletedEvent.getEndTime().orElse(Instant.MIN).toEpochMilli();

String payload = splitCompletedEvent.getPayload();

String queryId = splitCompletedEvent.getQueryId();

String stageId = splitCompletedEvent.getStageId();

long startTime = splitCompletedEvent.getStartTime().orElse(Instant.MIN).toEpochMilli();

String taskId = splitCompletedEvent.getTaskId();

long completedDataSizeBytes = splitCompletedEvent.getStatistics().getCompletedDataSizeBytes();

long completedPositions = splitCompletedEvent.getStatistics().getCompletedPositions();

long completedReadTime = splitCompletedEvent.getStatistics().getCompletedReadTime().toMillis();

long cpuTime = splitCompletedEvent.getStatistics().getCpuTime().toMillis();

long queuedTime = splitCompletedEvent.getStatistics().getQueuedTime().toMillis();

long wallTime = splitCompletedEvent.getStatistics().getWallTime().toMillis();

//insert into stage info table

}

}

打包

  1. Presto使用服务提供者接口(SPI)来扩展Presto。Presto使用SPI加载连接器功能类型系统访问控制。SPI通过元数据文件加载。我们还需要创建src/main/resources/META-INF/services/com.facebook.presto.spi.Plugin元数据文件。该文件应包含我们插件的类名如: com.ji3jin.presto.listener.QueryEventListener
  2. 执行mvn clean install打包

部署

  1. 创建配置文件etc/event-listener.properties
event-listener.name=query-event-listener

jdbc.uri=jdbc:mysql://localhost:3306/presto_monitor

jdbc.user=presto

jdbc.pwd=presto123

  1. 在presto根目录下创建query-event-listener目录,名称与我们上面event listener的name一致
  2. 将我们的jar包和mysql connector的jar包拷贝到上面创建的目录
  3. 重新启动Presto服务即可

好了,现在你可以执行查询,然后就可以在Mysql中看到你的查询历史和相关时间的统计信息了。如果你目前的工作对此也有需要,还等什么,快动手实现一个吧。

欢迎关注我的公众号:叁金大数据

Presto Event Listener开发的更多相关文章

  1. 引用fastclick.js或使用触屏监听 滑动屏幕报错:解决[Intervention] Unable to preventDefault inside passive event listener

    使用fastClick.js所产生的一些问题 开发h5活动页时想到移动端会有300ms的延迟,于是便打算用fastClick.js解决. 页面引入fastClick.js后,滑动H5页面的时候发现谷歌 ...

  2. Android事件监听器Event Listener

    在 Android 中,我们可以通过事件处理使UI与用户互动(UI Events). UI的用户事件处理,即View处理用户的操作,在应用程序中几乎不可避免.View是重要的类,它是与用户互动的前线: ...

  3. Unable to preventDefault inside passive event listener due to target being treated as passive

    Unable to preventDefault inside passive event listener due to target being treated as passive 今天在做项目 ...

  4. Unable to preventDefault inside passive event listener

    最近做项目经常在 chrome 的控制台看到如下提示: Unable to preventDefault inside passive event listener due to target bei ...

  5. 滑动时候警告:Unable to preventDefault inside passive event listener

    1 前言 在制作2048时,需要在手机端添加滑动检测事件,然后发现控制台有警告,如下: main2048.js:218 [Intervention] Unable to preventDefault ...

  6. 移动端页面滑动时候警告:Unable to preventDefault inside passive event listener due to target being treated as passive.

    移动端项目中,在滚动的时候,会报出以下提示: [Intervention] Unable to preventDefault inside passive event listener due to ...

  7. [Intervention] Unable to preventDefault inside passive event listener due to target being treated as passive. See https://www.chromestatus.com/features/5093566007214080

    相信如果用谷歌浏览器做移动端页面的时候 用touch事件的时候应该遇到过这个东东吧 documet.addEventListener("touchstart",function() ...

  8. [Intervention] Unable to preventDefault inside passive event listener due to target being treated as passive.

    1.滑动时候警告[Intervention] Unable to preventDefault inside passive event listener due to target being tr ...

  9. IScroll Unable to preventDefault inside passive event listener due to target being treated as passive

    最近两天企业微信下IScroll突然无法滚动了,特别慢,之前好好的,发现主要是有红色的Unable to preventDefault inside passive event listener du ...

随机推荐

  1. 解决wireshark打开错误

    错误提示如下: Lua: Error during loading: [string "/usr/share/wireshark/init.lua"]:44: dofile has ...

  2. Scala 学习之路(十一)—— 模式匹配

    一.模式匹配 Scala支持模式匹配机制,可以代替swith语句.执行类型检查.以及支持析构表达式等. 1.1 更好的swith Scala不支持swith,可以使用模式匹配match...case语 ...

  3. Spark学习之路(二)—— Spark开发环境搭建

    一.安装Spark 1.1 下载并解压 官方下载地址:http://spark.apache.org/downloads.html ,选择Spark版本和对应的Hadoop版本后再下载: 解压安装包: ...

  4. 老雷socket编程之websocket实现

    老雷socket编程之websocket实现 我们主要实现私聊和群聊两个功能,要在web端实现想微信QQ那样的即时通讯的功能,我们需要了解一下websocket.websocket是一种可以双向通讯的 ...

  5. Java学习笔记——三层架构

    Layer: UI层: user interface 用户接口层 Biz层:   service business login layer 业务逻辑层 DAO层:   Date Access Obje ...

  6. solr 重要的知识点

    1 solr 查询参数说明 常用 ) q - 查询字符串,必须的. ) fl - 指定返回那些字段内容,用逗号或空格分隔多个. ) start - 返回第一条记录在完整找到结果中的偏移位置, 开始,一 ...

  7. PHP中var_dump、&&和GLOBALS的爱恨纠缠

    var_dump函数:用来打印显示一个变量的内容与结构: &&:定义一个可变变量.php中,在定义变量时,需要在前面加上一个“&”符号,当加上两个“&&”符号时 ...

  8. .NET Core IdentityServer4实战 第六章-Consent授权页

    在identityServer4中登陆页面只要是成功了,就会注册一个Cookie在服务器资源上,像现在大部分的网站第三方授权,都是经过一个页面,然后选需要的功能,IdentityServer4也给我们 ...

  9. 研究Electron主进程、渲染进程、webview之间的通讯

    背景 由于某个Electron应用,需要主进程.渲染进程.webview之间能够互相通讯. 不过因为Electron仅提供了主进程与渲染进程的通讯,没有渲染进程之间或渲染进程与webview之间通讯的 ...

  10. SpringCloud解析之Ribbon

    Ribbon是分布式微服务架构中负载均衡的一个解决方案,我们只需要引入ribbon依赖,然后初始化一个RestTemplate对象,在其上添加@LoadBalanced注解,就可以实现请求的负载均衡, ...