Presto worker发现机制 (on k8s)

Presto 服务发现机制

吃个瓜

网上Presto有两个版本:prestosql/prestodb,大家喜欢称为社区版和Facebook版,除了包名,现在看还没多大的区别。但是这两家都在称自己是***offical***的,惊喜不?主要原因是两者是同一波人搞的。原Facebook Presto的三位核心另外拉了一个项目。为啥呢?facebook虽然开源了presto,但是并不怎么搭理开源社区的各位,还是围着自家转,文档什么的都是少到可怜。那三位有些不爽了,毕竟脱离了广大群众的软件不是好软件。总体来说,Presto也算是起了个早床,赶了个晚集。

导火索

大家引入Presto的时候感觉真是爽,查询速度飞起。突然有一天发现Presto抛出异常,报出的错误是和某某节点通信异常。鬼就鬼在这个节点ip不存在,后来发现是因为presto在k8s上的worker pod重启了,上面的ip呢是重启前的ip地址。其根源是冬冬在bbs上有讲的内存设置问题,同时也引发了另一个思考:Presto on k8s时不能按ip来进行通信,因为ip是浮动的。 那么有没有办法用域名呢?

问题解决的路径

获取worker信息

要想解决问题,首先就得把问题了解透彻了,怎么知道集群中有哪些worker?worker怎么标识的?

Presto的页面上能看到有几个节点,可以从证明Presto的coordinate肯定知道这个信息。圈里一般的玩法是提供个api或者是存储位置让用户看。正如瓜中所述,Presto的使用文档格式清晰,排版漂亮,但都是些基础的配置,你想找点高深的配置或者接口,就得费点心了。Presto的源码里面有个文档文件夹,绝对的宝库。

presto-docs/src/main/sphinix/

子文件夹rest中包含了rest接口的所有说明,可知通过接口 /v1/node 能拿到coordinater上worker的信息。标准的返回:

{
    "uri":"http://10.209.57.156:8080",
    "recentRequests":25.181940555111073,
    "recentFailures":0.0,
    "recentSuccesses":25.195472984170983,
    "lastRequestTime":"2013-12-22T13:32:44.673-05:00",
    "lastResponseTime":"2013-12-22T13:32:44.677-05:00",
    "age":"14155.28ms",
    "recentFailureRatio":0.0,
    "recentFailuresByType":{}
}

各返回值的含义:

字段 含义
uri worker的标致
recentRequests 一个特殊运算的单位窗口内request次数统计值
recentFailures 一个特殊运算的单位窗口内request失败的次数统计值
recentSuccesses 一个特殊运算的单位窗口内request成功的次数统计值
lastRequestTime 上次request时间
lastResponseTime 上次Response时间
age worker的生存时间
recentFailureRatio 失败频率,等于recentFailures/ recentRequests
recentFailuresByType 根据具体失败的原因进行分类统计

上述值的统计和计算逻辑,可以在如下类中找到:

io.prestosql.failuredetector.HeartbeatFailureDetector

提到的特殊统计方法比较复杂,有兴趣的可以看两个地方:

  1. airlift项目的DecayCounter类的实现;
  2. 原理上的解释参照 一种可行的时间衰减模型,么有细看,有研究的同学留言。

Worker是如何注册的

上一章节中,我们可以发现Worker的标识是一个uri,这个uri默认采用的是ip地址。一进步我们发现整个Presto的服务发现机制都不在本项目内,而是引用了facebook的另一个开源项目airlift。没错,冷门,网上零文档。你搜索的时候不加个“apache”都不一定能搜索到这个项目。

大数据圈儿里服务发现也不是啥稀罕事儿,一般会有默认值同时提供可配置。开始从airlift的源码中捞。

Apache Airlift中找node配置

First of all,搞明白个术语:在presto集群中管工作节点叫worker,但是在airlift中,大家都叫node。基本将配置文件锁定在NodeConfig类中。但是真正解释着几个配置的地方却在另一个地方的方法的注释中:NodeInfo。恕我不翻译了。

/**
  * The internal network address the server should use when announcing its location to other machines.
  * This address should available to all machines within the environment, but may not be globally routable.
  * If this is not set, the following algorithm is used to choose the public address:
  * 

*

    *
  1. InetAddress.getLocalHost() if good IPv4
  2. *
  3. First good IPv4 address of an up network interface
  4. *
  5. First good IPv6 address of an up network interface
  6. *
  7. InetAddress.getLocalHost()
  8. *
* An address is considered good if it is not a loopback address, a multicast address, or an any-local-address address. */
@Managed public String getInternalAddress() { return internalAddress; } /** * The IP address the server should use when binding a server socket. *

* If this is not set, this will be the IPv4 any local address (e.g., 0.0.0.0). */ @Managed public InetAddress getBindIp() { return bindIp; } private static String findInternalAddress(AddressSource addressSource) { switch (addressSource) { case IP: return InetAddresses.toAddrString(findInternalIp()); case HOSTNAME: return getLocalHost().getHostName(); case FQDN: return getLocalHost().getCanonicalHostName(); default: throw new IllegalArgumentException(); } }

至此,基本我们可以确认我们需要通过internal Address来指定成域名。注意上段代码中的最后一个方法,也就说有一种办法是可以直接去hostname的。But,这种方式并不适合k8s环境,为啥呢?K8s中pod的域名是{host name}.{service name},直接通过hostname是没法访问的。另外,咱们还得把presto k8s的部署方式设置为StatefuleSet,让hostname不会变来变去。

TL;DR

上文中我们已经零星的提到了服务的注册配置信息。那么整个服务发现是怎么玩的呢?Presto并不依赖其他的组件,没有共享DB,没有zookeeper等等,基本就是基于网络的服务发现。我们罗列一下现有的信息,查漏补缺:

  1. Presto依靠apache airlift实现服务发现;
  2. worker上配置有discovery.uri, 指定了discovery服务的地址,也就是各node汇报自己信息的目的地址;
  3. worker上配置有本节点的信息,可以改成域名,可自定义;
  4. airlift将节点信息汇报称为Announce,源码中discovery模块,有服务定时announce的逻辑代码;
  5. announce的方法可以从类“HttpDiscoveryAnnouncementClient”中发现,通过“/v1/announcement/” rest api进行通信;
  6. discovery server的代码并不在airlift项目中,在另外一个叫discovery的项目中。神奇不?
  7. Presto中可配置是否启动discovery服务,也就是说discovery其实也可以外置。在job on yarn等地址无法确认的场景比较需要。

Else

服务的发现和保活是两套机制,注册完全依靠的是airlift,airlift也会定时汇报本地信息,但是频率较低。Presto会以较高的频次ping workder节点并更新worker的状态。

你可能感兴趣的:(presto)