善仁,蚂蚁金服通用搜索产品负责人,通用搜索目前拥有上万亿文档,服务了上百个业务方,是蚂蚁内部最大的搜索产品。其所在的蚂蚁中间件搜索团队专一于构建简单可信的搜索产品,是阿里经济体中最大的搜索服务提供商。目前专一于抽象各类复杂场景下的搜索解决方案,力求让搜索人人可用,人人会用。 前端
本文根据他在 2018 Elastic 中国开发者大会的分享整理nginx
你们好,我是来自蚂蚁金服中间件团队的善仁,目前是蚂蚁通用搜索产品的负责人。今天给你们分享的主题是《Elasticsearch 在蚂蚁金服的中台实践经验》git
基于 Elasticsearch 的通用搜索是蚂蚁内部最大的搜索产品,目前拥有上万亿文档,服务了上百个业务方。而通用搜索的发展主要分为两个阶段:平台化和中台化。github
今天我将分别介绍下咱们在这两个阶段的发展中为业务解决了哪些痛点以及咱们是如何去解决这些痛点的。web
和大多数大型企业同样,蚂蚁内部也有一套自研的搜索系统,咱们称之为主搜。数据库
可是因为这种系统可定制性高,因此通常业务接入比较复杂,周期比较长。而对于大量新兴的中小业务而言,迭代速度尤其关键,所以难以用主搜去知足。后端
主搜不能知足,业务又实际要用,怎么办呢?那就只能自建了。在前几年蚂蚁内部有不少小的搜索系统,有 ES,也有 solr,甚至还有本身用 lucene 的。api
然而业务因为自身迭代速度很快,去运维这些搜索系统成本很大。就像 ES,虽然搭建一套非常简单,可是用在真实生产环境中仍是须要不少专业知识的。做为业务部门很难去投入人力去运维维护。缓存
而且因为蚂蚁自身的业务特性,不少业务都是须要高可用保证的。而咱们都知道ES自己的高可用目前只能跨机房部署了,先不谈跨机房部署时的分配策略,光是就近访问一点,业务都很难去完成。架构
由于这些缘由,致使这类场景基本都没有高可用,业务层宁愿写两套代码,准备一套兜底方案。以为容灾时直接降级也比高可用简单。
从总体架构层面看,各个业务自行搭建搜索引擎形成了烟囱林立,各类重复建设,而且这种中小业务通常数据量都比较小,每每一个业务一套三节点集群只有几万条数据,形成总体资源利用率很低,并且因为搜索引擎选用的版本,部署的方式都不一致,也难以保证质量。在架构层面只能当作不存在搜索能力。
基于以上痛点,咱们产生了构建一套标准搜索平台的想法,将业务从运维中解放出来,也从架构层面统一基础设施。提供一种简单可信的搜索服务。
如何作『低成本,高可用,少运维』呢?
咱们先来一块儿看一下总体架构,如图
首先说明一下咱们这两个框框表明两个机房,咱们总体就是一种多机房的架构,用来保证高可用
最上层是用户接入层,有 API,Kibana,Console 三种方式,用户和使用 ES 原生的 API 同样能够直接使用咱们的产品;
中间为路由层(Router),负责将用户请求真实发送到对应集群中,负责一些干预处理逻辑;
下面每一个机房中都有队列(Queue),负责削峰填谷和容灾多写。
每一个机房中有多个 ES 集群,用户的数据最终落在一个真实的集群中,或者一组对等的高可用集群中;
右边红色的是 Meta,负责全部组件的一站式自动化运维和元数据管理;
最下面是 kubernetes, 咱们全部的组件均是以容器跑在k8s上的,这解放了咱们不少物理机运维操做,使得滚动重启这些变得很是方便;
看完了总体,下面就逐点介绍下咱们是怎么作的,第一个目标是低成本。
在架构层面,成本优化是个每一年必谈的话题。那么下降成本是什么意思?实际上就是提升资源利用率。提升资源利用率方法有不少,好比提升压缩比,下降查询开销。可是在平台上作简单有效的方式则是多租户。
今天我就主要介绍下咱们的多租户方案:
多租户的关键就是租户隔离,租户隔离分为逻辑隔离和物理隔离。
首先介绍下咱们的逻辑隔离方案,逻辑隔离就是让业务仍是和以前本身搭 ES 同样的用法,也就是透明访问,可是实际上访问的只是真实集群中属于本身的一部分数据,而看不到其余人的数据,也就是保证水平权限。而 ES 有一点很适合用来作逻辑隔离,ES 的访问实际上都是按照 index 的。所以咱们逻辑隔离的问题就转化为如何让用户只能看到本身的表了。
咱们是经过 console 保存用户和表的映射关系,而后在访问时经过 router,也就是前面介绍的路由层进行干预,使得用户只能访问本身的 index。具体而言,咱们路由层采用 OpenResty+Lua 实现,将请求过程分为了右图的四步,Dispatch,Filter,Router,Reprocess
在 Dispatch 阶段咱们将请求结构化,抽出其用户,app,index,action 数据
而后进入 Filter 阶段,进行写过滤和改写,filter 又分为三步
Access 进行限流和验权这类的准入性拦截,
Action 对具体的操做进行拦截处理,好比说 DDL ,也就是建表,删表,修改结构这些操做,咱们将其转发到 Console 进行处理,一方面方便记录其 index 和 app 的对应信息,另外一方面因为建删表这种仍是很影响集群性能的,咱们经过转发给 console 能够对用户进行进一步限制,防止恶意行为对系统的影响。
Params 则是请求改写,在这一步咱们会根据具体的 index 和 action 进行相应的改写。好比去掉用户没有权限的 index,好比对于 kibana 索引将其改成用户本身的惟一 kibana 索引以实现 kibana 的多租户,好比对ES不一样版本的简单兼容。在这一步咱们能够作不少,不过须要注意的有两点,一是尽可能不要解析 body, 解 body是一种很是影响性能的行为,除了特殊的改写外应该尽力避免,好比 index 就应该让用户写在 url 上,并利用ES自己的参数关闭 body 中指定 index 的功能,这样改写速度能够快不少。 二是对于_all 和 getMapping 这种对全部 index 进行访问的,若是咱们替换为用户全部的索引会形成 url 过长,咱们采用的是建立一个和应用名同名的别名,而后将其改写成这个别名
进行完 Filter 就到了真实的 router 层,这一层就是根据 filter 的结果作真实的路由请求,多是转发到真实集群也能是转发到咱们其余的微服务中。
最后是 Reprocess ,这是拿到业务响应后的最终处理,咱们在这边会对一些结果进行改写,而且异步记录日志
上面这四步就是咱们路由层的大体逻辑,经过 app 和 index 的权限关系控制水平权限,经过 index 改写路由进行共享集群。
作完了逻辑隔离,咱们能够保证业务的水平权限了,那么是否就能够了呢?显然不是的,实际中不一样业务访问差别仍是很大的,只作逻辑隔离每每会形成业务间相互影响。这时候就须要物理隔离了。不过物理隔离咱们目前也没有找到很是好的方案,这边给你们分享下咱们的一些尝试
首当其冲,咱们采用的方法是服务分层,也就是将不一样用途,不一样重要性的业务分开,对于关键性的主链路业务甚至能够独占集群。对于其余的,咱们主要分为两类,写多查少的日志型和查多写少的检索型业务,按照其不一样的要求和流量预估将其分配在咱们预设的集群中。不过须要注意的是申报的和实际的总会有差别的,因此咱们还有按期巡检机制,会将已上线业务按照其真实流量进行集群迁移
作完了服务分层,咱们基本能够解决了低重要性业务影响高重要性业务的场景,可是在同级业务中依旧会有些业务由于好比说作营销活动这种形成突发流量。对于这种问题怎么办?
通常而言就是全局限流,可是因为咱们的访问都是长链接,因此限流并很差作。如右图所示,用户经过一个 LVS 访问了咱们多个 Router,而后咱们又经过了 LVS 访问了多个 ES 节点,咱们要作限流,也就是要保证全部 Router 上的令牌总数。通常而言全局限流有两种方案,一是以限流维度将全部请求打在同一实例上,也就是将同一表的全部访问打在一台机器上,可是在 ES 访问量这么高的场景下,这种并不合适,而且因为咱们前面已经有了一层lvs 作负载均衡,再作一层路由会显得过于复杂。
第二种方案就是均分令牌,可是因为长链接的问题,会形成有些节点早已被限流,可是其余节点却没有什么流量。
那么怎么办呢?
既然是令牌使用不均衡,那么咱们就让其分配也不均衡就行了呗。因此咱们采用了一种基于反馈的全局限流方案,什么叫基于反馈呢?就是咱们用巡检去定时去定时采集用量,用的多就多给一些,用的少就少给你一点。那么多给一些少给一点究竟是什么样的标准呢?这时咱们就须要决策单元来处理了,目前咱们采起的方案是简单的按比例分配。这边须要注意的一点是当有新机器接入时,不是一开始就达到终态的,而是渐进的过程。因此须要对这个收敛期设置一些策略,目前由于咱们机器性能比较好,不怕突发毛刺,因此咱们设置的是所有放行,到稳定后再进行限流。
这里说到长链接就顺便提一个 nginx 的小参数,keepalive_timeout, 用过 nginx 的同窗应该都见过,表示长链接超时时间,默认有75s,可是这个参数实际上还有一个可选配置,表示写在响应头里的超时时间,若是这个参数没写的话就会出如今服务端释放的瞬间客户端正好复用了这个链接,形成 Connection Reset或者 NoHttpResponse 的问题。出现频率不高,可是真实影响用户体验,由于随机低频出现,咱们以前一直觉得是客户端问题,后来才发现是原来是这个释放顺序的问题。
至此服务分层,全局限流都已经完成了,是否是能够睡个好觉了呢? 很遗憾,仍是不行,由于ES语法很是灵活,而且有许多大代价的操做,好比上千亿条数据作聚合,或者是用通配符作个中缀查询,写一个复杂的script都有可能形成拖垮咱们整个集群,那么对于这种状况怎么办呢? 咱们目前也是处于探索阶段,目前看比较有用的一种方式是过后补救,也就是咱们经过巡检去发现一些耗时大的 Task,而后对其应用的后继操做进行惩罚,好比降级,甚至熔断。这样就能够避免持续性的影响整个集群。可是一瞬间的rt上升仍是不可避免的,所以咱们也在尝试事前拦截,不过这个比较复杂,感兴趣的同窗能够一块儿线下交流一下
讲完了低成本,那么就来到了咱们第二个目标,高可用。
正如我以前提到那样,ES 自己其实提供了跨机房部署的方案,经过打标就能够进行跨机房部署,而后经过preference能够保证业务就近查询。我这里就再也不详细说了。可是这种方案须要两地三中心, 而咱们不少对外输出的场景出于成本考虑,并无三中心,只有两地两中心,所以双机房如何保证高可用就是咱们遇到的一个挑战。下面我主要就给你们分享下咱们基于对等多机房的高可用方案,咱们提供了两种类型,共三种方案分别适用于不一样的业务场景。
咱们有单写多读和多写多读两种类型
单写多读咱们采用的是跨集群复制的方案,经过修改 ES,咱们增长了利用 translog 将主集群数据推送给备的能力。就和 6.5 的 ccr 相似,可是咱们采用的是推模式,而不是拉模式,由于咱们以前作过测试,对于海量数据写入,推比拉的性能好了很多。容灾时进行主备互换,而后恢复后再补上在途数据。由上层来保证单写,多读和容灾切换逻辑。这种方案经过 ES 自己的t ranslog 同步,部署结构简单,数据也很准确,相似与数据库的备库,比较适合对写入 rt 没有太高要求的高可用场景。
多写多读,咱们提供了两种方案
第一种方案比较取巧,就是由于不少关键链路的业务场景都是从DB同步到搜索中的,所以咱们打通了数据通道,能够自动化的从DB写入到搜索,用户无需关心。那么对于这类用户的高可用,咱们采用的就是利用DB的高可用,搭建两条数据管道,分别写入不一样的集群。这样就能够实现高可用了,而且还能够绝对保证最终一致性。
第二种方案则是在对写入rt有强要求,有没有数据源的状况下,咱们会采用中间层的多写来实现高可用。咱们利用消息队列做为中间层,来实现双写。就是用户写的时候,写成功后保证队列也写成功了才返回成功,若是一个不成功就总体失败。而后由队列去保证推送到另外一个对等集群中。用外部版本号去保证一致性。可是因为中间层,对于Delete by Query的一致性保证就有些无能为力了。因此也仅适合特定的业务场景。
最后,在高可用上我还想说的一点是对于平台产品而言,技术方案有哪些,怎么实现的业务其实并不关心,业务关心的仅仅是他们能不能就近访问下降rt,和容灾时自动切换保证可用。所以咱们在平台上屏蔽了这些复杂的高可用类型和这些适用的场景,彻底交由咱们的后端去判断,让用户能够轻松自助接入。而且在交互上也将读写控制,容灾操做移到了咱们本身系统内,对用户无感知。只有用户能够这样透明拥有高可用能力了,咱们的平台才真正成为了高可用的搜索平台。
最后一个目标,少运维
今天运维的话题已经分享了不少了,我这边就不在赘述了。就简单介绍一下咱们在总体运维系统搭建过程当中沉淀出的四个原则。自包含,组件化,一站到底,自动化。
自包含ES作的就很不错了,一个jar就能够启动,而咱们的整套系统也都应该和单个ES同样,一条很简单的命令就能启动,没有什么外部依赖,这样就很好去输出。
组件化是指咱们每一个模块都应该能够插拔,来适应不一样的业务场景,好比有的不须要多租户,有的不须要削峰填谷。
一站究竟是指咱们的全部组件,router,queue,es,还有不少微服务的管控都应该在一个系统中去管控,万万不能一个组件一套本身的管控。
自动化就不说了,你们都懂
右边就是咱们的一个大盘页面,展示了router,es和queue的访问状况。固然,这是mock的数据
至此咱们已经拥有了一套『低成本,高可用,少运维』的 Elasticsearch 平台了,也解决了以前谈到的业务痛点,那么用户用的是否就爽了呢?咱们花了大半个月的时间,对咱们的业务进行了走访调研,发现业务虽然已经从运维中解放了出来,可是身上仍是有很多搜索的枷锁。
咱们主要分为两类用户,数据分析和全文检索的。
数据分析主要以为配置太复杂,他只是想导入一个日志数据,要学一堆的字段配置,并且好久才会用到一次,每次学完就忘,用到再重学,很耽误事情。其次,无关逻辑重,由于数据分析类的通常都是保留多天的数据,过时的数据就能够删除了,为了实现这一个功能,数据分析的同窗要写不少代码,还要控制不一样的别名,非常麻烦。
而全文检索类的同窗主要痛点有三个,一是分词配置复杂,二是难以修改字段,reindex 太复杂,还要本身先建立别名,再控制无缝切换。第三点是Debug艰难,虽然如今有 explain,可是用过的同窗应该都懂,想要总体梳理出具体的算分缘由仍是须要本身在脑中开辟很大的一块缓存的。对于不熟悉 ES 的同窗就太痛苦了。
整理一下,这些痛点归类起来就两个痛点,学习成本高和接口过于原子。
学习成本高和接口过于原子,虽然是业务的痛点,可是对ES自己而言却反而是优势,为何学习成本高呢?由于功能丰富。而为何接口原子呢,为了让上层能够灵活使用。
这些对于专家用户而言,很是不错,可是对于业务而言,的确非常麻烦。
所以咱们开始了咱们第二个阶段,搜索中台。什么叫中台呢,就是把一些通用的业务逻辑下移,来减小业务的逻辑,让业务专一于业务自己。
而为何业务不能作这些呢?固然也能作。可是俗话说『天下武功,惟快不破』,前台越轻,越能适应这变化极快的业务诉求。
所以咱们的搜索中台的主要目标就是两点:
一是下降业务学习成本,加快上手速度。咱们此次介绍的主要是如何下降对于配置类这种低频操做的学习成本;
二是抽象复杂逻辑来加速业务迭代,咱们此次主要会介绍抽象了哪两种业务逻辑。
下降学习成本,这个怎么作呢?众所周知,黑屏变白屏,也就是白屏化。可是不少的白屏化就是把命令放在了 web 上,回车变按钮。 这样真的能够下降用户学习成本么? 我想毋庸置疑,这样是不行的。
咱们在可视化上尝试了许多方案,也走了许多弯路,最后发现要想真正下降用户学习成本,须要把握三个要点
用户分层,区分出小白用户和专家用户,不要让专家用户的意见影响总体产品的极简设计,对于小白用户必定是越少越好,选择越少,路径越短,反馈越及时,效果越好。正如所谓的沉默的大多数,不少小白用户并不会去主动发声,只会随着复杂的配置而放弃使用。 下图就是咱们对于专家用户和小白用户在配置表结构时不一样的页面,对于专家用户,基本就是ES全部的功能可视化,加快使用速度。对于小白用户而言,则是彻底屏蔽这些功能点,让其能够直接使用。
引导式配置,引导式配置其实也就是加上限制,经过对用户的上一步输入决定下一步的可选。要避免一个页面打开一堆配置项,这样用户就会无从下手,更不要谈学习成本了。经过引导式配置来减小用户的选择,下降用户的记忆成本。限制不必定就意味着约束用户,合适的限制更能够下降用户的理解成本。好比右图就是咱们的一个分词器配置,很简单的引导,用户选择了中文字典后才能够选择相应的词典
第三点,深层次结构打平,什么叫深层次结构打平,就是指像如今的分词器,类似度这些都是在index级别下的,咱们将其抽象出来,变为全局的。用户能够自行建立全局的分词器,类似度,而且还能够共享给其余人,就像一个资源同样。而后在index中则是引用这个分词器。虽然这边作的仅仅是将分词器从index级别变为了全局,可是缺真正的减小了不少业务操做,由于在一个业务场景中,每每存在多张表,而多张表每每会使用同一套分词器。经过这种全局性的分词器用户仅需修改一处便可做用于全部位置。
好的,说完了白屏化的一些经验,这边给你们分享咱们对于复杂逻辑的抽象封装的两种新型表结构。这两种分别是数据分析类场景,咱们抽象出了日志型表,另外一种是全文检索类场景,咱们抽象出了别名型表。
日志型表的做用顾名思义就是存日志,也就是以前说的对于数据分析类业务,每每只保留几天,好比咱们如今有个业务场景,有张es的日志表,只想保留3天,因而咱们就给他按天建立索引,而后写入索引挂载到今天,查询索引挂载全部的,用router去自动改写别名,用户仍是传入es,可是执行写入操做时实际就是在es_write执行,查询就是在es_read执行。固然实际中咱们并非按天建的索引,咱们会利用Rollover建立不少的索引来保证海量写入下的速度。可是总体逻辑仍是和这个是同样的
而对于全文检索类场景,主要的痛点就是表结构的变动和分词器,字典类的变动,须要重建索引。因此咱们则抽象了一个叫别名表的表结构,用户建立一张表es,实际建立的是一个es的别名,咱们会把他和咱们真实的index一一对应上。这样利用这个别名,咱们就能够自动帮用户完成索引重建的操做。而索引重建,咱们有两种方式,一是用户配置了数据源的,咱们会直接从数据源进行重建,重建完成后直接切换。另外对于没有数据源,直接api写入的,目前咱们是利用了 ES 的 reindex 再配合咱们消息队列的消息回放实现的。具体而言,咱们就是首先提交 reindex,同时数据开始进 queue 转发,而后待 reindex 完成后,queue 再从 reindex 开始时进行回放,追平时切别名便可
总结
总结一下此次分享的内容,咱们首先构建了一个『低成本,高可用,少运维』的ES平台将业务从运维中解脱出来,而后又进一步构建了搜索中台,经过下降业务学习成本,下沉通用业务逻辑来加速业务迭代,赋能业务。 固然,这里介绍的搜索中台只是最基础的中台能力,咱们还在进一步探索些复杂场景下如何抽象来下降业务成本,也就是垂直化的搜索产品,有兴趣的同窗欢迎加入咱们,一同构建,让天下没有难用的搜索。咱们各种岗位持续招聘中,产品,设计,前端,后端,运维都欢迎加入。 邮箱: xinyu.jxy@antfin.com
完整 PPT 地址:
http://www.sofastack.tech/posts/2018-11-12-01
或点击文字底部“阅读全文”直接访问
长按关注,获取分布式架构干货
欢迎你们共同打造 SOFAStack https://github.com/alipay