简介

同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. jmeter安装与部署

    1.JDK组件安装 jmeter是一款java开发的测试软件,需要JDK运行环境支持,因此在安装jmeter前需要安装调试好jdk运行环境.jmeter3.2需要1.8以上版本jdk才可以正常运行.首 ...

  2. 原子操作CAS-最小的线程安全

    原文连接:(http://www.studyshare.cn/blog-front/blog/details/1166/0 )一.原子操作是什么? 如果有两个线程分别执行两个操作A和B,从第一个线程执 ...

  3. 系统学习 Java IO (十二)----数据流和对象流

    目录:系统学习 Java IO---- 目录,概览 DataInputStream/DataOutputStream 允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型. 要想使用 ...

  4. Niginx简单的配置

    #user nobody; #这里的数值不能超过 CPU 的总核数,因为在单个核上部署超过 1 个 Nginx 服务进程并不起到提高性能的作用.worker_processes 2; #nginx进程 ...

  5. spring源码深度解析— IOC 之 默认标签解析(下)

    在spring源码深度解析— IOC 之 默认标签解析(上)中我们已经完成了从xml配置文件到BeanDefinition的转换,转换后的实例是GenericBeanDefinition的实例.本文主 ...

  6. .Net之使用Jquery Ajax通过FormData对象异步提交图片文件到服务端保存并返回保存的图片路径

    前言: 首先对于图片上传而言,在我们的项目开发中可以说出现的频率是相当的高的.这篇文章中,我将要描述的是在我们.Net中如何使用Jquery Ajax通过FormData对象异步提交图片文件到后台保存 ...

  7. Python基础-组织列表

    1.字符串的几个常用方法 name = ''adb lovelace '' name.title() 首字母大写 name.upper() 全部大写 name.lower() 全部小写 2.组织列表操 ...

  8. pod update更新error: RPC failed; curl 18 transfer closed with outstanding read data remaining

    1. pod update 的时候出现下边的错误 error: RPC failed; curl 18 transfer closed with outstanding read data remai ...

  9. CSS属性过滤

    在前端领域,当我们想在ie6及以下版本和标准浏览器的样式不同我们怎么做呢 此时此刻不得不提到过滤器:即一种对特定浏览器显示和隐藏规则的声明方法 1.下划线属性过滤器 符合标准的浏览器都会忽略这个声明, ...

  10. Ajax探讨

    Ajax是一种技术方案,并不是什么新技术,Ajax请求使用XmlHttpRequest对象发送, XmlHttpRequest是一个浏览器接口,使得Javascript可以进行HTTP(S)通信. 最 ...