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


    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,具体实现代码如下所示:

     1 package org.elasticsearch.es.plugin;
     2 
     3 import org.elasticsearch.common.settings.Settings;
     4 import org.elasticsearch.rest.RestModule;
     5 import org.elasticsearch.plugins.Plugin;
     6 
     7 @SuppressWarnings("static-method")
     8 public class MyRestPlugin extends Plugin {
     9 
    10     private final Settings settings;
    11 
    12     public MyRestPlugin(Settings settings){
    13         this.settings = settings;
    14     }
    15 
    16     @Override
    17     public String name(){
    18         return "MyRest";
    19     }
    20     
    21     @Override
    22     public String description(){
    23         return "ElasticSearch Plugin";
    24     }
    25 
    26     public void onModule(RestModule module){
    27         String isPluginEnabled = settings.get("ld.enabled");
    28         MethodAuthenticator.setEnabledStr(isPluginEnabled);
    29         if(isPluginEnabled != null && isPluginEnabled.toLowerCase().equals("true")){
    30             MethodAuthenticator.setIsPluginEnabled(true);
    31             String[] denyMethods = settings.getAsArray("ld.deny", new String[]{});
    32             if(denyMethods != null){
    33                 MethodAuthenticator.setDenyMethods(denyMethods);
    34             }
    35         } else {
    36             MethodAuthenticator.setIsPluginEnabled(false);
    37         }
    38 
    39         module.addRestAction(MyRestHandler.class);
    40     }
    41     
    42 }
    View Code

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

     1 package org.elasticsearch.es.plugin;
     2 
     3 import java.util.Arrays;
     4 
     5 public class MethodAuthenticator{
     6     
     7     private static String enabledStr;
     8     private static boolean isPluginEnabled;
     9     private static String[] denyMethods;
    10 
    11 //    public MethodAuthenticator(){
    12 //
    13 //    }
    14 
    15     public static boolean isMethodEnabled(String method){
    16         if(denyMethods == null) {
    17             MyLogger.debug("The deny methods is null");
    18             return true;
    19         }
    20         if(Arrays.asList(denyMethods).contains(method)){
    21             return false;
    22         } else {
    23             return true;
    24         }
    25     }
    26     
    27     public static String getEnabledStr(){
    28         return enabledStr;
    29     }
    30     
    31     public static void setEnabledStr(String enabledStr){
    32         MethodAuthenticator.enabledStr = enabledStr;
    33     }
    34 
    35     public static boolean getIsPluginEnabled() {
    36         return isPluginEnabled;
    37     }
    38 
    39     public static String[] getDenyMethods() {
    40         return denyMethods;
    41     }
    42 
    43     public static void setIsPluginEnabled(boolean isPluginEnabled) {
    44         MethodAuthenticator.isPluginEnabled = isPluginEnabled;
    45     }
    46 
    47     public static void setDenyMethods(String[] denyMethods) {
    48         MethodAuthenticator.denyMethods = denyMethods;
    49     }
    50 }
    View Code

    2:相关的Handler

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

     1 package org.elasticsearch.es.plugin;
     2 
     3 import org.elasticsearch.rest.*;
     4 
     5 import org.elasticsearch.client.Client;
     6 import org.elasticsearch.common.inject.Inject;
     7 import org.elasticsearch.common.settings.Settings;
     8 
     9 public class MyRestHandler extends BaseRestHandler {
    10     @Inject
    11     public MyRestHandler(Settings settings, RestController restController, Client client){
    12         super(settings, restController, client);
    13         RestFilter filter = new MyRestFilter(client);
    14         restController.registerFilter(filter);
    15     }
    16 
    17     @Override
    18     protected void handleRequest(RestRequest restRequest, RestChannel restChannel, Client client)
    19             throws Exception {
    20         // TODO Auto-generated method stub
    21         
    22     }
    23 }
    View Code

    3:注入Filter

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

     1 package org.elasticsearch.es.plugin;
     2 
     3 import org.elasticsearch.client.Client;
     4 import org.elasticsearch.rest.RestFilter;
     5 import org.elasticsearch.rest.RestRequest;
     6 import org.elasticsearch.rest.RestChannel;
     7 import org.elasticsearch.rest.RestFilterChain;
     8 import org.elasticsearch.rest.RestStatus;
     9 import org.elasticsearch.rest.BytesRestResponse;
    10 
    11 import java.net.InetSocketAddress;
    12 
    13 
    14 public class MyRestFilter extends RestFilter{
    15     Client client;
    16     public MyRestFilter(Client client){
    17         this.client = client;
    18     }
    19     
    20     @Override
    21     public void process(RestRequest request, RestChannel channel, RestFilterChain filterChain) throws Exception{
    22         try{
    23             if(MethodAuthenticator.getIsPluginEnabled()){
    24                 MyLogger.info(getLogInfo(request));
    25                 String methodStr = request.method().name().toLowerCase();
    26                 if(MethodAuthenticator.isMethodEnabled(methodStr)){
    27                     //MyLogger.info("The request method " + methodStr + " is allowed");
    28                     filterChain.continueProcessing(request, channel);
    29                 } else {
    30                     MyLogger.info("The request method " + methodStr + " is denyed");
    31                     BytesRestResponse res = new BytesRestResponse(RestStatus.FORBIDDEN, "Forbidden Method Request by MyRest plugin");
    32                     channel.sendResponse(res);
    33                 }
    34             } else {
    35                 //MyLogger.info("MyRest is disabled");
    36                 filterChain.continueProcessing(request, channel);
    37             }
    38         } catch (Exception exp) {
    39             channel.sendResponse(new BytesRestResponse(RestStatus.INTERNAL_SERVER_ERROR, "My Rest Filter internal exception"));
    40         }
    41         return ;
    42     }
    43     
    44     private String getLogInfo(RestRequest request) throws Exception {
    45         try{
    46             String pathStr = request.path();
    47             String ipaddr = ((InetSocketAddress)request.getRemoteAddress()).getAddress().getHostAddress();
    48             return "The [" + request.method().name() + "] request [" + pathStr + "] is from [" + ipaddr + "]"; 
    49         } catch (Exception exp) {
    50             MyLogger.error("Request resolve failed: ", exp);
    51         }
    52         return "The request resolve failed";
    53     }
    54 }
    View Code

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

     1 package org.elasticsearch.es.plugin;
     2 
     3 import org.elasticsearch.common.logging.ESLogger;
     4 import org.elasticsearch.common.logging.ESLoggerFactory;
     5 
     6 public class MyLogger {
     7     
     8     private static ESLogger esLogger;
     9     static {
    10         esLogger = ESLoggerFactory.getLogger("myRest");
    11     }
    12     
    13     public static void debug(String msg){
    14         esLogger.debug(msg);
    15     }
    16     
    17     public static void info(String msg){
    18         esLogger.info(msg);
    19     }
    20     
    21     public static void warn(String msg){
    22         esLogger.warn(msg);
    23     }
    24     
    25     public static void error(String msg){
    26         esLogger.error(msg);
    27     }
    28     
    29     public static void error(String msg, Exception exp){
    30         esLogger.error(msg, exp);
    31     }
    32 }
    View Code

    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
    View Code

    我这里以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>
    View Code

    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>1.2.16</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路径下找到。

  • 相关阅读:
    php CodeIgniter处理多环境错误级别配置
    bootstrap导航条在手机上默认展开二级目录,必须用setTimeout才能实现
    WordPress博客网站fonts.useso加载慢解决办法
    JS实现复制网页内容自动加入版权内容代码和原文链接
    bootstrap实现 手机端滑动效果,滑动到下一页,jgestures.js插件
    mysql字段varchar区分大小写utf8_bin、utf8_general_ci编码区别
    js捕捉IE窗口失去焦点事件,判断离开页面刷新或关闭的方法
    php防盗链,php ci在control里面控制除了自己站内的链接点击跳转,其他来源的都跳到站内页面
    php原子操作,文件锁flock,数据库事务
    默认只显示指定高度,出来按钮 阅读更多,加载全文,点击后显示全文的实现方式
  • 原文地址:https://www.cnblogs.com/you-you-111/p/5736830.html
Copyright © 2020-2023  润新知