你可以的,加油

发布一个轻量级的 Elasticsearch 压测工具 - Loadgen

你是否遇到过新搭建一个 Elasticsearch 集群,但是却无法评估该集群的最大吞吐是多少,或者使用一些压测工具,比如 esrally,需要花费很大力气准备,但是却无法压测到极限速度,服务器资源跑不满,或者测试产生的数据和实际的业务有很多出入,又或者测试的请求太简单,比如查询,就是对单个固定的搜索请求进行查询,不仅测不准还可能浪费时间没有参考意义,so,有没有一个简单的工具可以支持灵活的自定义压测,并且足够快,答案是 Loadgen。

Loadgen

Elasticsearch 压测工具 Loadgen ,由极限实验室出品,基于 Elasticsearch 的开发运维需求而开发,久经实际客户环境的真实考验,简单好用速度快。

一个没有经过压测的 Elasticsearch 不是一个完整的 Elasticsearch。

Loadgen 具有以下主要特点:

  • 性能强劲
  • 轻量级无依赖
  • 支持模板化参数随机
  • 支持高并发
  • 支持压测端均衡流量控制

只有模拟自己真实业务数据场景的压测才有意义,通过使用 Loadgen 定义写入文档或者查询模板,同时将里面的变量词典化,确保每次请求都是足够随机,变量可以灵活复用,支持多个请求混合压测,最大程度模拟真实环境。

Loadgen

Loadgen 使用非常简单,下载解压之后会得到两个文件,一个可执行程序和一个配置文件 loadgen.yml,配置文件样例如下:

variables:
  - name: ip
    type: file
    path: test/ip.txt
  - name: user
    type: file
    path: test/user.txt
  - name: id
    type: sequence
  - name: uuid
    type: uuid
  - name: now_local
    type: now_local
  - name: now_utc
    type: now_utc
  - name: now_unix
    type: now_unix
requests:
  - request:
      method: GET
      basic_auth:
        username: elastic
        password: pass
      url: http://localhost:8000/medcl/_search
      body: '{  "query": {"match": {    "name": "$[[user]]"  }}}'

变量的使用

上面的配置中,variables 用来定义变量参数,根据 name 来设置变量标识,在构造请求的使用 $[[变量名]] 即可访问该变量的值,变量目前支持的类型有:

类型 说明
file 文件型外部变量参数
sequence 自增数字类型的变量
range 数字范围类型的变量,支持参数 fromto 来限制范围
uuid UUID 字符类型的变量
now_local 当前时间、本地时区
now_utc 当前时间、UTC 时区
now_unix 当前时间、Unix 时间戳

file 类型变量参数加载自外部文本文件,每行一个变量参数,访问该变量时每次随机取其中一个,变量里面的定义格式举例如下:

➜  loadgen git:(master) ✗ cat test/user.txt 
medcl
elastic

请求的定义

配置节点 requests 用来设置 Loadgen 将依次执行的请求,支持固定参数的请求,也可支持模板变量参数化构造请求,以下是一个普通的查询请求:

requests:
  - request:
      method: GET
      basic_auth:
        username: elastic
        password: pass
      url: http://localhost:8000/medcl/_search?q=name:$[[user]]

上面的查询对 medcl 索引进行了查询,并对 name 字段执行一个查询,每次请求的值来自随机变量 user

命令行参数

Loadgen 会循环执行配置文件里面定义的请求,默认 Loadgen 只会运行 5s 就自动退出了,如果希望延长运行时间或者加大并发可以通过启动的时候设置参数来控制,通过查看帮助命令如下:

➜  loadgen git:(master) ✗ ./bin/loadgen --help
Usage of ./bin/loadgen:
  -c int
        Number of concurrent threads (default 1)
  -compress
        Compress requests with gzip
  -config string
        the location of config file, default: loadgen.yml (default "loadgen.yml")
  -d int
        Duration of tests in seconds (default 5)
  -debug
        run in debug mode, loadgen will quit with panic error
  -l int
        Limit total requests (default -1)
  -log string
        the log level,options:trace,debug,info,warn,error (default "info")
  -r int
        Max requests per second (fixed QPS) (default -1)
  -v    version

执行压测

执行 Loadgen 程序即可执行压测,如下:

➜  loadgen git:(master) ✗ ./bin/loadgen -d 30 -c 100 -compress
   __   ___  _      ___  ___   __    __
  / /  /___\/_\    /   \/ _ \ /__\/\ \ \
 / /  //  ///_\\  / /\ / /_\//_\ /  \/ /
/ /__/ \_//  _  \/ /_// /_\\//__/ /\  /
\____|___/\_/ \_/___,'\____/\__/\_\ \/

[LOADGEN] A http load generator and testing suit.
[LOADGEN] 1.0.0_SNAPSHOT, 83f2cb9, Sun Jul 4 13:52:42 2021 +0800, medcl, support single item in dict files
[07-19 16:15:00] [INF] [instance.go:24] workspace: data/loadgen/nodes/0
[07-19 16:15:00] [INF] [loader.go:312] warmup started
[07-19 16:15:00] [INF] [app.go:306] loadgen now started.
[07-19 16:15:00] [INF] [loader.go:316] [GET] http://localhost:8000/medcl/_search
[07-19 16:15:00] [INF] [loader.go:317] status: 200,<nil>,{"took":1,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":0,"relation":"eq"},"max_score":null,"hits":[]}}
[07-19 16:15:00] [INF] [loader.go:316] [GET] http://localhost:8000/medcl/_search?q=name:medcl
[07-19 16:15:00] [INF] [loader.go:317] status: 200,<nil>,{"took":1,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":0,"relation":"eq"},"max_score":null,"hits":[]}}
[07-19 16:15:01] [INF] [loader.go:316] [POST] http://localhost:8000/_bulk
[07-19 16:15:01] [INF] [loader.go:317] status: 200,<nil>,{"took":120,"errors":false,"items":[{"index":{"_index":"medcl-y4","_type":"doc","_id":"c3qj9123r0okahraiej0","_version":1,"result":"created","_shards":{"total":2,"successful":1,"failed":0},"_seq_no":5735852,"_primary_term":3,"status":201}}]}
[07-19 16:15:01] [INF] [loader.go:325] warmup finished

5253 requests in 32.756483336s, 524.61KB sent, 2.49MB received

[Loadgen Client Metrics]
Requests/sec:       175.10
Request Traffic/sec:    17.49KB
Total Transfer/sec: 102.34KB
Avg Req Time:       5.711022ms
Fastest Request:    440.448µs
Slowest Request:    3.624302658s
Number of Errors:   0
Number of Invalid:  0
Status 200:     5253

[Estimated Server Metrics]
Requests/sec:       160.37
Transfer/sec:       93.73KB
Avg Req Time:       623.576686ms

Loadgen 在正式压测之前会将所有的请求执行一次来进行预热,如果出现错误会提示是否继续,预热的请求结果也会输出到终端,执行完成之后会输出执行的摘要信息。

因为 Loadgen 最后的结果是所有请求全部执行完成之后的累计统计,可能存在不准的问题,建议通过打开 Kibana 或者 INFINI Console 的监控仪表板来实时查看 Elasticsearch 的各项运行指标。

模拟批量写入

使用 Loadgen 来模拟 bulk 批量写入也非常简单,在请求体里面配置一条索引操作,然后使用 body_repeat_times 参数来随机参数化复制若干条请求即可完成一批请求的准备,如下:

  - request:
      method: POST
      basic_auth:
        username: test
        password: testtest
      url: http://localhost:8000/_bulk
      body_repeat_times: 1000
      body: "{ \"index\" : { \"_index\" : \"medcl-y4\",\"_type\":\"doc\", \"_id\" : \"$[[uuid]]\" } }\n{ \"id\" : \"$[[id]]\",\"field1\" : \"$[[user]]\",\"ip\" : \"$[[ip]]\",\"now_local\" : \"$[[now_local]]\",\"now_unix\" : \"$[[now_unix]]\" }\n"

限制客户端压力

使用 Loadgen 并设置命令行参数 -r 可以限制客户端发送的每秒请求数,从而评估固定压力下 Elasticsearch 的响应时间和负载情况,如下:

➜  loadgen git:(master) ✗ ./bin/loadgen -d 30 -c 100 -r 100

注意,在大量并发下,此客户端吞吐限制可能不完全准确。

限制请求的总条数

通过设置参数 -l 可以控制客户端发送的请求总数,从而制造固定的文档,修改配置如下:

requests:
  - request:
      method: POST
      basic_auth:
        username: test
        password: testtest
      url: http://localhost:8000/medcl-test/doc2/_bulk
      body_repeat_times: 1
      body: "{ \"index\" : { \"_index\" : \"medcl-test\", \"_id\" : \"$[[uuid]]\" } }\n{ \"id\" : \"$[[id]]\",\"field1\" : \"$[[user]]\",\"ip\" : \"$[[ip]]\" }\n"

每次请求只有一个文档,然后执行 loadgen

./bin/loadgen -config loadgen-gw.yml -d 600 -c 100 -l 50000

执行完成之后,Elasticsearch 的索引 medcl-test 将增加 50000 条记录。

使用自增 ID 来确保文档的顺序性

如果希望生成的文档编号自增有规律,方便进行对比,可以使用 sequence 类型的自增 ID 来作为主键,内容也不要用随机数,如下:

requests:
  - request:
      method: POST
      basic_auth:
        username: test
        password: testtest
      url: http://localhost:8000/medcl-test/doc2/_bulk
      body_repeat_times: 1
      body: "{ \"index\" : { \"_index\" : \"medcl-test\", \"_id\" : \"$[[id]]\" } }\n{ \"id\" : \"$[[id]]\" }\n"

上下文复用变量

在一个请求中,我们可能希望有相同的参数出现,比如 routing 参数用来控制分片的路由,同时我们又希望该参数也保存在文档的 JSON 里面, 可以使用 runtime_variables 来设置请求级别的变量,或者 runtime_body_line_variables 定义请求体级别的变量,如果请求体复制 N 份,每份的参数是不同的,举例如下:

variables:
  - name: id
    type: sequence
  - name: uuid
    type: uuid
  - name: now_local
    type: now_local
  - name: now_utc
    type: now_utc
  - name: now_unix
    type: now_unix
  - name: suffix
    type: range
    from: 10
    to: 15
requests:
  - request:
      method: POST
      runtime_variables:
        batch_no: id
      runtime_body_line_variables:
        routing_no: uuid
      basic_auth:
        username: ingest
        password: password
      #url: http://localhost:8000/_search?q=$[[id]]
      url: http://192.168.3.188:9206/_bulk
      body_repeat_times: 10
      body: "{ \"create\" : { \"_index\" : \"test-$[[suffix]]\",\"_type\":\"doc\", \"_id\" : \"$[[uuid]]\" , \"routing\" : \"$[[routing_no]]\" } }\n{ \"id\" : \"$[[uuid]]\",\"routing_no\" : \"$[[routing_no]]\",\"batch_number\" : \"$[[batch_no]]\", \"random_no\" : \"$[[suffix]]\",\"ip\" : \"$[[ip]]\",\"now_local\" : \"$[[now_local]]\",\"now_unix\" : \"$[[now_unix]]\" }\n"

我们定义了 batch_no 变量来代表一批文档里面的相同批次号,同时又定义了 routing_no 变量来代表每个文档级别的 routing 值。

最后,欢迎大家反馈使用过程遇到的任何问题。

继续阅读 »

你是否遇到过新搭建一个 Elasticsearch 集群,但是却无法评估该集群的最大吞吐是多少,或者使用一些压测工具,比如 esrally,需要花费很大力气准备,但是却无法压测到极限速度,服务器资源跑不满,或者测试产生的数据和实际的业务有很多出入,又或者测试的请求太简单,比如查询,就是对单个固定的搜索请求进行查询,不仅测不准还可能浪费时间没有参考意义,so,有没有一个简单的工具可以支持灵活的自定义压测,并且足够快,答案是 Loadgen。

Loadgen

Elasticsearch 压测工具 Loadgen ,由极限实验室出品,基于 Elasticsearch 的开发运维需求而开发,久经实际客户环境的真实考验,简单好用速度快。

一个没有经过压测的 Elasticsearch 不是一个完整的 Elasticsearch。

Loadgen 具有以下主要特点:

  • 性能强劲
  • 轻量级无依赖
  • 支持模板化参数随机
  • 支持高并发
  • 支持压测端均衡流量控制

只有模拟自己真实业务数据场景的压测才有意义,通过使用 Loadgen 定义写入文档或者查询模板,同时将里面的变量词典化,确保每次请求都是足够随机,变量可以灵活复用,支持多个请求混合压测,最大程度模拟真实环境。

Loadgen

Loadgen 使用非常简单,下载解压之后会得到两个文件,一个可执行程序和一个配置文件 loadgen.yml,配置文件样例如下:

variables:
  - name: ip
    type: file
    path: test/ip.txt
  - name: user
    type: file
    path: test/user.txt
  - name: id
    type: sequence
  - name: uuid
    type: uuid
  - name: now_local
    type: now_local
  - name: now_utc
    type: now_utc
  - name: now_unix
    type: now_unix
requests:
  - request:
      method: GET
      basic_auth:
        username: elastic
        password: pass
      url: http://localhost:8000/medcl/_search
      body: '{  "query": {"match": {    "name": "$[[user]]"  }}}'

变量的使用

上面的配置中,variables 用来定义变量参数,根据 name 来设置变量标识,在构造请求的使用 $[[变量名]] 即可访问该变量的值,变量目前支持的类型有:

类型 说明
file 文件型外部变量参数
sequence 自增数字类型的变量
range 数字范围类型的变量,支持参数 fromto 来限制范围
uuid UUID 字符类型的变量
now_local 当前时间、本地时区
now_utc 当前时间、UTC 时区
now_unix 当前时间、Unix 时间戳

file 类型变量参数加载自外部文本文件,每行一个变量参数,访问该变量时每次随机取其中一个,变量里面的定义格式举例如下:

➜  loadgen git:(master) ✗ cat test/user.txt 
medcl
elastic

请求的定义

配置节点 requests 用来设置 Loadgen 将依次执行的请求,支持固定参数的请求,也可支持模板变量参数化构造请求,以下是一个普通的查询请求:

requests:
  - request:
      method: GET
      basic_auth:
        username: elastic
        password: pass
      url: http://localhost:8000/medcl/_search?q=name:$[[user]]

上面的查询对 medcl 索引进行了查询,并对 name 字段执行一个查询,每次请求的值来自随机变量 user

命令行参数

Loadgen 会循环执行配置文件里面定义的请求,默认 Loadgen 只会运行 5s 就自动退出了,如果希望延长运行时间或者加大并发可以通过启动的时候设置参数来控制,通过查看帮助命令如下:

➜  loadgen git:(master) ✗ ./bin/loadgen --help
Usage of ./bin/loadgen:
  -c int
        Number of concurrent threads (default 1)
  -compress
        Compress requests with gzip
  -config string
        the location of config file, default: loadgen.yml (default "loadgen.yml")
  -d int
        Duration of tests in seconds (default 5)
  -debug
        run in debug mode, loadgen will quit with panic error
  -l int
        Limit total requests (default -1)
  -log string
        the log level,options:trace,debug,info,warn,error (default "info")
  -r int
        Max requests per second (fixed QPS) (default -1)
  -v    version

执行压测

执行 Loadgen 程序即可执行压测,如下:

➜  loadgen git:(master) ✗ ./bin/loadgen -d 30 -c 100 -compress
   __   ___  _      ___  ___   __    __
  / /  /___\/_\    /   \/ _ \ /__\/\ \ \
 / /  //  ///_\\  / /\ / /_\//_\ /  \/ /
/ /__/ \_//  _  \/ /_// /_\\//__/ /\  /
\____|___/\_/ \_/___,'\____/\__/\_\ \/

[LOADGEN] A http load generator and testing suit.
[LOADGEN] 1.0.0_SNAPSHOT, 83f2cb9, Sun Jul 4 13:52:42 2021 +0800, medcl, support single item in dict files
[07-19 16:15:00] [INF] [instance.go:24] workspace: data/loadgen/nodes/0
[07-19 16:15:00] [INF] [loader.go:312] warmup started
[07-19 16:15:00] [INF] [app.go:306] loadgen now started.
[07-19 16:15:00] [INF] [loader.go:316] [GET] http://localhost:8000/medcl/_search
[07-19 16:15:00] [INF] [loader.go:317] status: 200,<nil>,{"took":1,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":0,"relation":"eq"},"max_score":null,"hits":[]}}
[07-19 16:15:00] [INF] [loader.go:316] [GET] http://localhost:8000/medcl/_search?q=name:medcl
[07-19 16:15:00] [INF] [loader.go:317] status: 200,<nil>,{"took":1,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":0,"relation":"eq"},"max_score":null,"hits":[]}}
[07-19 16:15:01] [INF] [loader.go:316] [POST] http://localhost:8000/_bulk
[07-19 16:15:01] [INF] [loader.go:317] status: 200,<nil>,{"took":120,"errors":false,"items":[{"index":{"_index":"medcl-y4","_type":"doc","_id":"c3qj9123r0okahraiej0","_version":1,"result":"created","_shards":{"total":2,"successful":1,"failed":0},"_seq_no":5735852,"_primary_term":3,"status":201}}]}
[07-19 16:15:01] [INF] [loader.go:325] warmup finished

5253 requests in 32.756483336s, 524.61KB sent, 2.49MB received

[Loadgen Client Metrics]
Requests/sec:       175.10
Request Traffic/sec:    17.49KB
Total Transfer/sec: 102.34KB
Avg Req Time:       5.711022ms
Fastest Request:    440.448µs
Slowest Request:    3.624302658s
Number of Errors:   0
Number of Invalid:  0
Status 200:     5253

[Estimated Server Metrics]
Requests/sec:       160.37
Transfer/sec:       93.73KB
Avg Req Time:       623.576686ms

Loadgen 在正式压测之前会将所有的请求执行一次来进行预热,如果出现错误会提示是否继续,预热的请求结果也会输出到终端,执行完成之后会输出执行的摘要信息。

因为 Loadgen 最后的结果是所有请求全部执行完成之后的累计统计,可能存在不准的问题,建议通过打开 Kibana 或者 INFINI Console 的监控仪表板来实时查看 Elasticsearch 的各项运行指标。

模拟批量写入

使用 Loadgen 来模拟 bulk 批量写入也非常简单,在请求体里面配置一条索引操作,然后使用 body_repeat_times 参数来随机参数化复制若干条请求即可完成一批请求的准备,如下:

  - request:
      method: POST
      basic_auth:
        username: test
        password: testtest
      url: http://localhost:8000/_bulk
      body_repeat_times: 1000
      body: "{ \"index\" : { \"_index\" : \"medcl-y4\",\"_type\":\"doc\", \"_id\" : \"$[[uuid]]\" } }\n{ \"id\" : \"$[[id]]\",\"field1\" : \"$[[user]]\",\"ip\" : \"$[[ip]]\",\"now_local\" : \"$[[now_local]]\",\"now_unix\" : \"$[[now_unix]]\" }\n"

限制客户端压力

使用 Loadgen 并设置命令行参数 -r 可以限制客户端发送的每秒请求数,从而评估固定压力下 Elasticsearch 的响应时间和负载情况,如下:

➜  loadgen git:(master) ✗ ./bin/loadgen -d 30 -c 100 -r 100

注意,在大量并发下,此客户端吞吐限制可能不完全准确。

限制请求的总条数

通过设置参数 -l 可以控制客户端发送的请求总数,从而制造固定的文档,修改配置如下:

requests:
  - request:
      method: POST
      basic_auth:
        username: test
        password: testtest
      url: http://localhost:8000/medcl-test/doc2/_bulk
      body_repeat_times: 1
      body: "{ \"index\" : { \"_index\" : \"medcl-test\", \"_id\" : \"$[[uuid]]\" } }\n{ \"id\" : \"$[[id]]\",\"field1\" : \"$[[user]]\",\"ip\" : \"$[[ip]]\" }\n"

每次请求只有一个文档,然后执行 loadgen

./bin/loadgen -config loadgen-gw.yml -d 600 -c 100 -l 50000

执行完成之后,Elasticsearch 的索引 medcl-test 将增加 50000 条记录。

使用自增 ID 来确保文档的顺序性

如果希望生成的文档编号自增有规律,方便进行对比,可以使用 sequence 类型的自增 ID 来作为主键,内容也不要用随机数,如下:

requests:
  - request:
      method: POST
      basic_auth:
        username: test
        password: testtest
      url: http://localhost:8000/medcl-test/doc2/_bulk
      body_repeat_times: 1
      body: "{ \"index\" : { \"_index\" : \"medcl-test\", \"_id\" : \"$[[id]]\" } }\n{ \"id\" : \"$[[id]]\" }\n"

上下文复用变量

在一个请求中,我们可能希望有相同的参数出现,比如 routing 参数用来控制分片的路由,同时我们又希望该参数也保存在文档的 JSON 里面, 可以使用 runtime_variables 来设置请求级别的变量,或者 runtime_body_line_variables 定义请求体级别的变量,如果请求体复制 N 份,每份的参数是不同的,举例如下:

variables:
  - name: id
    type: sequence
  - name: uuid
    type: uuid
  - name: now_local
    type: now_local
  - name: now_utc
    type: now_utc
  - name: now_unix
    type: now_unix
  - name: suffix
    type: range
    from: 10
    to: 15
requests:
  - request:
      method: POST
      runtime_variables:
        batch_no: id
      runtime_body_line_variables:
        routing_no: uuid
      basic_auth:
        username: ingest
        password: password
      #url: http://localhost:8000/_search?q=$[[id]]
      url: http://192.168.3.188:9206/_bulk
      body_repeat_times: 10
      body: "{ \"create\" : { \"_index\" : \"test-$[[suffix]]\",\"_type\":\"doc\", \"_id\" : \"$[[uuid]]\" , \"routing\" : \"$[[routing_no]]\" } }\n{ \"id\" : \"$[[uuid]]\",\"routing_no\" : \"$[[routing_no]]\",\"batch_number\" : \"$[[batch_no]]\", \"random_no\" : \"$[[suffix]]\",\"ip\" : \"$[[ip]]\",\"now_local\" : \"$[[now_local]]\",\"now_unix\" : \"$[[now_unix]]\" }\n"

我们定义了 batch_no 变量来代表一批文档里面的相同批次号,同时又定义了 routing_no 变量来代表每个文档级别的 routing 值。

最后,欢迎大家反馈使用过程遇到的任何问题。

收起阅读 »

在 Kibana 里统一访问来自不同集群的索引

在 Kibana 里统一访问来自不同集群的索引

现在有这么一个需求,客户根据需要将数据按照业务维度划分,将索引分别存放在了不同的三个集群, 将一个大集群拆分成多个小集群有很多好处,比如降低了耦合,带来了集群可用性和稳定性方面的好处,也避免了单个业务的热点访问造成其他业务的影响, 尽管拆分集群是很常见的玩法,但是管理起来不是那么方便了,尤其是在查询的时候,可能要分别访问三套集群各自的 API,甚至要切换三套不同的 Kibana 来访问集群的数据, 那么有没有办法将他们无缝的联合在一起呢?

极限网关!

答案自然是有的,通过将 Kibana 访问 Elasticsearch 的地址切换为极限网关的地址,我们可以将请求按照索引来进行智能的路由, 也就是当访问不同的业务索引时会智能的路由到不同的集群,如下图:

WechatIMG27.png

上图,我们分别有 3 个不同的索引:

  • apm-*
  • erp-*
  • mall-*

分别对应不同的三套 Elasticsearch 集群:

  • ES1-APM
  • ES2-ERP
  • ES3-MALL

接下来我们来看如何在极限网关里面进行相应的配置来满足这个业务需求。

配置集群信息

首先配置 3 个集群的连接信息。

elasticsearch:
  - name: es1-apm
    enabled: true
    endpoints:
     - http://192.168.3.188:9206
  - name: es2-erp
    enabled: true
    endpoints:
     - http://192.168.3.188:9207
  - name: es3-mall
    enabled: true
    endpoints:
     - http://192.168.3.188:9208

配置服务 Flow

然后,我们定义 3 个 Flow,分别对应用来访问 3 个不同的 Elasticsearch 集群,如下:

flow:
  - name: es1-flow
    filter:
      - elasticsearch:
          elasticsearch: es1-apm
  - name: es2-flow
    filter:
      - elasticsearch:
          elasticsearch: es2-erp
  - name: es3-flow
    filter:
      - elasticsearch:
          elasticsearch: es3-mall

然后再定义一个 flow 用来进行路径的判断和转发,如下:

  - name: default-flow
    filter:
      - switch:
          remove_prefix: false
          path_rules:
            - prefix: apm-
              flow: es1-flow
            - prefix: erp-
              flow: es2-flow
            - prefix: mall-
              flow: es3-flow
      - flow: #default flow
          flows:
            - es1-flow

根据请求路径里面的索引前缀来匹配不同的索引,并转发到不同的 Flow。

配置路由信息

接下来,我们定义路由信息,具体配置如下:

router:
  - name: my_router
    default_flow: default-flow

指向上面定义的默认 flow 来统一请求的处理。

定义服务及关联路由

最后,我们定义一个监听为 8000 端口的服务,用来提供给 Kibana 来进行统一的入口访问,如下:

entry:
  - name: es_entry
    enabled: true
    router: my_router
    max_concurrency: 10000
    network:
      binding: 0.0.0.0:8000

完整配置

最后的完整配置如下:

path.data: data
path.logs: log

entry:
  - name: es_entry
    enabled: true
    router: my_router
    max_concurrency: 10000
    network:
      binding: 0.0.0.0:8000

flow:
  - name: default-flow
    filter:
      - switch:
          remove_prefix: false
          path_rules:
            - prefix: apm-
              flow: es1-flow
            - prefix: erp-
              flow: es2-flow
            - prefix: mall-
              flow: es3-flow
      - flow: #default flow
          flows:
            - es1-flow
  - name: es1-flow
    filter:
      - elasticsearch:
          elasticsearch: es1-apm
  - name: es2-flow
    filter:
      - elasticsearch:
          elasticsearch: es2-erp
  - name: es3-flow
    filter:
      - elasticsearch:
          elasticsearch: es3-mall

router:
  - name: my_router
    default_flow: default-flow

elasticsearch:
  - name: es1-apm
    enabled: true
    endpoints:
     - http://192.168.3.188:9206
  - name: es2-erp
    enabled: true
    endpoints:
     - http://192.168.3.188:9207
  - name: es3-mall
    enabled: true
    endpoints:
     - http://192.168.3.188:9208

启动网关

直接启动网关,如下:

➜  gateway git:(master) ✗ ./bin/gateway -config sample-configs/elasticsearch-route-by-index.yml 

   ___   _   _____  __  __    __  _       
  / _ \ /_\ /__   \/__\/ / /\ \ \/_\ /\_/\
 / /_\///_\\  / /\/_\  \ \/  \/ //_\\\_ _/
/ /_\\/  _  \/ / //__   \  /\  /  _  \/ \ 
\____/\_/ \_/\/  \__/    \/  \/\_/ \_/\_/ 

[GATEWAY] A light-weight, powerful and high-performance elasticsearch gateway.
[GATEWAY] 1.0.0_SNAPSHOT, 2022-04-20 08:23:56, 2023-12-31 10:10:10, 51650a5c3d6aaa436f3c8a8828ea74894c3524b9
[04-21 13:41:21] [INF] [app.go:174] initializing gateway.
[04-21 13:41:21] [INF] [app.go:175] using config: /Users/medcl/go/src/infini.sh/gateway/sample-configs/elasticsearch-route-by-index.yml.
[04-21 13:41:21] [INF] [instance.go:72] workspace: /Users/medcl/go/src/infini.sh/gateway/data/gateway/nodes/c9bpg0ai4h931o4ngs3g
[04-21 13:41:21] [INF] [app.go:283] gateway is up and running now.
[04-21 13:41:21] [INF] [api.go:262] api listen at: http://0.0.0.0:2900
[04-21 13:41:21] [INF] [reverseproxy.go:255] elasticsearch [es1-apm] hosts: [] => [192.168.3.188:9206]
[04-21 13:41:21] [INF] [reverseproxy.go:255] elasticsearch [es2-erp] hosts: [] => [192.168.3.188:9207]
[04-21 13:41:21] [INF] [reverseproxy.go:255] elasticsearch [es3-mall] hosts: [] => [192.168.3.188:9208]
[04-21 13:41:21] [INF] [actions.go:349] elasticsearch [es2-erp] is available
[04-21 13:41:21] [INF] [actions.go:349] elasticsearch [es1-apm] is available
[04-21 13:41:21] [INF] [entry.go:312] entry [es_entry] listen at: http://0.0.0.0:8000
[04-21 13:41:21] [INF] [module.go:116] all modules are started
[04-21 13:41:21] [INF] [actions.go:349] elasticsearch [es3-mall] is available
[04-21 13:41:55] [INF] [reverseproxy.go:255] elasticsearch [es1-apm] hosts: [] => [192.168.3.188:9206]

网关启动成功之后,就可以通过网关的 IP+8000 端口来访问目标 Elasticsearch 集群了。

测试访问

首先通过 API 来访问测试一下,如下:

➜  ~ curl http://localhost:8000/apm-2022/_search -v
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET /apm-2022/_search HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.54.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Date: Thu, 21 Apr 2022 05:45:44 GMT
< content-type: application/json; charset=UTF-8
< Content-Length: 162
< X-elastic-product: Elasticsearch
< X-Backend-Cluster: es1-apm
< X-Backend-Server: 192.168.3.188:9206
< X-Filters: filters->elasticsearch
< 
* Connection #0 to host localhost left intact
{"took":142,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":0,"relation":"eq"},"max_score":null,"hits":[]}}%  

可以看到 apm-2022 指向了后端的 es1-apm 集群。

继续测试,erp 索引的访问,如下:

➜  ~ curl http://localhost:8000/erp-2022/_search -v 
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET /erp-2022/_search HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.54.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Date: Thu, 21 Apr 2022 06:24:46 GMT
< content-type: application/json; charset=UTF-8
< Content-Length: 161
< X-Backend-Cluster: es2-erp
< X-Backend-Server: 192.168.3.188:9207
< X-Filters: filters->switch->filters->elasticsearch->skipped
< 
* Connection #0 to host localhost left intact
{"took":12,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":0,"relation":"eq"},"max_score":null,"hits":[]}}%

继续测试,mall 索引的访问,如下:

➜  ~ curl http://localhost:8000/mall-2022/_search -v
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET /mall-2022/_search HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.54.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Date: Thu, 21 Apr 2022 06:25:08 GMT
< content-type: application/json; charset=UTF-8
< Content-Length: 134
< X-Backend-Cluster: es3-mall
< X-Backend-Server: 192.168.3.188:9208
< X-Filters: filters->switch->filters->elasticsearch->skipped
< 
* Connection #0 to host localhost left intact
{"took":8,"timed_out":false,"_shards":{"total":5,"successful":5,"skipped":0,"failed":0},"hits":{"total":0,"max_score":null,"hits":[]}}% 

完美转发。

修改 Kibana 配置

修改 Kibana 的配置文件: kibana.yml,替换 Elasticsearch 的地址为网关地址(http://192.168.3.200:8000),如下:

elasticsearch.hosts: ["http://192.168.3.200:8000"]

重启 Kibana 让配置生效。

效果如下

kibana-clusers-op.jpg

可以看到,在一个 Kibana 的开发者工具里面,我们已经可以像操作一个集群一样来同时读写实际上来自三个不同集群的索引数据了。

展望

通过极限网关,我们还可以非常灵活的进行在线请求的流量编辑,动态组合不同集群的操作。

继续阅读 »

在 Kibana 里统一访问来自不同集群的索引

现在有这么一个需求,客户根据需要将数据按照业务维度划分,将索引分别存放在了不同的三个集群, 将一个大集群拆分成多个小集群有很多好处,比如降低了耦合,带来了集群可用性和稳定性方面的好处,也避免了单个业务的热点访问造成其他业务的影响, 尽管拆分集群是很常见的玩法,但是管理起来不是那么方便了,尤其是在查询的时候,可能要分别访问三套集群各自的 API,甚至要切换三套不同的 Kibana 来访问集群的数据, 那么有没有办法将他们无缝的联合在一起呢?

极限网关!

答案自然是有的,通过将 Kibana 访问 Elasticsearch 的地址切换为极限网关的地址,我们可以将请求按照索引来进行智能的路由, 也就是当访问不同的业务索引时会智能的路由到不同的集群,如下图:

WechatIMG27.png

上图,我们分别有 3 个不同的索引:

  • apm-*
  • erp-*
  • mall-*

分别对应不同的三套 Elasticsearch 集群:

  • ES1-APM
  • ES2-ERP
  • ES3-MALL

接下来我们来看如何在极限网关里面进行相应的配置来满足这个业务需求。

配置集群信息

首先配置 3 个集群的连接信息。

elasticsearch:
  - name: es1-apm
    enabled: true
    endpoints:
     - http://192.168.3.188:9206
  - name: es2-erp
    enabled: true
    endpoints:
     - http://192.168.3.188:9207
  - name: es3-mall
    enabled: true
    endpoints:
     - http://192.168.3.188:9208

配置服务 Flow

然后,我们定义 3 个 Flow,分别对应用来访问 3 个不同的 Elasticsearch 集群,如下:

flow:
  - name: es1-flow
    filter:
      - elasticsearch:
          elasticsearch: es1-apm
  - name: es2-flow
    filter:
      - elasticsearch:
          elasticsearch: es2-erp
  - name: es3-flow
    filter:
      - elasticsearch:
          elasticsearch: es3-mall

然后再定义一个 flow 用来进行路径的判断和转发,如下:

  - name: default-flow
    filter:
      - switch:
          remove_prefix: false
          path_rules:
            - prefix: apm-
              flow: es1-flow
            - prefix: erp-
              flow: es2-flow
            - prefix: mall-
              flow: es3-flow
      - flow: #default flow
          flows:
            - es1-flow

根据请求路径里面的索引前缀来匹配不同的索引,并转发到不同的 Flow。

配置路由信息

接下来,我们定义路由信息,具体配置如下:

router:
  - name: my_router
    default_flow: default-flow

指向上面定义的默认 flow 来统一请求的处理。

定义服务及关联路由

最后,我们定义一个监听为 8000 端口的服务,用来提供给 Kibana 来进行统一的入口访问,如下:

entry:
  - name: es_entry
    enabled: true
    router: my_router
    max_concurrency: 10000
    network:
      binding: 0.0.0.0:8000

完整配置

最后的完整配置如下:

path.data: data
path.logs: log

entry:
  - name: es_entry
    enabled: true
    router: my_router
    max_concurrency: 10000
    network:
      binding: 0.0.0.0:8000

flow:
  - name: default-flow
    filter:
      - switch:
          remove_prefix: false
          path_rules:
            - prefix: apm-
              flow: es1-flow
            - prefix: erp-
              flow: es2-flow
            - prefix: mall-
              flow: es3-flow
      - flow: #default flow
          flows:
            - es1-flow
  - name: es1-flow
    filter:
      - elasticsearch:
          elasticsearch: es1-apm
  - name: es2-flow
    filter:
      - elasticsearch:
          elasticsearch: es2-erp
  - name: es3-flow
    filter:
      - elasticsearch:
          elasticsearch: es3-mall

router:
  - name: my_router
    default_flow: default-flow

elasticsearch:
  - name: es1-apm
    enabled: true
    endpoints:
     - http://192.168.3.188:9206
  - name: es2-erp
    enabled: true
    endpoints:
     - http://192.168.3.188:9207
  - name: es3-mall
    enabled: true
    endpoints:
     - http://192.168.3.188:9208

启动网关

直接启动网关,如下:

➜  gateway git:(master) ✗ ./bin/gateway -config sample-configs/elasticsearch-route-by-index.yml 

   ___   _   _____  __  __    __  _       
  / _ \ /_\ /__   \/__\/ / /\ \ \/_\ /\_/\
 / /_\///_\\  / /\/_\  \ \/  \/ //_\\\_ _/
/ /_\\/  _  \/ / //__   \  /\  /  _  \/ \ 
\____/\_/ \_/\/  \__/    \/  \/\_/ \_/\_/ 

[GATEWAY] A light-weight, powerful and high-performance elasticsearch gateway.
[GATEWAY] 1.0.0_SNAPSHOT, 2022-04-20 08:23:56, 2023-12-31 10:10:10, 51650a5c3d6aaa436f3c8a8828ea74894c3524b9
[04-21 13:41:21] [INF] [app.go:174] initializing gateway.
[04-21 13:41:21] [INF] [app.go:175] using config: /Users/medcl/go/src/infini.sh/gateway/sample-configs/elasticsearch-route-by-index.yml.
[04-21 13:41:21] [INF] [instance.go:72] workspace: /Users/medcl/go/src/infini.sh/gateway/data/gateway/nodes/c9bpg0ai4h931o4ngs3g
[04-21 13:41:21] [INF] [app.go:283] gateway is up and running now.
[04-21 13:41:21] [INF] [api.go:262] api listen at: http://0.0.0.0:2900
[04-21 13:41:21] [INF] [reverseproxy.go:255] elasticsearch [es1-apm] hosts: [] => [192.168.3.188:9206]
[04-21 13:41:21] [INF] [reverseproxy.go:255] elasticsearch [es2-erp] hosts: [] => [192.168.3.188:9207]
[04-21 13:41:21] [INF] [reverseproxy.go:255] elasticsearch [es3-mall] hosts: [] => [192.168.3.188:9208]
[04-21 13:41:21] [INF] [actions.go:349] elasticsearch [es2-erp] is available
[04-21 13:41:21] [INF] [actions.go:349] elasticsearch [es1-apm] is available
[04-21 13:41:21] [INF] [entry.go:312] entry [es_entry] listen at: http://0.0.0.0:8000
[04-21 13:41:21] [INF] [module.go:116] all modules are started
[04-21 13:41:21] [INF] [actions.go:349] elasticsearch [es3-mall] is available
[04-21 13:41:55] [INF] [reverseproxy.go:255] elasticsearch [es1-apm] hosts: [] => [192.168.3.188:9206]

网关启动成功之后,就可以通过网关的 IP+8000 端口来访问目标 Elasticsearch 集群了。

测试访问

首先通过 API 来访问测试一下,如下:

➜  ~ curl http://localhost:8000/apm-2022/_search -v
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET /apm-2022/_search HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.54.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Date: Thu, 21 Apr 2022 05:45:44 GMT
< content-type: application/json; charset=UTF-8
< Content-Length: 162
< X-elastic-product: Elasticsearch
< X-Backend-Cluster: es1-apm
< X-Backend-Server: 192.168.3.188:9206
< X-Filters: filters->elasticsearch
< 
* Connection #0 to host localhost left intact
{"took":142,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":0,"relation":"eq"},"max_score":null,"hits":[]}}%  

可以看到 apm-2022 指向了后端的 es1-apm 集群。

继续测试,erp 索引的访问,如下:

➜  ~ curl http://localhost:8000/erp-2022/_search -v 
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET /erp-2022/_search HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.54.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Date: Thu, 21 Apr 2022 06:24:46 GMT
< content-type: application/json; charset=UTF-8
< Content-Length: 161
< X-Backend-Cluster: es2-erp
< X-Backend-Server: 192.168.3.188:9207
< X-Filters: filters->switch->filters->elasticsearch->skipped
< 
* Connection #0 to host localhost left intact
{"took":12,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":0,"relation":"eq"},"max_score":null,"hits":[]}}%

继续测试,mall 索引的访问,如下:

➜  ~ curl http://localhost:8000/mall-2022/_search -v
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET /mall-2022/_search HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.54.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Date: Thu, 21 Apr 2022 06:25:08 GMT
< content-type: application/json; charset=UTF-8
< Content-Length: 134
< X-Backend-Cluster: es3-mall
< X-Backend-Server: 192.168.3.188:9208
< X-Filters: filters->switch->filters->elasticsearch->skipped
< 
* Connection #0 to host localhost left intact
{"took":8,"timed_out":false,"_shards":{"total":5,"successful":5,"skipped":0,"failed":0},"hits":{"total":0,"max_score":null,"hits":[]}}% 

完美转发。

修改 Kibana 配置

修改 Kibana 的配置文件: kibana.yml,替换 Elasticsearch 的地址为网关地址(http://192.168.3.200:8000),如下:

elasticsearch.hosts: ["http://192.168.3.200:8000"]

重启 Kibana 让配置生效。

效果如下

kibana-clusers-op.jpg

可以看到,在一个 Kibana 的开发者工具里面,我们已经可以像操作一个集群一样来同时读写实际上来自三个不同集群的索引数据了。

展望

通过极限网关,我们还可以非常灵活的进行在线请求的流量编辑,动态组合不同集群的操作。

收起阅读 »

在极限网关里面使用 JavaScript 脚本来进行复杂的查询改写

使用 JavaScript 脚本来进行复杂的查询改写

有这么一个需求:

网关里怎样对跨集群搜索进行支持的呢?我想实现: 输入的搜索请求是 lp:9200/index1/_search 这个索引在3个集群上,需要跨集群检索,也就是网关能否改成 lp:9200/cluster01:index1,cluster02,index1,cluster03:index1/_search 呢? 索引有一百多个,名称不一定是 app, 还可能多个索引一起的。

极限网关自带的过滤器 content_regex_replace 虽然可以实现字符正则替换,但是这个需求是带参数的变量替换,稍微复杂一点,没有办法直接用这个正则替换实现,有什么其他办法实现么?

使用脚本过滤器

当然有的,上面的这个需求,理论上我们只需要将其中的索引 index1 匹配之后,替换为 cluster01:index1,cluster02,index1,cluster03:index1 就行了。

答案就是使用自定义脚本来做,再复杂的业务逻辑都不是问题,都能通过自定义脚本来实现,一行脚本不行,那就两行。

使用极限网关提供的 JavaScript 过滤器可以很灵活的实现这个功能,具体继续看。

定义过滤器

首先创建一个脚本文件,放在网关数据目录的 scripts 子目录下面,如下:

➜  gateway ✗ tree data 
data
└── gateway
    └── nodes
        └── c9bpg0ai4h931o4ngs3g
            ├── kvdb
            ├── queue
            ├── scripts
            │   └── index_path_rewrite.js
            └── stats

这个脚本的内容如下:

function process(context) {
    var originalPath = context.Get("_ctx.request.path");
    var matches = originalPath.match(/\/?(.*?)\/_search/)
    var indexNames = [];
    if(matches && matches.length > 1) {
        indexNames = matches[1].split(",")
    }
    var resultNames = []
    var clusterNames = ["cluster01", "cluster02"]
    if(indexNames.length > 0) {
        for(var i=0; i<indexNames.length; i++){
            if(indexNames[i].length > 0) {
                for(var j=0; j<clusterNames.length; j++){
                    resultNames.push(clusterNames[j]+":"+indexNames[i])
                }
            }
        }
    }

    if (resultNames.length>0){
        var newPath="/"+resultNames.join(",")+"/_search";
        context.Put("_ctx.request.path",newPath);
    }
}

和普通的 JavaScript 一样,定义一个特定的函数 process 来处理请求里面的上下文信息,_ctx.request.path 是网关内置上下文的一个变量,用来获取请求的路径,通过 context.Get("_ctx.request.path") 在脚本里面进行访问。

中间我们使用了 JavaScript 的正则匹配和字符处理,做了一些字符拼接,得到新的路径 newPath 变量,最后使用 context.Put("_ctx.request.path",newPath) 更新网关请求的路径信息,从而实现查询条件里面的参数替换。

有关网关内置上下文的变量列表,请访问 Request Context

接下来,创建一个网关配置,并使用 javascript 过滤器调用该脚本,如下:

entry:
  - name: my_es_entry
    enabled: true
    router: my_router
    max_concurrency: 10000
    network:
      binding: 0.0.0.0:8000

flow:
  - name: default_flow
    filter:
      - dump:
          context:
            - _ctx.request.path
      - javascript:
          file: index_path_rewrite.js
      - dump:
          context:
          - _ctx.request.path
      - elasticsearch:
          elasticsearch: dev
router:
  - name: my_router
    default_flow: default_flow

elasticsearch:
- name: dev
  enabled: true
  schema: http
  hosts:
    - 192.168.3.188:9206

上面的例子中,使用了一个 javascript 过滤器,并且指定了加载的脚本文件为 index_path_rewrite.js,并使用了两个 dump 过滤器来输出脚本运行前后的路径信息,最后再使用一个 elasticsearch 过滤器来转发请求给 Elasticsearch 进行查询。

我们启动网关测试一下,如下:

➜  gateway ✗ ./bin/gateway
   ___   _   _____  __  __    __  _       
  / _ \ /_\ /__   \/__\/ / /\ \ \/_\ /\_/\
 / /_\///_\\  / /\/_\  \ \/  \/ //_\\\_ _/
/ /_\\/  _  \/ / //__   \  /\  /  _  \/ \ 
\____/\_/ \_/\/  \__/    \/  \/\_/ \_/\_/ 

[GATEWAY] A light-weight, powerful and high-performance elasticsearch gateway.
[GATEWAY] 1.0.0_SNAPSHOT, 2022-04-18 07:11:09, 2023-12-31 10:10:10, 8062c4bc6e57a3fefcce71c0628d2d4141e46953
[04-19 11:41:29] [INF] [app.go:174] initializing gateway.
[04-19 11:41:29] [INF] [app.go:175] using config: /Users/medcl/go/src/infini.sh/gateway/gateway.yml.
[04-19 11:41:29] [INF] [instance.go:72] workspace: /Users/medcl/go/src/infini.sh/gateway/data/gateway/nodes/c9bpg0ai4h931o4ngs3g
[04-19 11:41:29] [INF] [app.go:283] gateway is up and running now.
[04-19 11:41:30] [INF] [api.go:262] api listen at: http://0.0.0.0:2900
[04-19 11:41:30] [INF] [entry.go:312] entry [my_es_entry] listen at: http://0.0.0.0:8000
[04-19 11:41:30] [INF] [module.go:116] all modules are started
[04-19 11:41:30] [INF] [actions.go:349] elasticsearch [dev] is available

运行下面的查询来验证查询结果,如下:

curl localhost:8000/abc,efg/_search

可以看到网关通过 dump 过滤器输出的调试信息:

---- DUMPING CONTEXT ---- 
_ctx.request.path  :  /abc,efg/_search
---- DUMPING CONTEXT ---- 
_ctx.request.path  :  /cluster01:abc,cluster02:abc,cluster01:efg,cluster02:efg/_search

查询条件按照我们的需求进行了改写,Nice!

重写 DSL 查询语句

好吧,我们刚刚只是修改了查询的索引而已,那么查询请求的 DSL 呢?行不行?

那自然是可以的嘛,瞧下面的例子:

function process(context) {
    var originalDSL = context.Get("_ctx.request.body");
    if (originalDSL.length >0){
        var jsonObj=JSON.parse(originalDSL);
        jsonObj.size=123;
        jsonObj.aggs= {
            "test1": {
                "terms": {
                    "field": "abc",
                        "size": 10
                }
            }
        }
        context.Put("_ctx.request.body",JSON.stringify(jsonObj));
    }
}

先是获取查询请求,然后转换成 JSON 对象,之后任意修改查询对象就行了,保存回去,搞掂。

测试一下:

 curl -XPOST   localhost:8000/abc,efg/_search -d'{"query":{}}'

输出:

---- DUMPING CONTEXT ---- 
_ctx.request.path  :  /abc,efg/_search
_ctx.request.body  :  {"query":{}}
[04-19 18:14:24] [INF] [reverseproxy.go:255] elasticsearch [dev] hosts: [] => [192.168.3.188:9206]
---- DUMPING CONTEXT ---- 
_ctx.request.path  :  /abc,efg/_search
_ctx.request.body  :  {"query":{},"size":123,"aggs":{"test1":{"terms":{"field":"abc","size":10}}}}

是不是感觉解锁了新的世界?

结论

通过使用 Javascript 脚本过滤器,我们可以非常灵活的进行复杂逻辑的操作来满足我们的业务需求。

继续阅读 »

使用 JavaScript 脚本来进行复杂的查询改写

有这么一个需求:

网关里怎样对跨集群搜索进行支持的呢?我想实现: 输入的搜索请求是 lp:9200/index1/_search 这个索引在3个集群上,需要跨集群检索,也就是网关能否改成 lp:9200/cluster01:index1,cluster02,index1,cluster03:index1/_search 呢? 索引有一百多个,名称不一定是 app, 还可能多个索引一起的。

极限网关自带的过滤器 content_regex_replace 虽然可以实现字符正则替换,但是这个需求是带参数的变量替换,稍微复杂一点,没有办法直接用这个正则替换实现,有什么其他办法实现么?

使用脚本过滤器

当然有的,上面的这个需求,理论上我们只需要将其中的索引 index1 匹配之后,替换为 cluster01:index1,cluster02,index1,cluster03:index1 就行了。

答案就是使用自定义脚本来做,再复杂的业务逻辑都不是问题,都能通过自定义脚本来实现,一行脚本不行,那就两行。

使用极限网关提供的 JavaScript 过滤器可以很灵活的实现这个功能,具体继续看。

定义过滤器

首先创建一个脚本文件,放在网关数据目录的 scripts 子目录下面,如下:

➜  gateway ✗ tree data 
data
└── gateway
    └── nodes
        └── c9bpg0ai4h931o4ngs3g
            ├── kvdb
            ├── queue
            ├── scripts
            │   └── index_path_rewrite.js
            └── stats

这个脚本的内容如下:

function process(context) {
    var originalPath = context.Get("_ctx.request.path");
    var matches = originalPath.match(/\/?(.*?)\/_search/)
    var indexNames = [];
    if(matches && matches.length > 1) {
        indexNames = matches[1].split(",")
    }
    var resultNames = []
    var clusterNames = ["cluster01", "cluster02"]
    if(indexNames.length > 0) {
        for(var i=0; i<indexNames.length; i++){
            if(indexNames[i].length > 0) {
                for(var j=0; j<clusterNames.length; j++){
                    resultNames.push(clusterNames[j]+":"+indexNames[i])
                }
            }
        }
    }

    if (resultNames.length>0){
        var newPath="/"+resultNames.join(",")+"/_search";
        context.Put("_ctx.request.path",newPath);
    }
}

和普通的 JavaScript 一样,定义一个特定的函数 process 来处理请求里面的上下文信息,_ctx.request.path 是网关内置上下文的一个变量,用来获取请求的路径,通过 context.Get("_ctx.request.path") 在脚本里面进行访问。

中间我们使用了 JavaScript 的正则匹配和字符处理,做了一些字符拼接,得到新的路径 newPath 变量,最后使用 context.Put("_ctx.request.path",newPath) 更新网关请求的路径信息,从而实现查询条件里面的参数替换。

有关网关内置上下文的变量列表,请访问 Request Context

接下来,创建一个网关配置,并使用 javascript 过滤器调用该脚本,如下:

entry:
  - name: my_es_entry
    enabled: true
    router: my_router
    max_concurrency: 10000
    network:
      binding: 0.0.0.0:8000

flow:
  - name: default_flow
    filter:
      - dump:
          context:
            - _ctx.request.path
      - javascript:
          file: index_path_rewrite.js
      - dump:
          context:
          - _ctx.request.path
      - elasticsearch:
          elasticsearch: dev
router:
  - name: my_router
    default_flow: default_flow

elasticsearch:
- name: dev
  enabled: true
  schema: http
  hosts:
    - 192.168.3.188:9206

上面的例子中,使用了一个 javascript 过滤器,并且指定了加载的脚本文件为 index_path_rewrite.js,并使用了两个 dump 过滤器来输出脚本运行前后的路径信息,最后再使用一个 elasticsearch 过滤器来转发请求给 Elasticsearch 进行查询。

我们启动网关测试一下,如下:

➜  gateway ✗ ./bin/gateway
   ___   _   _____  __  __    __  _       
  / _ \ /_\ /__   \/__\/ / /\ \ \/_\ /\_/\
 / /_\///_\\  / /\/_\  \ \/  \/ //_\\\_ _/
/ /_\\/  _  \/ / //__   \  /\  /  _  \/ \ 
\____/\_/ \_/\/  \__/    \/  \/\_/ \_/\_/ 

[GATEWAY] A light-weight, powerful and high-performance elasticsearch gateway.
[GATEWAY] 1.0.0_SNAPSHOT, 2022-04-18 07:11:09, 2023-12-31 10:10:10, 8062c4bc6e57a3fefcce71c0628d2d4141e46953
[04-19 11:41:29] [INF] [app.go:174] initializing gateway.
[04-19 11:41:29] [INF] [app.go:175] using config: /Users/medcl/go/src/infini.sh/gateway/gateway.yml.
[04-19 11:41:29] [INF] [instance.go:72] workspace: /Users/medcl/go/src/infini.sh/gateway/data/gateway/nodes/c9bpg0ai4h931o4ngs3g
[04-19 11:41:29] [INF] [app.go:283] gateway is up and running now.
[04-19 11:41:30] [INF] [api.go:262] api listen at: http://0.0.0.0:2900
[04-19 11:41:30] [INF] [entry.go:312] entry [my_es_entry] listen at: http://0.0.0.0:8000
[04-19 11:41:30] [INF] [module.go:116] all modules are started
[04-19 11:41:30] [INF] [actions.go:349] elasticsearch [dev] is available

运行下面的查询来验证查询结果,如下:

curl localhost:8000/abc,efg/_search

可以看到网关通过 dump 过滤器输出的调试信息:

---- DUMPING CONTEXT ---- 
_ctx.request.path  :  /abc,efg/_search
---- DUMPING CONTEXT ---- 
_ctx.request.path  :  /cluster01:abc,cluster02:abc,cluster01:efg,cluster02:efg/_search

查询条件按照我们的需求进行了改写,Nice!

重写 DSL 查询语句

好吧,我们刚刚只是修改了查询的索引而已,那么查询请求的 DSL 呢?行不行?

那自然是可以的嘛,瞧下面的例子:

function process(context) {
    var originalDSL = context.Get("_ctx.request.body");
    if (originalDSL.length >0){
        var jsonObj=JSON.parse(originalDSL);
        jsonObj.size=123;
        jsonObj.aggs= {
            "test1": {
                "terms": {
                    "field": "abc",
                        "size": 10
                }
            }
        }
        context.Put("_ctx.request.body",JSON.stringify(jsonObj));
    }
}

先是获取查询请求,然后转换成 JSON 对象,之后任意修改查询对象就行了,保存回去,搞掂。

测试一下:

 curl -XPOST   localhost:8000/abc,efg/_search -d'{"query":{}}'

输出:

---- DUMPING CONTEXT ---- 
_ctx.request.path  :  /abc,efg/_search
_ctx.request.body  :  {"query":{}}
[04-19 18:14:24] [INF] [reverseproxy.go:255] elasticsearch [dev] hosts: [] => [192.168.3.188:9206]
---- DUMPING CONTEXT ---- 
_ctx.request.path  :  /abc,efg/_search
_ctx.request.body  :  {"query":{},"size":123,"aggs":{"test1":{"terms":{"field":"abc","size":10}}}}

是不是感觉解锁了新的世界?

结论

通过使用 Javascript 脚本过滤器,我们可以非常灵活的进行复杂逻辑的操作来满足我们的业务需求。

收起阅读 »

Elasticsearch 迁移工具 ESM 更新 0.4.4

timg.jpeg

 ESM 0.4.4 修复一堆 bug,支持几个新的特性:
  • 可用于生成测试数据,一般用于压力测试,基于源 ES 或者导入到本地的 JSON 数据,随机修改 ID,可以指定重复次数
  • 修复 Routing 参数在不同 ES 版本下的参数差异,支持 1.x\2.x\3.x\5.x 到 6.x\7.x 的相互导入
  • 修复终端下不能切换到后台执行的 bug,可以以 crontab 定时执行
  • 支持指定 _source 字段导出
  • 支持 _source 字段重命名
  • 支持文档 _type 重命名

 
下载地址:
 
https://github.com/medcl/esm/releases/tag/v0.4.4
 
 
生成测试数据示例:
 
1.直接以来源 es 的 my_index1 的索引数据生成到目标 es 集群的索引 my_index2,产生 10 份一样的数据
./bin/esm -s http://localhost:9201 -d http://localhost:9200 -x my_index1 -y 
my_index2
-n elastic:pass --regenerate_id --repeat_times=10

2.先导出索引文档到本地的文件 dump.json
./bin/esm -s http://localhost:9201 -x my_index1 -o dump.json
再基于这份样本,生成 10 份一样的数据到目标集群
./bin/esm -i dunp.json -d  http://localhost:9201 -y target-index1  --regenerate_id  --repeat_times=10 


更多使用示例参照项目 README
继续阅读 »
timg.jpeg

 ESM 0.4.4 修复一堆 bug,支持几个新的特性:
  • 可用于生成测试数据,一般用于压力测试,基于源 ES 或者导入到本地的 JSON 数据,随机修改 ID,可以指定重复次数
  • 修复 Routing 参数在不同 ES 版本下的参数差异,支持 1.x\2.x\3.x\5.x 到 6.x\7.x 的相互导入
  • 修复终端下不能切换到后台执行的 bug,可以以 crontab 定时执行
  • 支持指定 _source 字段导出
  • 支持 _source 字段重命名
  • 支持文档 _type 重命名

 
下载地址:
 
https://github.com/medcl/esm/releases/tag/v0.4.4
 
 
生成测试数据示例:
 
1.直接以来源 es 的 my_index1 的索引数据生成到目标 es 集群的索引 my_index2,产生 10 份一样的数据
./bin/esm -s http://localhost:9201 -d http://localhost:9200 -x my_index1 -y 
my_index2
-n elastic:pass --regenerate_id --repeat_times=10

2.先导出索引文档到本地的文件 dump.json
./bin/esm -s http://localhost:9201 -x my_index1 -o dump.json
再基于这份样本,生成 10 份一样的数据到目标集群
./bin/esm -i dunp.json -d  http://localhost:9201 -y target-index1  --regenerate_id  --repeat_times=10 


更多使用示例参照项目 README 收起阅读 »

【直播回放】ElasticTalk #12 Elasticsearch 数据建模

第12期 ElasticTalk 直播已经结束,我们主要分享了在 Elasticsearch 中进行数据建模设计,内容如下:
  • 数据建模是什么?

  • 数据建模的过程有哪些?
  • [size=20]Elasticsearch 数据建模要注意哪些?[/size]

如果你没来的及参加直播,来看下回放吧!
 

视频回放地址
 
ElasticTalk 是一个社区的 Webinar 活动,也欢迎感兴趣的伙伴加入,我们的目标如下:
1.研讨 Elastic Stack 的新功能、原理等,使更多的人以更直接的方式掌握其功能
2.锻炼参与者的选题、语言表达及沟通能力
 
目前是计划每周六下午 2:00 举行一场,形式主要以在线直播为主。
 
如果你对这个项目感兴趣,欢迎联系我!
 

招聘时间
我司在大力招聘 Elastic 技术专家,欢迎感兴趣的同学来投简历,地址如下:
http://www.elastictech.cn/jobs

 
想在 Elasticsearch 领域精进的同学,不妨来看看机会,作为 Elastic 官方战略级合作伙伴,我们的培训都是和 Elastic 官方同步的,机不可失!
而我们服务的客户遍布金融、证券、零售、制造业,充满挑战,感兴趣的你快来投递简历吧!
继续阅读 »
第12期 ElasticTalk 直播已经结束,我们主要分享了在 Elasticsearch 中进行数据建模设计,内容如下:
  • 数据建模是什么?

  • 数据建模的过程有哪些?
  • [size=20]Elasticsearch 数据建模要注意哪些?[/size]

如果你没来的及参加直播,来看下回放吧!
 

视频回放地址
 
ElasticTalk 是一个社区的 Webinar 活动,也欢迎感兴趣的伙伴加入,我们的目标如下:
1.研讨 Elastic Stack 的新功能、原理等,使更多的人以更直接的方式掌握其功能
2.锻炼参与者的选题、语言表达及沟通能力
 
目前是计划每周六下午 2:00 举行一场,形式主要以在线直播为主。
 
如果你对这个项目感兴趣,欢迎联系我!
 

招聘时间
我司在大力招聘 Elastic 技术专家,欢迎感兴趣的同学来投简历,地址如下:
http://www.elastictech.cn/jobs

 
想在 Elasticsearch 领域精进的同学,不妨来看看机会,作为 Elastic 官方战略级合作伙伴,我们的培训都是和 Elastic 官方同步的,机不可失!
而我们服务的客户遍布金融、证券、零售、制造业,充满挑战,感兴趣的你快来投递简历吧!
收起阅读 »

【直播回放】ElasticTalk #10 Kibana Canvas Intro

第10期 ElasticTalk 直播已经结束,我们主要分享了 Kibana Canvas,内容如下:
  • Canvas 是什么?
  • Canvas 与 Dashboard 的区别
  • Canvas 如何上手?

 
如果你没来的及参加直播,来看下回放吧!
 
视频回放
 
ElasticTalk 是一个社区的 Webinar 活动,也欢迎感兴趣的伙伴加入,我们的目标如下:
1.研讨 Elastic Stack 的新功能、原理等,使更多的人以更直接的方式掌握其功能
2.锻炼参与者的选题、语言表达及沟通能力
 
目前是计划每周六下午 2:00 举行一场,形式主要以在线直播为主。
 
如果你对这个项目感兴趣,欢迎联系我!
 

招聘时间
我司在大力招聘 Elastic 技术专家,欢迎感兴趣的同学来投简历,地址如下:
http://www.elastictech.cn/jobs
 
想在 Elasticsearch 领域精进的同学,不妨来看看机会,作为 Elastic 官方战略级合作伙伴我们的培训都是和 Elastic 官方同步的,机不可失!
而我们服务的客户遍布金融、证券、零售、制造业,充满挑战,感兴趣的你快来投递简历吧!
继续阅读 »
第10期 ElasticTalk 直播已经结束,我们主要分享了 Kibana Canvas,内容如下:
  • Canvas 是什么?
  • Canvas 与 Dashboard 的区别
  • Canvas 如何上手?

 
如果你没来的及参加直播,来看下回放吧!
 
视频回放
 
ElasticTalk 是一个社区的 Webinar 活动,也欢迎感兴趣的伙伴加入,我们的目标如下:
1.研讨 Elastic Stack 的新功能、原理等,使更多的人以更直接的方式掌握其功能
2.锻炼参与者的选题、语言表达及沟通能力
 
目前是计划每周六下午 2:00 举行一场,形式主要以在线直播为主。
 
如果你对这个项目感兴趣,欢迎联系我!
 

招聘时间
我司在大力招聘 Elastic 技术专家,欢迎感兴趣的同学来投简历,地址如下:
http://www.elastictech.cn/jobs
 
想在 Elasticsearch 领域精进的同学,不妨来看看机会,作为 Elastic 官方战略级合作伙伴我们的培训都是和 Elastic 官方同步的,机不可失!
而我们服务的客户遍布金融、证券、零售、制造业,充满挑战,感兴趣的你快来投递简历吧!
收起阅读 »

【直播报名】ElasticTalk #9 Elasticsearch 分词器那些事儿

直播时间:
4月20日(周六) 14:00~15:00

直播内容:
对于刚接触 ES 的新手来说,分词器是个坎儿,如何正确理解分词以及如何正确使用分词器,是很重要的一项基础功,这次我们就来聊聊分词器的那些事儿~

analysis-chain.png

直播方式: 
直播链接会在开始前发送到讨论群中。
点击下方网站链接,选择对应 Webinar 注册即可

Webinar 注册链接
继续阅读 »
直播时间:
4月20日(周六) 14:00~15:00

直播内容:
对于刚接触 ES 的新手来说,分词器是个坎儿,如何正确理解分词以及如何正确使用分词器,是很重要的一项基础功,这次我们就来聊聊分词器的那些事儿~

analysis-chain.png

直播方式: 
直播链接会在开始前发送到讨论群中。
点击下方网站链接,选择对应 Webinar 注册即可

Webinar 注册链接 收起阅读 »

ElasticTalk #5 冷热架构那些事儿 直播回放

第5期 ElasticTalk 直播已经结束,我们主要讨论了 Elasticsearch 冷热架构的相关只是,感兴趣的同学可去如下网址观看回放。
 
ElasticTalk #5 Hot-Warm Architecture
 
ElasticTalk 是一个社区的 Webinar 活动,也欢迎感兴趣的伙伴加入,我们的目标如下:
1.研讨 Elastic Stack 的新功能、原理等,使更多的人以更直接的方式掌握其功能
2.锻炼参与者的选题、语言表达及沟通能力
 
目前是计划每周六下午 2:00 举行一场,形式主要以在线直播为主。
 
如果你对这个项目感兴趣,欢迎联系我!
 
招聘时间
我司在大力招聘 Elastic 技术专家,欢迎感兴趣的同学来投简历,地址如下:
http://www.elastictech.cn/jobs
继续阅读 »
第5期 ElasticTalk 直播已经结束,我们主要讨论了 Elasticsearch 冷热架构的相关只是,感兴趣的同学可去如下网址观看回放。
 
ElasticTalk #5 Hot-Warm Architecture
 
ElasticTalk 是一个社区的 Webinar 活动,也欢迎感兴趣的伙伴加入,我们的目标如下:
1.研讨 Elastic Stack 的新功能、原理等,使更多的人以更直接的方式掌握其功能
2.锻炼参与者的选题、语言表达及沟通能力
 
目前是计划每周六下午 2:00 举行一场,形式主要以在线直播为主。
 
如果你对这个项目感兴趣,欢迎联系我!
 
招聘时间
我司在大力招聘 Elastic 技术专家,欢迎感兴趣的同学来投简历,地址如下:
http://www.elastictech.cn/jobs 收起阅读 »

ElasticTalk #4 ILM 索引生命周期管理直播回放

第4期 ElasticTalk 直播已经结束,我们主要讨论了 Elasticsearch 6.6 新增的 Index Lifecycle Management 功能,感兴趣的同学可去如下网址观看回放。
 
ElasticTalk #4 ILM
 
ElasticTalk 是一个社区的 Webinar 活动,也欢迎感兴趣的伙伴加入,我们的目标如下:
1.研讨 Elastic Stack 的新功能、原理等,使更多的人以更直接的方式掌握其功能
2.锻炼参与者的选题、语言表达及沟通能力
 
目前是计划每周六下午 2:00 举行一场,形式主要以在线直播为主。
 
如果你对这个项目感兴趣,欢迎联系我!
 
 
继续阅读 »
第4期 ElasticTalk 直播已经结束,我们主要讨论了 Elasticsearch 6.6 新增的 Index Lifecycle Management 功能,感兴趣的同学可去如下网址观看回放。
 
ElasticTalk #4 ILM
 
ElasticTalk 是一个社区的 Webinar 活动,也欢迎感兴趣的伙伴加入,我们的目标如下:
1.研讨 Elastic Stack 的新功能、原理等,使更多的人以更直接的方式掌握其功能
2.锻炼参与者的选题、语言表达及沟通能力
 
目前是计划每周六下午 2:00 举行一场,形式主要以在线直播为主。
 
如果你对这个项目感兴趣,欢迎联系我!
 
  收起阅读 »

【ElasticTalk 直播来袭】Elasticsearch 6.6 之 Index Lifecycle Management Practice

直播时间:

3月16日(本周六) 14:00~15:00

直播内容:

Elastic Stack 已经更新到 6.6 版本,该版本带来很多新功能,比如:
  • Index Lifecycle Management
  • Frozen Index
  • Geoshape based on Bkd Tree
  • SQL adds support for Date histograms
  • ......


在这些众多功能中,Index Lifecycle Management(索引生命周期管理,后文简称 ILM) 是最受社区欢迎的功能,我们此次研讨会将聚焦该功能,为大家讲解关于 ILM 的概念、原理及实践。

ilm_summary.png



直播方式: 待定(B站或者斗鱼)

直播链接会在开始前发送到讨论群中。
点击我报名吧!
继续阅读 »
直播时间:

3月16日(本周六) 14:00~15:00

直播内容:

Elastic Stack 已经更新到 6.6 版本,该版本带来很多新功能,比如:
  • Index Lifecycle Management
  • Frozen Index
  • Geoshape based on Bkd Tree
  • SQL adds support for Date histograms
  • ......


在这些众多功能中,Index Lifecycle Management(索引生命周期管理,后文简称 ILM) 是最受社区欢迎的功能,我们此次研讨会将聚焦该功能,为大家讲解关于 ILM 的概念、原理及实践。

ilm_summary.png



直播方式: 待定(B站或者斗鱼)

直播链接会在开始前发送到讨论群中。
点击我报名吧! 收起阅读 »

【直播预告】ElasticTalk #4 Elastic 官方认证考试那些事儿

大家好,ElasticTalk 第4次直播将于本周进行,主题是关于 Elastic 官方认证考试的。
 
我于7月初成功通过了 Elastic Certified Engineer 的考试,拿到下面的徽章。

bin_certificate.png

 
感兴趣的同学可以扫下面海报中的二维码或者搜索 elastic-talk 微信号,添加好友后进入直播群。
 

phone_post.001_.jpeg

 
继续阅读 »
大家好,ElasticTalk 第4次直播将于本周进行,主题是关于 Elastic 官方认证考试的。
 
我于7月初成功通过了 Elastic Certified Engineer 的考试,拿到下面的徽章。

bin_certificate.png

 
感兴趣的同学可以扫下面海报中的二维码或者搜索 elastic-talk 微信号,添加好友后进入直播群。
 

phone_post.001_.jpeg

  收起阅读 »

ElasticTalk #3 Elasticsearch压测实战 II esrally 进阶实战

ElasticTalk 第3期 直播的内容是 Elasticsearch 压测实战之 esrally 进阶实战。

本次我们主要讲解了 esrally 如何自定义测试集群、自定义数据集和报告,最后还讲了三步上手 esrally 的方法。
 
视频地址如下:
http://www.bilibili.com/video/av27117279/
继续阅读 »
ElasticTalk 第3期 直播的内容是 Elasticsearch 压测实战之 esrally 进阶实战。

本次我们主要讲解了 esrally 如何自定义测试集群、自定义数据集和报告,最后还讲了三步上手 esrally 的方法。
 
视频地址如下:
http://www.bilibili.com/video/av27117279/ 收起阅读 »

ElasticTalk #2 Elasticsearch压测实战 I esrally 入门与实战

 
ElasticTalk 第2期 直播的内容是 Elasticsearch 压测实战之 esrally 入门和实战。希望这次直播可以帮助大家快速掌握 esrally 这款优秀的 es 压测工具。
 
视频地址如下:
https://www.bilibili.com/video/av27114309/
继续阅读 »
 
ElasticTalk 第2期 直播的内容是 Elasticsearch 压测实战之 esrally 入门和实战。希望这次直播可以帮助大家快速掌握 esrally 这款优秀的 es 压测工具。
 
视频地址如下:
https://www.bilibili.com/video/av27114309/ 收起阅读 »

ElasticTalk #1 用 ElasticStack 快速收集和分析 Nginx 日志

 
去年做了3期 ElasticTalk 的直播节目,预计下周开始恢复。现在放出相关的视频内容,希望对大家有所帮助。
 
第1期的课程内容为用 ElasticStack 快速收集分析 Nginx 日志,其中详细讲解了如何使用 filebeat 的 module 功能。
 
 
视频地址如下:
https://www.bilibili.com/video/av27123368/
继续阅读 »
 
去年做了3期 ElasticTalk 的直播节目,预计下周开始恢复。现在放出相关的视频内容,希望对大家有所帮助。
 
第1期的课程内容为用 ElasticStack 快速收集分析 Nginx 日志,其中详细讲解了如何使用 filebeat 的 module 功能。
 
 
视频地址如下:
https://www.bilibili.com/video/av27123368/ 收起阅读 »

Elastic Certified Engineer 徽章到手!Elastic 官方认证考试

Elastic 官方在6月29日推出了认证服务,即 elastic certified engineer(官方网页),在7月9日我参加了认证考试,并顺利通过拿到了这枚官方认证的徽章。
 

elastic_certificate.png

 
考试还是有一定难度的,需要你对 elastic 的知识面有比较全面的掌握,由于是面向 engineer 的认证,所以只是在应用层面,不会考理论深度层面,感兴趣的同学可以根据官方的考纲重点来准备考试了,下周我会和大家再详细分享下该认证考试的方式和建议。
 
Good Luck!
继续阅读 »
Elastic 官方在6月29日推出了认证服务,即 elastic certified engineer(官方网页),在7月9日我参加了认证考试,并顺利通过拿到了这枚官方认证的徽章。
 

elastic_certificate.png

 
考试还是有一定难度的,需要你对 elastic 的知识面有比较全面的掌握,由于是面向 engineer 的认证,所以只是在应用层面,不会考理论深度层面,感兴趣的同学可以根据官方的考纲重点来准备考试了,下周我会和大家再详细分享下该认证考试的方式和建议。
 
Good Luck! 收起阅读 »