1:elasticsearch插件分类简述

2:Java插件开发要点

3:如何针对不同版本elasticsearch提供多版本的插件

4:插件具有外部依赖时遇到的一些问题(2016-09-07更新)


elasticsearch插件分类简述

elasticsearch插件分为Site插件及Java插件,前者比如使用最广泛的head插件,而后者比如elastic官方提供的商业插件shield。

需要注意的是Site插件从elasticsearch2.3.0版本开始已被标记为Deprecated,并将从5.0.0版本开始被正式移除,相关的Site插件将被整合到kibana中,或者单独部署web server。具体如何整合我还不太清楚。

下文主要记录Java插件的开发要点,以maven管理为例。


Java插件开发要点

以下一一个简单功能为例来说明开发流程,该插件记录所有对部署elasticsearch节点的请求,并且根据指定的配置参数,过滤掉敏感操作(DELETE)

1:Java插件入口

继承org.elasticsearch.plugins.Plugin类的入口类,并实现onModule方法。该类中可以通过settings访问elasticsearch配置文件,获取配置信息,并加载下一步的handler,具体实现代码如下所示:

 package org.elasticsearch.es.plugin;

 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.rest.RestModule;
 import org.elasticsearch.plugins.Plugin;

 @SuppressWarnings("static-method")
 public class MyRestPlugin extends Plugin {

     private final Settings settings;

     public MyRestPlugin(Settings settings){
         this.settings = settings;
     }

     @Override
     public String name(){
         return "MyRest";
     }

     @Override
     public String description(){
         return "ElasticSearch Plugin";
     }

     public void onModule(RestModule module){
         String isPluginEnabled = settings.get("ld.enabled");
         MethodAuthenticator.setEnabledStr(isPluginEnabled);
         if(isPluginEnabled != null && isPluginEnabled.toLowerCase().equals("true")){
             MethodAuthenticator.setIsPluginEnabled(true);
             String[] denyMethods = settings.getAsArray("ld.deny", new String[]{});
             if(denyMethods != null){
                 MethodAuthenticator.setDenyMethods(denyMethods);
             }
         } else {
             MethodAuthenticator.setIsPluginEnabled(false);
         }

         module.addRestAction(MyRestHandler.class);
     }

 }

配置文件信息放入authenticator静态类,代码如下:

 package org.elasticsearch.es.plugin;

 import java.util.Arrays;

 public class MethodAuthenticator{

     private static String enabledStr;
     private static boolean isPluginEnabled;
     private static String[] denyMethods;

 //    public MethodAuthenticator(){
 //
 //    }

     public static boolean isMethodEnabled(String method){
         if(denyMethods == null) {
             MyLogger.debug("The deny methods is null");
             return true;
         }
         if(Arrays.asList(denyMethods).contains(method)){
             return false;
         } else {
             return true;
         }
     }

     public static String getEnabledStr(){
         return enabledStr;
     }

     public static void setEnabledStr(String enabledStr){
         MethodAuthenticator.enabledStr = enabledStr;
     }

     public static boolean getIsPluginEnabled() {
         return isPluginEnabled;
     }

     public static String[] getDenyMethods() {
         return denyMethods;
     }

     public static void setIsPluginEnabled(boolean isPluginEnabled) {
         MethodAuthenticator.isPluginEnabled = isPluginEnabled;
     }

     public static void setDenyMethods(String[] denyMethods) {
         MethodAuthenticator.denyMethods = denyMethods;
     }
 }

2:相关的Handler

继承org.elasticsearch.rest.BaseRestHandler类的handler,将filter注入controller,实现代码如下:

 package org.elasticsearch.es.plugin;

 import org.elasticsearch.rest.*;

 import org.elasticsearch.client.Client;
 import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.settings.Settings;

 public class MyRestHandler extends BaseRestHandler {
     @Inject
     public MyRestHandler(Settings settings, RestController restController, Client client){
         super(settings, restController, client);
         RestFilter filter = new MyRestFilter(client);
         restController.registerFilter(filter);
     }

     @Override
     protected void handleRequest(RestRequest restRequest, RestChannel restChannel, Client client)
             throws Exception {
         // TODO Auto-generated method stub

     }
 }

3:注入Filter

继承org.elasticsearch.rest.RestFilter类的filter,在过滤器中实现对请求的记录及过滤,实现代码如下:

 package org.elasticsearch.es.plugin;

 import org.elasticsearch.client.Client;
 import org.elasticsearch.rest.RestFilter;
 import org.elasticsearch.rest.RestRequest;
 import org.elasticsearch.rest.RestChannel;
 import org.elasticsearch.rest.RestFilterChain;
 import org.elasticsearch.rest.RestStatus;
 import org.elasticsearch.rest.BytesRestResponse;

 import java.net.InetSocketAddress;

 public class MyRestFilter extends RestFilter{
     Client client;
     public MyRestFilter(Client client){
         this.client = client;
     }

     @Override
     public void process(RestRequest request, RestChannel channel, RestFilterChain filterChain) throws Exception{
         try{
             if(MethodAuthenticator.getIsPluginEnabled()){
                 MyLogger.info(getLogInfo(request));
                 String methodStr = request.method().name().toLowerCase();
                 if(MethodAuthenticator.isMethodEnabled(methodStr)){
                     //MyLogger.info("The request method " + methodStr + " is allowed");
                     filterChain.continueProcessing(request, channel);
                 } else {
                     MyLogger.info("The request method " + methodStr + " is denyed");
                     BytesRestResponse res = new BytesRestResponse(RestStatus.FORBIDDEN, "Forbidden Method Request by MyRest plugin");
                     channel.sendResponse(res);
                 }
             } else {
                 //MyLogger.info("MyRest is disabled");
                 filterChain.continueProcessing(request, channel);
             }
         } catch (Exception exp) {
             channel.sendResponse(new BytesRestResponse(RestStatus.INTERNAL_SERVER_ERROR, "My Rest Filter internal exception"));
         }
         return ;
     }

     private String getLogInfo(RestRequest request) throws Exception {
         try{
             String pathStr = request.path();
             String ipaddr = ((InetSocketAddress)request.getRemoteAddress()).getAddress().getHostAddress();
             return "The [" + request.method().name() + "] request [" + pathStr + "] is from [" + ipaddr + "]";
         } catch (Exception exp) {
             MyLogger.error("Request resolve failed: ", exp);
         }
         return "The request resolve failed";
     }
 }

日志记录使用elasticsearch的logger,入口static类代码如下所示:

 package org.elasticsearch.es.plugin;

 import org.elasticsearch.common.logging.ESLogger;
 import org.elasticsearch.common.logging.ESLoggerFactory;

 public class MyLogger {

     private static ESLogger esLogger;
     static {
         esLogger = ESLoggerFactory.getLogger("myRest");
     }

     public static void debug(String msg){
         esLogger.debug(msg);
     }

     public static void info(String msg){
         esLogger.info(msg);
     }

     public static void warn(String msg){
         esLogger.warn(msg);
     }

     public static void error(String msg){
         esLogger.error(msg);
     }

     public static void error(String msg, Exception exp){
         esLogger.error(msg, exp);
     }
 }

4:插件配置信息

由于插件使用elasticsearch配置文件保存配置信息,因此在配置文件中加入如下信息:

默认elasticsearch配置文件路径为:/etc/elasticsearch/elasticsearch.yml

  ld.enabled: false
  ld.deny: ["delete"]

需要注意的是配置文件中配置项前面加入2个空格字符,否则可能导致elasticsearch不能启动;

此外,插件正常工作还依赖于plugin-descriptor.properties文件(elasticsearch-1.x版本中是es-plugin.properties文件),该文件配置信息如下例:

name=myRest
description=GridsumLawDissectorElasticSearchPlugin
version=0.0.1
jvm=true
site=false
classname=org.elasticsearch.es.plugin.MyRestPlugin
java.version=1.8
elasticsearch.version=2.3.3

我这里以elasticsearch-2.3.3版本为例,可按需求改为2.X的任意版本。

插件描述文件如下所示(其中format指定插件以zip形式安装):

<?xml version="1.0" encoding="UTF-8"?>
<assembly
    xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
    <id>plugin</id>
    <formats>
        <format>zip</format>
    </formats>
    <includeBaseDirectory>false</includeBaseDirectory>
    <dependencySets>
        <dependencySet>
            <outputDirectory>/</outputDirectory>
            <useProjectArtifact>true</useProjectArtifact>
            <useTransitiveFiltering>true</useTransitiveFiltering>
        </dependencySet>
    </dependencySets>
</assembly>

5:编译部署

插件编译使用maven即可:

mvn clean package

需要注意的是如果首次编译可能需要较长时间,maven会下载目标版本的org.elasticsearch.jar。

版本信息在maven对应的pom文件中指定。

编译完成后,将打包的jar文件与plugin-descriptor.properties文件打包为zip安装包,拷贝至elasticsearch的部署节点。

使用如下方式安装插件:

/usr/share/elasticsearch/bin/plugin install file:///opt/myrest.zip

指令中需要说明的是:

1)elasticsearch安装目录下的bin目录下的plugin脚本;

2)由于直接通过打包安装文件安装,因此指定为file://;

3)紧跟打包zip文件的路径即可。

部署完毕后,确保elasticsearch.yml中的配置信息正确,即可重启elasticsearch节点:

systemctl restart elasticsearch

6:验证插件

通过监控elasticsearch日志可看到插件记录的所有请求目标及来源:

tail -f /var/log/elasticsearch/xx.xx.log

上述命令中为log的默认路径,查看以集群clustername命名的日志文件即可。

如果配置参数中过滤方法设置了DELETE,则可通过发送DELETE请求来验证,插件将过滤该请求,并返回403。


如何针对不同版本elasticsearch提供多版本的插件

如前文所述,elasticsearch从2.X版本开始JAVA插件相关的Plugin,BaseRestHandler,RestFilter等抽象类都相同,区别只在于依赖的org.elasticsearch,因此,JAVA插件对elasticsearch2.X以上各版本的支持,只需要修改对应的依赖版本信息并重新打包部署即可。

具体的两个需要修改依赖版本信息的文件分别是:

plugin-descriptor.properties
pom.xml

当然这是以maven为例,如果不是以maven,则只需修改第一个文件的版本信息,并手动引用对应版本的org.elasticsearch,重新编译打包即可。


插件具有外部依赖时遇到的一些问题(2016-09-07更新)

最近由于插件功能扩展,需要用到json序列化,使用了fastJson的1.2.16版本,如下是dependency:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version></version>
</dependency>

遇到的问题主要是在插件部署环节,主要有2个问题:

1:插件发布并未包含依赖的jar包;

2:fastJson.jar需要许可权(不知道表述是否正确,本人不是javaer),以下为问题2的异常信息:

java.security.AccessControlException: access denied (java.lang.RuntimePermission getClassLoader)
...
...
...略

解决方法:

1:在插件发布后打包zip过程中加入fastjson-1.2.16.jar即可,如果打包的package中未包含该jar包,可以从maven的repository路径中获取;

2:更改java.policy文件许可权(默认路径/usr/java/jdk1.8.0_73/jre/lib/security/java.policy),jdk版本为目标主机的jdk版本,在grant中加入以下许可信息:

permission java.lang.RuntimePermission "createClassLoader";
permission java.lang.RuntimePermission "getClassLoader";

重启elasticsearch进程即可。

java.policy如找不到,可以在/usr/lib/jvm/java-1.8.0/jre/lib/security路径下找到。

Elasticsearch 2.X 版本Java插件开发简述的更多相关文章

  1. Elasticsearch 1.X 版本Java插件开发

    接上一篇<Elasticsearch 2.X 版本Java插件开发简述> 开发1.X版本elasticsearch java插件与2.X版本有一些不同,同时在安装部署上也有些不同,主要区别 ...

  2. Java版本及历史简述

    Java版本及历史简述 初学Java,对于Java那么多版本很困惑,这里做一点笔记,如有错误希望指出. Java由Sun公司创造,后Sun公司被Oracle公司收购,Java也随之变为Oracle的产 ...

  3. elasticsearch源码分析及插件开发

    ElasticSearch是一个基于Lucene的搜索服务器.它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口.Elasticsearch是用Java开发的,并作为Apach ...

  4. SpringBoot整合ElasticSearch实现多版本的兼容

    前言 在上一篇学习SpringBoot中,整合了Mybatis.Druid和PageHelper并实现了多数据源的操作.本篇主要是介绍和使用目前最火的搜索引擎ElastiSearch,并和Spring ...

  5. Elasticsearch教程(二)java集成Elasticsearch

    1.添加maven <!--tika抽取文件内容 --> <dependency> <groupId>org.apache.tika</groupId> ...

  6. Elasticsearch 6.x版本全文检索学习之分布式特性介绍

    1.Elasticsearch 6.x版本全文检索学习之分布式特性介绍. 1).Elasticsearch支持集群默认,是一个分布式系统,其好处主要有两个. a.增大系统容量,如内存.磁盘.使得es集 ...

  7. Elasticsearch 6.x版本全文检索学习之Search API

    Elasticsearch 6.x版本全文检索学习之Search API. 1).Search API,实现对es中存储的数据进行查询分析,endpoind为_search,如下所示. 方式一.GET ...

  8. 微信机器人 返现机器人 pc版本 移动版本 java开发 小范省钱

    微信机器人 返现机器人 pc版本 移动版本 java开发 小范省钱 微信搜索微信号 fanli-x 或 扫描下方二维码,可查看效果. 非web版微信,pc/移动版微信 支持新号24小时 不封号! 有任 ...

  9. 下载各个版本java (Java Development Kit)

    本文介绍怎么样下载各个版本java开发工具包. 方法/步骤   打开官方下载网址:http://www.oracle.com/technetwork/java/javase/downloads/ind ...

随机推荐

  1. AC日记——统计数字字符个数 openjudge 1.7 01

    01:统计数字字符个数 总时间限制:  1000ms 内存限制:  65536kB 描述 输入一行字符,统计出其中数字字符的个数. 输入 一行字符串,总长度不超过255. 输出 输出为1行,输出字符串 ...

  2. java 24 - 3 GUI之添加按钮

    需求:把按钮添加到窗体,并对按钮添加一个点击事件. A:创建窗体对象 B:创建按钮对象 C:把按钮添加到窗体 D:窗体显示 注意:这里对按钮添加点击事件,同样使用监听器. 但是,这里的按钮是组件,所以 ...

  3. 我心目中的Asp.net核心对象

    转:http://www.cnblogs.com/fish-li/archive/2011/08/21/2148640.html 阅读目录 开始 HttpRuntime HttpServerUtili ...

  4. SS命令和Netstat命令比较

    在早期运维工作中,查看服务器连接数一般都会用netstat命令.其实,有一个命令比netstat更高效,那就是ss(Socket Statistics)命令!ss命令可以用来获取socket统计信息, ...

  5. TP框架自带的正则验证的规则(转载)

    thinkphp框架里面自带有很多自动验证的规则,下面是框架自带的正则验证的规则,官方的说明文档里面没有这么多,所以记下来,以备使用. view sourceprint?01static $regex ...

  6. JavaScript的闭包和内存泄漏问题

    闭包 http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html JavaScript中必须提到的功能最强大的抽象 ...

  7. Alwayson--SYS.dm_hadr_instance_node_map 返回0行

    下面是MSDN关于SYS.dm_hadr_instance_node_map 的解释,我就不在翻译了 For every instance of SQL Server that hosts an av ...

  8. SharePoint Foundation 2013 with SP1

    终于支持在 Windows Server 2012 R2 上安装了. 下载 另外,还有一个针对SharePoint Foundation 2013的重要更新.可以在安装SP1之前或之后安装. Micr ...

  9. (转)c# 解析JSON的几种办法

    来自:http://blog.csdn.net/gaofang2009/article/details/6073029 欲成为海洋大师,必知晓海中每一滴水的真名. 刚开始只是想找一个转换JSON数组的 ...

  10. NOI2018准备Day7

    昨天没写,就不补了. 晚上追剧到3点,今天困死...... 上午做了一道水题,然后找一个程序的神奇的错误花了3个小时 下午做了3道递归吧,稍微难一点儿的黄金题就卡了 刚开始学递归时没多做题练练,现在 ...