Q:非洲食人族的酋长吃什么?

人民在线招聘ES搜索研发工程师

 岗位描述
1、负责众云大数据平台Elasticsearch相关服务的功能设计、开发、运营和维护工作;
2、持续优化Elasticsearch的性能,完善功能,支持各业务线检索、聚合等场景;
3、负责众云事业部ELK平台的运营和维护工作;
岗位要求
1. 计算机相关专业全日制本科及以上学历,三年以上开发工作经验;
2. 熟练掌握java语言,熟练使用linux,强悍的编码和troubleshooting能力;
3. 深入了解Elasticsearch、Solr等开源搜索引擎,了解Lucene、Elasticsearch源码优先;
4. 精通搜索引擎架构原理、排序算法、索引处理及分词算法,索引数据结构;
5. 熟练掌握常见SQL数据库原理、数据库设计、查询编写和优化;
6. 有基础框架、中间件、基础库的开发经验优先;
7. 具有大型搜索引擎或舆情相关项目经验优先;
8. 对linux kernel、存储、文件系统、分布式任一方向有深入研究者优先;
9. 逻辑分析能力强,善于沟通,有良好的团队合作精神,良好的学习能力;
继续阅读 »
 岗位描述
1、负责众云大数据平台Elasticsearch相关服务的功能设计、开发、运营和维护工作;
2、持续优化Elasticsearch的性能,完善功能,支持各业务线检索、聚合等场景;
3、负责众云事业部ELK平台的运营和维护工作;
岗位要求
1. 计算机相关专业全日制本科及以上学历,三年以上开发工作经验;
2. 熟练掌握java语言,熟练使用linux,强悍的编码和troubleshooting能力;
3. 深入了解Elasticsearch、Solr等开源搜索引擎,了解Lucene、Elasticsearch源码优先;
4. 精通搜索引擎架构原理、排序算法、索引处理及分词算法,索引数据结构;
5. 熟练掌握常见SQL数据库原理、数据库设计、查询编写和优化;
6. 有基础框架、中间件、基础库的开发经验优先;
7. 具有大型搜索引擎或舆情相关项目经验优先;
8. 对linux kernel、存储、文件系统、分布式任一方向有深入研究者优先;
9. 逻辑分析能力强,善于沟通,有良好的团队合作精神,良好的学习能力; 收起阅读 »

社区日报 第515期 (2019-01-20)

1.Java应用日志导入ELK。
http://t.cn/E5GiA1T
2.使用Tokens分发Cassandra数据。
http://t.cn/E5GIOd5
3.(自备梯子)为什么如此难以让计算机像人一样说话?
http://t.cn/EqFOf04

编辑:至尊宝
归档:https://elasticsearch.cn/article/6337
订阅:https://tinyletter.com/elastic-daily
继续阅读 »
1.Java应用日志导入ELK。
http://t.cn/E5GiA1T
2.使用Tokens分发Cassandra数据。
http://t.cn/E5GIOd5
3.(自备梯子)为什么如此难以让计算机像人一样说话?
http://t.cn/EqFOf04

编辑:至尊宝
归档:https://elasticsearch.cn/article/6337
订阅:https://tinyletter.com/elastic-daily 收起阅读 »

社区日报 第514期 (2019-01-19)

  1. Flink 写入数据到 ElasticSearch。 http://t.cn/E5L88Q7

2.ES分布式一致性原则分析系列:节点、Meta、数据(需翻墙)。 http://t.cn/E5LGg4i

http://t.cn/E5LqqbD

http://t.cn/E5L57t0

  1. 一周热点:最近刷屏的《啥是佩奇》。 http://t.cn/E5vkFQc
继续阅读 »
  1. Flink 写入数据到 ElasticSearch。 http://t.cn/E5L88Q7

2.ES分布式一致性原则分析系列:节点、Meta、数据(需翻墙)。 http://t.cn/E5LGg4i

http://t.cn/E5LqqbD

http://t.cn/E5L57t0

  1. 一周热点:最近刷屏的《啥是佩奇》。 http://t.cn/E5vkFQc
收起阅读 »

如何修改kibana的默认主页

在6.0版本以前,登录kibana之后,默认会路由到app/kibana下的discover应用。 在6.3版本以后,新增了一个home路径/app/kibana#/home?_g=h@44136fa,访问根路径\会直接跳到以上路径。

希望在kibana上做更多定制化开发的同学,或许会有需求在登录kibana之后能够跳转到自己的页面。

要完成以上需求,只需要在kibana的配置文件里面增加一行:

server.defaultRoute: /app/system_portal

以上例子,我让kibana登录之后直接跳到我自己的app插件system_portal

配置默认路由的文件, src/server/http/get_default_route.js

import _ from 'lodash';

export default _.once(function (kbnServer) {
  const {
    config
  } = kbnServer;
  // 根目录basePath加上defaultRoute
  return `${config.get('server.basePath')}${config.get('server.defaultRoute')}`;
});

默认路由就是定义在server.defaultRoute中,默认值是app/kibana,可查看src/server/config/schema.js:

import Joi from 'joi';
import { constants as cryptoConstants } from 'crypto';
import os from 'os';

import { fromRoot } from '../../utils';
import { getData } from '../path';

export default async () => Joi.object({
  pkg: Joi.object({
    version: Joi.string().default(Joi.ref('$version')),
    branch: Joi.string().default(Joi.ref('$branch')),
    buildNum: Joi.number().default(Joi.ref('$buildNum')),
    buildSha: Joi.string().default(Joi.ref('$buildSha')),
  }).default(),

  env: Joi.object({
    name: Joi.string().default(Joi.ref('$env')),
    dev: Joi.boolean().default(Joi.ref('$dev')),
    prod: Joi.boolean().default(Joi.ref('$prod'))
  }).default(),

  dev: Joi.object({
    basePathProxyTarget: Joi.number().default(5603),
  }).default(),

  pid: Joi.object({
    file: Joi.string(),
    exclusive: Joi.boolean().default(false)
  }).default(),

  cpu: Joi.object({
    cgroup: Joi.object({
      path: Joi.object({
        override: Joi.string().default()
      })
    })
  }),

  cpuacct: Joi.object({
    cgroup: Joi.object({
      path: Joi.object({
        override: Joi.string().default()
      })
    })
  }),

  server: Joi.object({
    uuid: Joi.string().guid().default(),
    name: Joi.string().default(os.hostname()),
    host: Joi.string().hostname().default('localhost'),
    port: Joi.number().default(5601),
    maxPayloadBytes: Joi.number().default(1048576),
    autoListen: Joi.boolean().default(true),
    defaultRoute: Joi.string().default('/app/kibana').regex(/^\//, `start with a slash`),
    basePath: Joi.string().default('').allow('').regex(/(^$|^\/.*[^\/]$)/, `start with a slash, don't end with one`),
    rewriteBasePath: Joi.boolean().when('basePath', {
      is: '',
      then: Joi.default(false).valid(false),
      otherwise: Joi.default(false),
    }),
    customResponseHeaders: Joi.object().unknown(true).default({}),
    ssl: Joi.object({
      enabled: Joi.boolean().default(false),
      redirectHttpFromPort: Joi.number(),
      certificate: Joi.string().when('enabled', {
        is: true,
        then: Joi.required(),
      }),
      key: Joi.string().when('enabled', {
        is: true,
        then: Joi.required()
      }),
      keyPassphrase: Joi.string(),
      certificateAuthorities: Joi.array().single().items(Joi.string()).default(),
      supportedProtocols: Joi.array().items(Joi.string().valid('TLSv1', 'TLSv1.1', 'TLSv1.2')),
      cipherSuites: Joi.array().items(Joi.string()).default(cryptoConstants.defaultCoreCipherList.split(':'))
    }).default(),
    cors: Joi.when('$dev', {
      is: true,
      then: Joi.object().default({
        origin: ['*://localhost:9876'] // karma test server
      }),
      otherwise: Joi.boolean().default(false)
    }),
    xsrf: Joi.object({
      disableProtection: Joi.boolean().default(false),
      whitelist: Joi.array().items(
        Joi.string().regex(/^\//, 'start with a slash')
      ).default(),
      token: Joi.string().optional().notes('Deprecated')
    }).default(),
  }).default(),

  logging: Joi.object().keys({
    silent: Joi.boolean().default(false),

    quiet: Joi.boolean()
      .when('silent', {
        is: true,
        then: Joi.default(true).valid(true),
        otherwise: Joi.default(false)
      }),

    verbose: Joi.boolean()
      .when('quiet', {
        is: true,
        then: Joi.valid(false).default(false),
        otherwise: Joi.default(false)
      }),

    events: Joi.any().default({}),
    dest: Joi.string().default('stdout'),
    filter: Joi.any().default({}),
    json: Joi.boolean()
      .when('dest', {
        is: 'stdout',
        then: Joi.default(!process.stdout.isTTY),
        otherwise: Joi.default(true)
      }),

    useUTC: Joi.boolean().default(true),
  })
    .default(),

  ops: Joi.object({
    interval: Joi.number().default(5000),
  }).default(),

  plugins: Joi.object({
    paths: Joi.array().items(Joi.string()).default(),
    scanDirs: Joi.array().items(Joi.string()).default(),
    initialize: Joi.boolean().default(true)
  }).default(),

  path: Joi.object({
    data: Joi.string().default(getData())
  }).default(),

  optimize: Joi.object({
    enabled: Joi.boolean().default(true),
    bundleFilter: Joi.string().default('!tests'),
    bundleDir: Joi.string().default(fromRoot('optimize/bundles')),
    viewCaching: Joi.boolean().default(Joi.ref('$prod')),
    watch: Joi.boolean().default(false),
    watchPort: Joi.number().default(5602),
    watchHost: Joi.string().hostname().default('localhost'),
    watchPrebuild: Joi.boolean().default(false),
    watchProxyTimeout: Joi.number().default(5 * 60000),
    useBundleCache: Joi.boolean().default(Joi.ref('$prod')),
    unsafeCache: Joi.when('$prod', {
      is: true,
      then: Joi.boolean().valid(false),
      otherwise: Joi
        .alternatives()
        .try(
          Joi.boolean(),
          Joi.string().regex(/^\/.+\/$/)
        )
        .default(true),
    }),
    sourceMaps: Joi.when('$prod', {
      is: true,
      then: Joi.boolean().valid(false),
      otherwise: Joi
        .alternatives()
        .try(
          Joi.string().required(),
          Joi.boolean()
        )
        .default('#cheap-source-map'),
    }),
    profile: Joi.boolean().default(false)
  }).default(),
  status: Joi.object({
    allowAnonymous: Joi.boolean().default(false)
  }).default(),
  map: Joi.object({
    manifestServiceUrl: Joi.string().default(' https://catalogue.maps.elastic.co/v2/manifest'),
    emsLandingPageUrl: Joi.string().default('https://maps.elastic.co/v2'),
    includeElasticMapsService: Joi.boolean().default(true)
  }).default(),
  tilemap: Joi.object({
    url: Joi.string(),
    options: Joi.object({
      attribution: Joi.string(),
      minZoom: Joi.number().min(0, 'Must be 0 or higher').default(0),
      maxZoom: Joi.number().default(10),
      tileSize: Joi.number(),
      subdomains: Joi.array().items(Joi.string()).single(),
      errorTileUrl: Joi.string().uri(),
      tms: Joi.boolean(),
      reuseTiles: Joi.boolean(),
      bounds: Joi.array().items(Joi.array().items(Joi.number()).min(2).required()).min(2)
    }).default()
  }).default(),
  regionmap: Joi.object({
    includeElasticMapsService: Joi.boolean().default(true),
    layers: Joi.array().items(Joi.object({
      url: Joi.string(),
      format: Joi.object({
        type: Joi.string().default('geojson')
      }).default({
        type: 'geojson'
      }),
      meta: Joi.object({
        feature_collection_path: Joi.string().default('data')
      }).default({
        feature_collection_path: 'data'
      }),
      attribution: Joi.string(),
      name: Joi.string(),
      fields: Joi.array().items(Joi.object({
        name: Joi.string(),
        description: Joi.string()
      }))
    }))
  }).default(),

  i18n: Joi.object({
    defaultLocale: Joi.string().default('en'),
  }).default(),

  // This is a configuration node that is specifically handled by the config system
  // in the new platform, and that the current platform doesn't need to handle at all.
  __newPlatform: Joi.any(),

}).default();
继续阅读 »

在6.0版本以前,登录kibana之后,默认会路由到app/kibana下的discover应用。 在6.3版本以后,新增了一个home路径/app/kibana#/home?_g=h@44136fa,访问根路径\会直接跳到以上路径。

希望在kibana上做更多定制化开发的同学,或许会有需求在登录kibana之后能够跳转到自己的页面。

要完成以上需求,只需要在kibana的配置文件里面增加一行:

server.defaultRoute: /app/system_portal

以上例子,我让kibana登录之后直接跳到我自己的app插件system_portal

配置默认路由的文件, src/server/http/get_default_route.js

import _ from 'lodash';

export default _.once(function (kbnServer) {
  const {
    config
  } = kbnServer;
  // 根目录basePath加上defaultRoute
  return `${config.get('server.basePath')}${config.get('server.defaultRoute')}`;
});

默认路由就是定义在server.defaultRoute中,默认值是app/kibana,可查看src/server/config/schema.js:

import Joi from 'joi';
import { constants as cryptoConstants } from 'crypto';
import os from 'os';

import { fromRoot } from '../../utils';
import { getData } from '../path';

export default async () => Joi.object({
  pkg: Joi.object({
    version: Joi.string().default(Joi.ref('$version')),
    branch: Joi.string().default(Joi.ref('$branch')),
    buildNum: Joi.number().default(Joi.ref('$buildNum')),
    buildSha: Joi.string().default(Joi.ref('$buildSha')),
  }).default(),

  env: Joi.object({
    name: Joi.string().default(Joi.ref('$env')),
    dev: Joi.boolean().default(Joi.ref('$dev')),
    prod: Joi.boolean().default(Joi.ref('$prod'))
  }).default(),

  dev: Joi.object({
    basePathProxyTarget: Joi.number().default(5603),
  }).default(),

  pid: Joi.object({
    file: Joi.string(),
    exclusive: Joi.boolean().default(false)
  }).default(),

  cpu: Joi.object({
    cgroup: Joi.object({
      path: Joi.object({
        override: Joi.string().default()
      })
    })
  }),

  cpuacct: Joi.object({
    cgroup: Joi.object({
      path: Joi.object({
        override: Joi.string().default()
      })
    })
  }),

  server: Joi.object({
    uuid: Joi.string().guid().default(),
    name: Joi.string().default(os.hostname()),
    host: Joi.string().hostname().default('localhost'),
    port: Joi.number().default(5601),
    maxPayloadBytes: Joi.number().default(1048576),
    autoListen: Joi.boolean().default(true),
    defaultRoute: Joi.string().default('/app/kibana').regex(/^\//, `start with a slash`),
    basePath: Joi.string().default('').allow('').regex(/(^$|^\/.*[^\/]$)/, `start with a slash, don't end with one`),
    rewriteBasePath: Joi.boolean().when('basePath', {
      is: '',
      then: Joi.default(false).valid(false),
      otherwise: Joi.default(false),
    }),
    customResponseHeaders: Joi.object().unknown(true).default({}),
    ssl: Joi.object({
      enabled: Joi.boolean().default(false),
      redirectHttpFromPort: Joi.number(),
      certificate: Joi.string().when('enabled', {
        is: true,
        then: Joi.required(),
      }),
      key: Joi.string().when('enabled', {
        is: true,
        then: Joi.required()
      }),
      keyPassphrase: Joi.string(),
      certificateAuthorities: Joi.array().single().items(Joi.string()).default(),
      supportedProtocols: Joi.array().items(Joi.string().valid('TLSv1', 'TLSv1.1', 'TLSv1.2')),
      cipherSuites: Joi.array().items(Joi.string()).default(cryptoConstants.defaultCoreCipherList.split(':'))
    }).default(),
    cors: Joi.when('$dev', {
      is: true,
      then: Joi.object().default({
        origin: ['*://localhost:9876'] // karma test server
      }),
      otherwise: Joi.boolean().default(false)
    }),
    xsrf: Joi.object({
      disableProtection: Joi.boolean().default(false),
      whitelist: Joi.array().items(
        Joi.string().regex(/^\//, 'start with a slash')
      ).default(),
      token: Joi.string().optional().notes('Deprecated')
    }).default(),
  }).default(),

  logging: Joi.object().keys({
    silent: Joi.boolean().default(false),

    quiet: Joi.boolean()
      .when('silent', {
        is: true,
        then: Joi.default(true).valid(true),
        otherwise: Joi.default(false)
      }),

    verbose: Joi.boolean()
      .when('quiet', {
        is: true,
        then: Joi.valid(false).default(false),
        otherwise: Joi.default(false)
      }),

    events: Joi.any().default({}),
    dest: Joi.string().default('stdout'),
    filter: Joi.any().default({}),
    json: Joi.boolean()
      .when('dest', {
        is: 'stdout',
        then: Joi.default(!process.stdout.isTTY),
        otherwise: Joi.default(true)
      }),

    useUTC: Joi.boolean().default(true),
  })
    .default(),

  ops: Joi.object({
    interval: Joi.number().default(5000),
  }).default(),

  plugins: Joi.object({
    paths: Joi.array().items(Joi.string()).default(),
    scanDirs: Joi.array().items(Joi.string()).default(),
    initialize: Joi.boolean().default(true)
  }).default(),

  path: Joi.object({
    data: Joi.string().default(getData())
  }).default(),

  optimize: Joi.object({
    enabled: Joi.boolean().default(true),
    bundleFilter: Joi.string().default('!tests'),
    bundleDir: Joi.string().default(fromRoot('optimize/bundles')),
    viewCaching: Joi.boolean().default(Joi.ref('$prod')),
    watch: Joi.boolean().default(false),
    watchPort: Joi.number().default(5602),
    watchHost: Joi.string().hostname().default('localhost'),
    watchPrebuild: Joi.boolean().default(false),
    watchProxyTimeout: Joi.number().default(5 * 60000),
    useBundleCache: Joi.boolean().default(Joi.ref('$prod')),
    unsafeCache: Joi.when('$prod', {
      is: true,
      then: Joi.boolean().valid(false),
      otherwise: Joi
        .alternatives()
        .try(
          Joi.boolean(),
          Joi.string().regex(/^\/.+\/$/)
        )
        .default(true),
    }),
    sourceMaps: Joi.when('$prod', {
      is: true,
      then: Joi.boolean().valid(false),
      otherwise: Joi
        .alternatives()
        .try(
          Joi.string().required(),
          Joi.boolean()
        )
        .default('#cheap-source-map'),
    }),
    profile: Joi.boolean().default(false)
  }).default(),
  status: Joi.object({
    allowAnonymous: Joi.boolean().default(false)
  }).default(),
  map: Joi.object({
    manifestServiceUrl: Joi.string().default(' https://catalogue.maps.elastic.co/v2/manifest'),
    emsLandingPageUrl: Joi.string().default('https://maps.elastic.co/v2'),
    includeElasticMapsService: Joi.boolean().default(true)
  }).default(),
  tilemap: Joi.object({
    url: Joi.string(),
    options: Joi.object({
      attribution: Joi.string(),
      minZoom: Joi.number().min(0, 'Must be 0 or higher').default(0),
      maxZoom: Joi.number().default(10),
      tileSize: Joi.number(),
      subdomains: Joi.array().items(Joi.string()).single(),
      errorTileUrl: Joi.string().uri(),
      tms: Joi.boolean(),
      reuseTiles: Joi.boolean(),
      bounds: Joi.array().items(Joi.array().items(Joi.number()).min(2).required()).min(2)
    }).default()
  }).default(),
  regionmap: Joi.object({
    includeElasticMapsService: Joi.boolean().default(true),
    layers: Joi.array().items(Joi.object({
      url: Joi.string(),
      format: Joi.object({
        type: Joi.string().default('geojson')
      }).default({
        type: 'geojson'
      }),
      meta: Joi.object({
        feature_collection_path: Joi.string().default('data')
      }).default({
        feature_collection_path: 'data'
      }),
      attribution: Joi.string(),
      name: Joi.string(),
      fields: Joi.array().items(Joi.object({
        name: Joi.string(),
        description: Joi.string()
      }))
    }))
  }).default(),

  i18n: Joi.object({
    defaultLocale: Joi.string().default('en'),
  }).default(),

  // This is a configuration node that is specifically handled by the config system
  // in the new platform, and that the current platform doesn't need to handle at all.
  __newPlatform: Joi.any(),

}).default();
收起阅读 »

社区日报 第513期(2019-1-18)

1、使用rally对Elasticsearch进行基准测试
http://t.cn/E5vfSSq
2、Django Elasticsearch检索解读
http://t.cn/E5vxFXe
3、基于Vert.X框架的Elasticsearch客户端
http://t.cn/E5vJbG3

编辑:铭毅天下
归档:https://elasticsearch.cn/article/6334
订阅:https://tinyletter.com/elastic-daily
继续阅读 »
1、使用rally对Elasticsearch进行基准测试
http://t.cn/E5vfSSq
2、Django Elasticsearch检索解读
http://t.cn/E5vxFXe
3、基于Vert.X框架的Elasticsearch客户端
http://t.cn/E5vJbG3

编辑:铭毅天下
归档:https://elasticsearch.cn/article/6334
订阅:https://tinyletter.com/elastic-daily 收起阅读 »

社区日报 第512期 (2019-01-17)

1.使用Logstash拆分数据并将其发送到多个输出
http://t.cn/EquNcDU
2.Logan:美团点评的开源移动端基础日志库
http://t.cn/EquNpVL
3.praeco:Elasticsearch报警工具
http://t.cn/EAgg8WQ

编辑:金桥
归档:https://elasticsearch.cn/article/6333
订阅:https://tinyletter.com/elastic-daily
继续阅读 »
1.使用Logstash拆分数据并将其发送到多个输出
http://t.cn/EquNcDU
2.Logan:美团点评的开源移动端基础日志库
http://t.cn/EquNpVL
3.praeco:Elasticsearch报警工具
http://t.cn/EAgg8WQ

编辑:金桥
归档:https://elasticsearch.cn/article/6333
订阅:https://tinyletter.com/elastic-daily 收起阅读 »

社区日报 第511期 (2019-01-16)

1.实时日志流系统
http://t.cn/Eqn8swf
2.Lucene索引结构漫谈
http://t.cn/Re1Dp6g
3.Elasticsearch让你的搜索引擎全面发展
http://t.cn/EqnEwAs
 
编辑:江水
归档:https://elasticsearch.cn/article/6332
订阅:https://tinyletter.com/elastic-daily
继续阅读 »
1.实时日志流系统
http://t.cn/Eqn8swf
2.Lucene索引结构漫谈
http://t.cn/Re1Dp6g
3.Elasticsearch让你的搜索引擎全面发展
http://t.cn/EqnEwAs
 
编辑:江水
归档:https://elasticsearch.cn/article/6332
订阅:https://tinyletter.com/elastic-daily 收起阅读 »

社区日报 第510期 (2019-01-15)

1、几个常用的Elasticsearch Management GUI推荐。
http://t.cn/Eql0exu
2、(自备梯子)Dejavu 3.0: 一个你需要的Elasticsearch Web UI。
http://t.cn/EqlOPrD
3、从Lucene到Elasticsearch:全文检索实战。
http://t.cn/EqlOLdL

编辑:叮咚光军
归档:https://elasticsearch.cn/article/6331
订阅:https://tinyletter.com/elastic-daily
继续阅读 »
1、几个常用的Elasticsearch Management GUI推荐。
http://t.cn/Eql0exu
2、(自备梯子)Dejavu 3.0: 一个你需要的Elasticsearch Web UI。
http://t.cn/EqlOPrD
3、从Lucene到Elasticsearch:全文检索实战。
http://t.cn/EqlOLdL

编辑:叮咚光军
归档:https://elasticsearch.cn/article/6331
订阅:https://tinyletter.com/elastic-daily 收起阅读 »

社区日报 第509期 (2019-01-14)

1.ElasticSearch架构反向思路
http://t.cn/Eq9VgsV

2.在kibana中集中管理rollup任务
http://t.cn/EqtKAf7

3.在es崩溃以后自动重启
http://t.cn/Eq9IJFG

编辑:cyberdak
归档:https://elasticsearch.cn/article/6330
订阅:https://tinyletter.com/elastic-daily
 
继续阅读 »
1.ElasticSearch架构反向思路
http://t.cn/Eq9VgsV

2.在kibana中集中管理rollup任务
http://t.cn/EqtKAf7

3.在es崩溃以后自动重启
http://t.cn/Eq9IJFG

编辑:cyberdak
归档:https://elasticsearch.cn/article/6330
订阅:https://tinyletter.com/elastic-daily
  收起阅读 »

社区日报 第508期 (2019-01-13)

1.创建DSL。
http://t.cn/EqcmScm
2.使用ANTLR从GSA迁移到Elasticsearch。
http://t.cn/Eqc3j1w
3.(自备梯子)将定义2019年的技术。
http://t.cn/EqUQ61i

编辑:至尊宝
归档:https://elasticsearch.cn/article/6329
订阅:https://tinyletter.com/elastic-daily
继续阅读 »
1.创建DSL。
http://t.cn/EqcmScm
2.使用ANTLR从GSA迁移到Elasticsearch。
http://t.cn/Eqc3j1w
3.(自备梯子)将定义2019年的技术。
http://t.cn/EqUQ61i

编辑:至尊宝
归档:https://elasticsearch.cn/article/6329
订阅:https://tinyletter.com/elastic-daily 收起阅读 »

社区日报 第507期 (2019-01-12)

1、在kibana里创建、管理、可视化汇总数据 http://t.cn/EqtKAf7

2、在Spring Boot使用ES教程 http://t.cn/Eqt9rC4

3、一周热点:GitHub提供免费私有Repo http://t.cn/EGmbUQX

继续阅读 »

1、在kibana里创建、管理、可视化汇总数据 http://t.cn/EqtKAf7

2、在Spring Boot使用ES教程 http://t.cn/Eqt9rC4

3、一周热点:GitHub提供免费私有Repo http://t.cn/EGmbUQX

收起阅读 »

社区日报 第506期(2019-1-11)

1、在Spring Boot 2.0中使用ElasticSearch
http://t.cn/EqLtnXp
2、Elasticsearch索引管理利器——Curator深入详解
http://t.cn/EqL9Tdq
3、ES分片分配策略
http://t.cn/EqLc7XK

编辑:铭毅天下
归档:https://elasticsearch.cn/article/6327
订阅:https://tinyletter.com/elastic-daily
 
继续阅读 »
1、在Spring Boot 2.0中使用ElasticSearch
http://t.cn/EqLtnXp
2、Elasticsearch索引管理利器——Curator深入详解
http://t.cn/EqL9Tdq
3、ES分片分配策略
http://t.cn/EqLc7XK

编辑:铭毅天下
归档:https://elasticsearch.cn/article/6327
订阅:https://tinyletter.com/elastic-daily
  收起阅读 »

如何让kibana零等待时间升级插件(前后端分离的部署)

正如官方文档所自豪宣称的那样。Kibana更多的是一个平台,一个可以让插件独立开发,“独立部署”的可扩展性平台,可以让我们自由的发挥自己的想象力和能力,根据自己的需求往上添加原生Kibana所不提供的功能。你可以开发一个新的app,也可以只部署一个后台服务,也可以是一个隐藏的跳转页面,这些,都有赖于plugin的方式,自由的在kibana上install, update和remove。

问题描述

以上。看起来是比较美好的,但硬币的反面是kibana作为一个单页应用,任何都其他功能都是"/"路径下都一个子path,任何插件的安装(除非是一个纯后台的服务,但我没有测试)都需要和主页面、所有已安装都插件产生联系,即每次插件都变动,都需要将所有的页面和js重新bundle一次。这个捆绑不是简单的捆绑,而是经过优化后的打包操作,相当耗时。重点是,按照目前的方式,optimize(bundle)的过程必须是现场的,即必须在正在运行的kibana服务器上进行,因此在以下情况下你可能会遇到麻烦:

  • 你的kibana服务作为一个生产服务,不能停
  • 你没有给kibana做双活
  • 因为只是一个前端,你给kibana分配的硬件资源很少(单核2G,双核4G)
  • 你使用的是6.3之后的版本,kibana已经默认安装了xpack。或者你是之前的版本,自己手动安装了xpack

这时,你若是安装或者更新插件(包括remove插件),都可能会因为optimize过程占用大量的cpu和内存资源,而造成kibana停止服务响应。

这里有一个小tips,如果你开发了多个插件,需要同时更新当时候,安装当时候请使用命令kibana-plugin install --no-optimize file:///path_to_your_file,当全部的插件都安装完了之后,再重启kibana,一次性的执行optimize流程,或者通过bin/kibana --optimize命令触发

Kibana架构简述

如果我们的目标是让kibana零等待时间升级插件,找到解决方案的前提是我们能够了解Kibana的软件架构和部署方式。

首先,我们需要知道的是Kibana是一个基于node的web应用,前端后端都主要使用的javascript。web后端使用的hapi作为web服务器应用程序。并且node无需安装,已经包含在了kibana目录下。(node目录)

以下是kibana的目录,所有的插件都安装在plugins目录,而所有打包后的内容都放在optimize目录。

├── LICENSE.txt
├── NOTICE.txt
├── README.txt
├── bin
├── config
├── data
├── node
├── node_modules
├── optimize
├── package.json
├── plugins
├── src
└── webpackShims

plugins目录(这里,我有两个插件):

.
├── kibana_auth_plugin
│   ├── index.js
│   ├── node_modules
│   ├── package.json
│   ├── public
│   ├── server
│   └── yarn.lock
└── system_portal
    ├── index.js
    ├── node_modules
    ├── package.json
    ├── public
    ├── server
    └── yarn.lock

每个插件都是类似的目录结构。public目录存放的是前端的页面和js,server目录存放的是后端的js。这里最终要的信息是,插件的开发其实也是一种 前后端分离的架构 。插件安装后后端主程序会调用server目录下的文件,而前端public目录下的文件会被压缩后打包到optimize目录,详见如下。

optimize目录:

├── bundles
│   ├── 176bcca991b07a6ec908fc4d36ac5ae0.svg
│   ├── 45c73723862c6fc5eb3d6961db2d71fb.eot
│   ├── 4b5a84aaf1c9485e060c503a0ff8cadb.woff2
│   ├── 69d89e51f62b6a582c311c35c0f778aa.svg
│   ├── 76a4f23c6be74fd309e0d0fd2c27a5de.svg
│   ├── 7c87870ab40d63cfb8870c1f183f9939.ttf
│   ├── apm.bundle.js
│   ├── apm.entry.js
│   ├── apm.style.css
│   ├── kibana-auth-plugin.bundle.js
│   ├── kibana-auth-plugin.entry.js
│   ├── kibana-auth-plugin.style.css
│   ├── canvas.bundle.js
│   ├── canvas.entry.js
│   ├── canvas.style.css
│   ├── cc17a3dbad9fc4557b4d5d47a38fcc56.svg
│   ├── commons.bundle.js
│   ├── commons.style.css
│   ├── dashboardViewer.bundle.js
│   ├── dashboardViewer.entry.js
│   ├── dashboardViewer.style.css
│   ├── dfb02f8f6d0cedc009ee5887cc68f1f3.woff
│   ├── fa0bbd682c66f1187d48f74b33b5bbd0.svg
│   ├── graph.bundle.js
│   ├── graph.entry.js
│   ├── graph.style.css
│   ├── infra.bundle.js
│   ├── infra.entry.js
│   ├── infra.style.css
│   ├── kibana.bundle.js
│   ├── kibana.entry.js
│   ├── kibana.style.css
│   ├── ml.bundle.js
│   ├── ml.entry.js
│   ├── ml.style.css
│   ├── monitoring.bundle.js
│   ├── monitoring.entry.js
│   ├── monitoring.style.css
│   ├── space_selector.bundle.js
│   ├── space_selector.entry.js
│   ├── space_selector.style.css
│   ├── src
│   ├── stateSessionStorageRedirect.bundle.js
│   ├── stateSessionStorageRedirect.entry.js
│   ├── stateSessionStorageRedirect.style.css
│   ├── status_page.bundle.js
│   ├── status_page.entry.js
│   ├── status_page.style.css
│   ├── system_portal.bundle.js
│   ├── system_portal.entry.js
│   ├── system_portal.style.css
│   ├── timelion.bundle.js
│   ├── timelion.entry.js
│   ├── timelion.style.css
│   ├── vendors.bundle.js
│   └── vendors.style.css

前端浏览器在访问"/"目录的时候会最先获取到kibana.*.js相关的文件。我们看一下 kibana.entry.js, 里面是包含了所有插件的信息的,即,每次插件的变动,这些文件也会跟着跟新

/**
 * Kibana entry file
 *
 * This is programmatically created and updated, do not modify
 *
 * context: ä
  "env": "production",
  "kbnVersion": "6.5.0",
  "buildNum": 18730,
  "plugins": Ä
    "apm",
    "apm_oss",
    "beats_management",
    "kibana_auth_plugin",
    "canvas",
    "cloud",
    "console",
    "console_extensions",
    "dashboard_mode",
    "elasticsearch",
    "graph",
    "grokdebugger",
    "index_management",
    "infra",
    "input_control_vis",
    "inspector_views",
    "kbn_doc_views",
    "kbn_vislib_vis_types",
    "kibana",
    "kuery_autocomplete",
    "license_management",
    "logstash",
    "markdown_vis",
    "metric_vis",
    "metrics",
    "ml",
    "monitoring",
    "notifications",
    "region_map",
    "reporting",
    "rollup",
    "searchprofiler",
    "spaces",
    "state_session_storage_redirect",
    "status_page",
    "system_portal",
    "table_vis",
    "tagcloud",
    "tile_map",
    "tilemap",
    "timelion",
    "vega",
    "watcher",
    "xpack_main"
  Å
å
 */

// import global polyfills before everything else
import 'babel-polyfill';
import 'custom-event-polyfill';
import 'whatwg-fetch';
import 'abortcontroller-polyfill';
import 'childnode-remove-polyfill';

import ä i18n å from 'Ékbn/i18n';
import ä CoreSystem å from '__kibanaCore__'

const injectedMetadata = JSON.parse(document.querySelector('kbn-injected-metadata').getAttribute('data'));
i18n.init(injectedMetadata.legacyMetadata.translations);

new CoreSystem(ä
  injectedMetadata,
  rootDomElement: document.body,
  requireLegacyFiles: () => ä
    require('plugins/kibana/kibana');
  å
å).start()

优化部署的方案(前后端分离的部署)

我们已经初步了解了kibana和kibana plugins的架构。那kibana插件的安装方案是怎么样的呢? kibana为了简化我们的工作,只需要我们将打包好的源码丢给kibana,然后执行命令:kibana-plugin install file:///path_to_your_file,这样貌似省事,但也把所有的工作都丢给了kibana服务器去完成。 在kibana服务器性能不佳的情况下,这部分工作可能会造成服务中断。因此,我们要代替kibana服务器完成这部分工作,做一个前后端分离的部署

后端部署

后端部署的速度是极快的,只需要把文件解压缩到具体目录就可以:

`kibana-plugin install --no-optimize file:///path_to_your_file`

这里特别要注意: --no-optimize参数是必须的,这时,插件的安装只是一个解压的过程,不会让kibana服务器去做繁重的optimize工作。

注意,执行这一步之后,不能重启kibana服务器,否则会自动做optimize

前端部署

这里说的前端,主要是指bundle之后的内容。在你的开发环境上,安装插件。当插件安装完成后,把bundles目录整体打包(bundles.zip)。将打包好之后的内容,上传到kibana服务器,删除旧的optimize/bundles目录,把打包好的bundles目录解压到optimize目录下

注意,这里开发环境上的kibana版本,和kibana安装的插件必须是和生产环境上是一致的,否则会造成无法启动或者自动重做optimize

重启kibana服务器

当以上两步完成之后,重启kibana service即可,你会发现,内容已经更新,但是不会触发任何的optimize过程。

参考示例

以下是该过程的一个ansible playbook供大家参考

---

- name: deploy bundles zip
  copy: src=bundles.zip dest={{kibana_home}}/optimize force=yes mode={{file_mask}}

- name: deploy system plugins zip
  copy: src=system_portal-0.0.0.zip dest={{kibana_home}}/ force=yes mode={{file_mask}}

- name: deploy auth zip
  copy: src=kibana_auth_plugin-6.5.0.zip dest={{kibana_home}}/ force=yes mode={{file_mask}}

- name: remove system plugin
  shell: "{{kibana_home}}/bin/kibana-plugin remove system_portal"
  ignore_errors: True

- name: remove auth plugin
  shell: "{{kibana_home}}/bin/kibana-plugin remove kibana_auth_plugin"
  ignore_errors: True

- name: install system plugin
  shell: "{{kibana_home}}/bin/kibana-plugin install --no-optimize file://{{kibana_home}}/system_portal-0.0.0.zip"
  register: install_state

- name: install auth plugin
  shell: "{{kibana_home}}/bin/kibana-plugin install --no-optimize file://{{kibana_home}}/kibana_auth_plugin-6.5.0.zip"
  register: install_state
#  failed_when: "'Extraction complete' in install_state.stdout_lines"

- name: delete old bundls
  file: dest={{kibana_home}}/optimize/bundles state=absent

- name: delete old bundls
  unarchive:
     src: "{{kibana_home}}/optimize/bundles.zip"
     dest: "{{kibana_home}}/optimize/"
     copy: no
     group: "kibana"
     owner: "kibana"
     mode: "{{file_mask}}"

- name: delete zip files
  file: dest={{kibana_home}}/optimize/bundles.zip state=absent

- name: restart kibana
  become: yes
  service: name={{kibana_init_script | basename}} state=restarted enabled=yes
继续阅读 »

正如官方文档所自豪宣称的那样。Kibana更多的是一个平台,一个可以让插件独立开发,“独立部署”的可扩展性平台,可以让我们自由的发挥自己的想象力和能力,根据自己的需求往上添加原生Kibana所不提供的功能。你可以开发一个新的app,也可以只部署一个后台服务,也可以是一个隐藏的跳转页面,这些,都有赖于plugin的方式,自由的在kibana上install, update和remove。

问题描述

以上。看起来是比较美好的,但硬币的反面是kibana作为一个单页应用,任何都其他功能都是"/"路径下都一个子path,任何插件的安装(除非是一个纯后台的服务,但我没有测试)都需要和主页面、所有已安装都插件产生联系,即每次插件都变动,都需要将所有的页面和js重新bundle一次。这个捆绑不是简单的捆绑,而是经过优化后的打包操作,相当耗时。重点是,按照目前的方式,optimize(bundle)的过程必须是现场的,即必须在正在运行的kibana服务器上进行,因此在以下情况下你可能会遇到麻烦:

  • 你的kibana服务作为一个生产服务,不能停
  • 你没有给kibana做双活
  • 因为只是一个前端,你给kibana分配的硬件资源很少(单核2G,双核4G)
  • 你使用的是6.3之后的版本,kibana已经默认安装了xpack。或者你是之前的版本,自己手动安装了xpack

这时,你若是安装或者更新插件(包括remove插件),都可能会因为optimize过程占用大量的cpu和内存资源,而造成kibana停止服务响应。

这里有一个小tips,如果你开发了多个插件,需要同时更新当时候,安装当时候请使用命令kibana-plugin install --no-optimize file:///path_to_your_file,当全部的插件都安装完了之后,再重启kibana,一次性的执行optimize流程,或者通过bin/kibana --optimize命令触发

Kibana架构简述

如果我们的目标是让kibana零等待时间升级插件,找到解决方案的前提是我们能够了解Kibana的软件架构和部署方式。

首先,我们需要知道的是Kibana是一个基于node的web应用,前端后端都主要使用的javascript。web后端使用的hapi作为web服务器应用程序。并且node无需安装,已经包含在了kibana目录下。(node目录)

以下是kibana的目录,所有的插件都安装在plugins目录,而所有打包后的内容都放在optimize目录。

├── LICENSE.txt
├── NOTICE.txt
├── README.txt
├── bin
├── config
├── data
├── node
├── node_modules
├── optimize
├── package.json
├── plugins
├── src
└── webpackShims

plugins目录(这里,我有两个插件):

.
├── kibana_auth_plugin
│   ├── index.js
│   ├── node_modules
│   ├── package.json
│   ├── public
│   ├── server
│   └── yarn.lock
└── system_portal
    ├── index.js
    ├── node_modules
    ├── package.json
    ├── public
    ├── server
    └── yarn.lock

每个插件都是类似的目录结构。public目录存放的是前端的页面和js,server目录存放的是后端的js。这里最终要的信息是,插件的开发其实也是一种 前后端分离的架构 。插件安装后后端主程序会调用server目录下的文件,而前端public目录下的文件会被压缩后打包到optimize目录,详见如下。

optimize目录:

├── bundles
│   ├── 176bcca991b07a6ec908fc4d36ac5ae0.svg
│   ├── 45c73723862c6fc5eb3d6961db2d71fb.eot
│   ├── 4b5a84aaf1c9485e060c503a0ff8cadb.woff2
│   ├── 69d89e51f62b6a582c311c35c0f778aa.svg
│   ├── 76a4f23c6be74fd309e0d0fd2c27a5de.svg
│   ├── 7c87870ab40d63cfb8870c1f183f9939.ttf
│   ├── apm.bundle.js
│   ├── apm.entry.js
│   ├── apm.style.css
│   ├── kibana-auth-plugin.bundle.js
│   ├── kibana-auth-plugin.entry.js
│   ├── kibana-auth-plugin.style.css
│   ├── canvas.bundle.js
│   ├── canvas.entry.js
│   ├── canvas.style.css
│   ├── cc17a3dbad9fc4557b4d5d47a38fcc56.svg
│   ├── commons.bundle.js
│   ├── commons.style.css
│   ├── dashboardViewer.bundle.js
│   ├── dashboardViewer.entry.js
│   ├── dashboardViewer.style.css
│   ├── dfb02f8f6d0cedc009ee5887cc68f1f3.woff
│   ├── fa0bbd682c66f1187d48f74b33b5bbd0.svg
│   ├── graph.bundle.js
│   ├── graph.entry.js
│   ├── graph.style.css
│   ├── infra.bundle.js
│   ├── infra.entry.js
│   ├── infra.style.css
│   ├── kibana.bundle.js
│   ├── kibana.entry.js
│   ├── kibana.style.css
│   ├── ml.bundle.js
│   ├── ml.entry.js
│   ├── ml.style.css
│   ├── monitoring.bundle.js
│   ├── monitoring.entry.js
│   ├── monitoring.style.css
│   ├── space_selector.bundle.js
│   ├── space_selector.entry.js
│   ├── space_selector.style.css
│   ├── src
│   ├── stateSessionStorageRedirect.bundle.js
│   ├── stateSessionStorageRedirect.entry.js
│   ├── stateSessionStorageRedirect.style.css
│   ├── status_page.bundle.js
│   ├── status_page.entry.js
│   ├── status_page.style.css
│   ├── system_portal.bundle.js
│   ├── system_portal.entry.js
│   ├── system_portal.style.css
│   ├── timelion.bundle.js
│   ├── timelion.entry.js
│   ├── timelion.style.css
│   ├── vendors.bundle.js
│   └── vendors.style.css

前端浏览器在访问"/"目录的时候会最先获取到kibana.*.js相关的文件。我们看一下 kibana.entry.js, 里面是包含了所有插件的信息的,即,每次插件的变动,这些文件也会跟着跟新

/**
 * Kibana entry file
 *
 * This is programmatically created and updated, do not modify
 *
 * context: ä
  "env": "production",
  "kbnVersion": "6.5.0",
  "buildNum": 18730,
  "plugins": Ä
    "apm",
    "apm_oss",
    "beats_management",
    "kibana_auth_plugin",
    "canvas",
    "cloud",
    "console",
    "console_extensions",
    "dashboard_mode",
    "elasticsearch",
    "graph",
    "grokdebugger",
    "index_management",
    "infra",
    "input_control_vis",
    "inspector_views",
    "kbn_doc_views",
    "kbn_vislib_vis_types",
    "kibana",
    "kuery_autocomplete",
    "license_management",
    "logstash",
    "markdown_vis",
    "metric_vis",
    "metrics",
    "ml",
    "monitoring",
    "notifications",
    "region_map",
    "reporting",
    "rollup",
    "searchprofiler",
    "spaces",
    "state_session_storage_redirect",
    "status_page",
    "system_portal",
    "table_vis",
    "tagcloud",
    "tile_map",
    "tilemap",
    "timelion",
    "vega",
    "watcher",
    "xpack_main"
  Å
å
 */

// import global polyfills before everything else
import 'babel-polyfill';
import 'custom-event-polyfill';
import 'whatwg-fetch';
import 'abortcontroller-polyfill';
import 'childnode-remove-polyfill';

import ä i18n å from 'Ékbn/i18n';
import ä CoreSystem å from '__kibanaCore__'

const injectedMetadata = JSON.parse(document.querySelector('kbn-injected-metadata').getAttribute('data'));
i18n.init(injectedMetadata.legacyMetadata.translations);

new CoreSystem(ä
  injectedMetadata,
  rootDomElement: document.body,
  requireLegacyFiles: () => ä
    require('plugins/kibana/kibana');
  å
å).start()

优化部署的方案(前后端分离的部署)

我们已经初步了解了kibana和kibana plugins的架构。那kibana插件的安装方案是怎么样的呢? kibana为了简化我们的工作,只需要我们将打包好的源码丢给kibana,然后执行命令:kibana-plugin install file:///path_to_your_file,这样貌似省事,但也把所有的工作都丢给了kibana服务器去完成。 在kibana服务器性能不佳的情况下,这部分工作可能会造成服务中断。因此,我们要代替kibana服务器完成这部分工作,做一个前后端分离的部署

后端部署

后端部署的速度是极快的,只需要把文件解压缩到具体目录就可以:

`kibana-plugin install --no-optimize file:///path_to_your_file`

这里特别要注意: --no-optimize参数是必须的,这时,插件的安装只是一个解压的过程,不会让kibana服务器去做繁重的optimize工作。

注意,执行这一步之后,不能重启kibana服务器,否则会自动做optimize

前端部署

这里说的前端,主要是指bundle之后的内容。在你的开发环境上,安装插件。当插件安装完成后,把bundles目录整体打包(bundles.zip)。将打包好之后的内容,上传到kibana服务器,删除旧的optimize/bundles目录,把打包好的bundles目录解压到optimize目录下

注意,这里开发环境上的kibana版本,和kibana安装的插件必须是和生产环境上是一致的,否则会造成无法启动或者自动重做optimize

重启kibana服务器

当以上两步完成之后,重启kibana service即可,你会发现,内容已经更新,但是不会触发任何的optimize过程。

参考示例

以下是该过程的一个ansible playbook供大家参考

---

- name: deploy bundles zip
  copy: src=bundles.zip dest={{kibana_home}}/optimize force=yes mode={{file_mask}}

- name: deploy system plugins zip
  copy: src=system_portal-0.0.0.zip dest={{kibana_home}}/ force=yes mode={{file_mask}}

- name: deploy auth zip
  copy: src=kibana_auth_plugin-6.5.0.zip dest={{kibana_home}}/ force=yes mode={{file_mask}}

- name: remove system plugin
  shell: "{{kibana_home}}/bin/kibana-plugin remove system_portal"
  ignore_errors: True

- name: remove auth plugin
  shell: "{{kibana_home}}/bin/kibana-plugin remove kibana_auth_plugin"
  ignore_errors: True

- name: install system plugin
  shell: "{{kibana_home}}/bin/kibana-plugin install --no-optimize file://{{kibana_home}}/system_portal-0.0.0.zip"
  register: install_state

- name: install auth plugin
  shell: "{{kibana_home}}/bin/kibana-plugin install --no-optimize file://{{kibana_home}}/kibana_auth_plugin-6.5.0.zip"
  register: install_state
#  failed_when: "'Extraction complete' in install_state.stdout_lines"

- name: delete old bundls
  file: dest={{kibana_home}}/optimize/bundles state=absent

- name: delete old bundls
  unarchive:
     src: "{{kibana_home}}/optimize/bundles.zip"
     dest: "{{kibana_home}}/optimize/"
     copy: no
     group: "kibana"
     owner: "kibana"
     mode: "{{file_mask}}"

- name: delete zip files
  file: dest={{kibana_home}}/optimize/bundles.zip state=absent

- name: restart kibana
  become: yes
  service: name={{kibana_init_script | basename}} state=restarted enabled=yes
收起阅读 »

社区日报 第505期 (2019-01-10)

Elastic日报 第505期 (2019-01-10) 
1.基于Kafka与Spark的实时大数据质量监控平台 
http://t.cn/EqhWgHG 
2.使用 Apache Spark 和 Elasticsearch 构建一个推荐系统 
http://t.cn/RrdR6Hp 
3.使用ELK实时优化工程优化预测 
http://t.cn/R9ZM0XJ 
 
编辑:金桥 
归档:https://elasticsearch.cn/article/6325 
订阅:https://tinyletter.com/elastic-daily
继续阅读 »
Elastic日报 第505期 (2019-01-10) 
1.基于Kafka与Spark的实时大数据质量监控平台 
http://t.cn/EqhWgHG 
2.使用 Apache Spark 和 Elasticsearch 构建一个推荐系统 
http://t.cn/RrdR6Hp 
3.使用ELK实时优化工程优化预测 
http://t.cn/R9ZM0XJ 
 
编辑:金桥 
归档:https://elasticsearch.cn/article/6325 
订阅:https://tinyletter.com/elastic-daily 收起阅读 »

社区日报 第504期 (2019-01-09)

1、基于elasticsearch站内搜索引擎实战;
http://t.cn/EGr5gbR
2、基于elasticSearch的搜房网实战;
http://t.cn/EGrtIi5
3、使用elasticsearch构建一个完整的搜索引擎。
http://t.cn/EGrc25K

编辑:wt
归档:https://elasticsearch.cn/article/6324
订阅:https://tinyletter.com/elastic-daily
继续阅读 »
1、基于elasticsearch站内搜索引擎实战;
http://t.cn/EGr5gbR
2、基于elasticSearch的搜房网实战;
http://t.cn/EGrtIi5
3、使用elasticsearch构建一个完整的搜索引擎。
http://t.cn/EGrc25K

编辑:wt
归档:https://elasticsearch.cn/article/6324
订阅:https://tinyletter.com/elastic-daily 收起阅读 »