Elasticsearch 2.X 版本Java插件开发简述

时间:2022-02-03 00:17:05

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&period;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教程&lpar;二)java集成Elasticsearch

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

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

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

  7. Elasticsearch 6&period;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. Titanium系列--我常用的Titanium的快捷键(持续更新中。。)

    Titanium快捷键: (注:Titanium属于eclipse系列的IDE) ctrl + shift + r 快速打开 ctrl + shift + e / ctrl + e 打开之前的文件 c ...

  2. C&plus;&plus;的几种强制类型转换

    有时我们希望显式地将对象强制类型转换成另外一种类型.例如,如果想在下面的代码中执行浮点数除法: int i, j; double slope = i / j; 就要使用某种方法将i和/或j显式地转换成 ...

  3. MongoDB shell 格式化

    直接的方法: db.collection.find().pretty(); 如果想要所有的查询都格式化,可以执行: echo "DBQuery.prototype._prettyShell ...

  4. oracle的to&lowbar;number、to&lowbar;char、to&lowbar;date用法

    TO_DATE 是把字符串转换为数据库中得日期类型转换函数TO_CHAR 是把日期或数字转换为字符串TO_NUMBER 将字符转化为数字 TO_DATE使用TO_DATE函数将字符转换为日期TO_DA ...

  5. Activity 的启动模式

    好久没用过那几种activity的启动模式了,如今看到singletop竟然傻了眼,完全忘记了这几种启动模式的区别!隧将两年前的总结翻出来,通读一遍那晦涩难懂的记录,又理解了一遍,在以前记录的基础上, ...

  6. c&num; winform 中的 工具栏自动隐藏 splitter用法 带源码

    c# winform 中的 工具栏自动隐藏 splitter用法 带源码 代码下载地址 http://download.csdn.net/detail/simadi/7649313

  7. SpringBoot编写自定义的starter 专题

    What’s in a name All official starters follow a similar naming pattern; spring-boot-starter-*, where ...

  8. spring 配置 applicationContext&period;xml

    <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.spr ...

  9. Java 容器源码分析之1&period;7HashMap

    以下内容基于jdk1.7.0_79源码: 什么是HashMap 基于哈希表的一个Map接口实现,存储的对象是一个键值对对象(Entry<K,V>): HashMap补充说明 基于数组和链表 ...

  10. PowerBuilder -- 保存图片

    String ls_path, ls_file_name, ls_filter, ls_errInt li_ret, li_loop, li_i, li_file, li_bytesLong ll_f ...