elasticsearch之Java調用本地代碼

雖然Java虛擬機爲開發人員屏蔽了底層的實現細節,使得開發人員不用考慮底層操做系統的差別性。不過在某些應用程序中,仍是免不了要直接與底層操做系統上的原生代碼進行交互。今天咱們就來看一下Java對本地調用提供的支持。java

1、爲何要進行本地調用windows

1.基於性能的考慮elasticsearch

Java語言從其運行速度上來講,在大多數方面是慢於底層操做系統上原生的C和C++等語言的。這主要是因爲Java虛擬機這個中間層次的存在。若是徹底用Java語言實現的性能沒法達到程序的預期要求,能夠選擇把部分重要且耗時的代碼用C或C++來實現。工具

2.基於某些特殊的需求性能

Java平臺提供的標準類庫的功能很強大,包括了在開發中可能遇到的大部分功能。可是仍然有一些功能沒法用標準API來實現,主要是一些與底層硬件平臺直接交互的功能。Java虛擬機沒有把這一部分功能暴露給運行在其上的程序。若是須要這方面的功能,那麼只能使用原生代碼來進行開發。ui

3.與已有的使用原生代碼編寫的程序之間進行集成。this

若是Java程序須要與底層操做系統上由C和C++語言開發的程序進行交互,那麼能夠進行本地調用。操作系統

咱們平時的開發更多的狀況是後邊兩種狀況;在elasticsearch中基本上是屬於第二種狀況。命令行

2、使用JNI實現本地調用code

針對以上提到的各類狀況,Java提供了JNI(Java Native Interface)和JNA(Java Native Access)兩種方式,其中JNI的一個重要使用場景是提升程序的性能。當對程序中關鍵部分的性能要求比較高的時候,可使用C和C++代碼來實現。

咱們先來看下怎麼使用JNI來進行本地調用。

首先咱們須要有一個Java類來聲明本地方法,並負責加載本地代碼庫。本地方法與Java接口中的方法或抽象類中的抽象方法同樣,只包含方法聲明,沒有相關的實現。程序中的其餘部分能夠用正常的方法調用本地方法,好比參數傳遞和返回值使用等都與正常的方法相同。當虛擬機在執行本地方法時,會嘗試在已經加載的本地代碼庫中查找本地方法的對應實現。在查找到對應的實現方法以後,虛擬機會負責進行參數傳遞、實際方法調用和返回值傳遞等工做。

public class HelloNative {
    static{
        System.loadLibrary("greetLib");
    }

    public static native  void greeting();
}

下一步要編寫實現本地方法的C/C++代碼。Java提供的命令行工具根據Java源代碼生成C/C++代碼所需的頭文件。對於本地方法,頭文件中會包含相關的方法聲明與其對應。

F:\source\JNI\src>javac -h .\ .\HelloNative.java

經過下邊自動生成的頭文件,咱們能夠看到這裏有不少的隱式約定,咱們只要按照這個聲明進行實現便可,具體的規則不是今天的重點,不進行詳述。

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloNative */

#ifndef _Included_HelloNative
#define _Included_HelloNative
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     HelloNative
 * Method:    greeting
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_HelloNative_greeting
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

3、elasticsearch使用JNA實現本地調用

經過上邊對JNI的簡單瞭解,咱們更多的時候碰到的狀況是,在編寫Java程序以前,就已經有了可使用的本地代碼庫。這個本地代碼庫多是程序的一部分,也多是底層操做系統自帶的。這些本地代碼庫的特色是在實現的時候並無考慮與Java虛擬機的集成,所以也沒有使用與JNI相關的內容。在使用這樣的本地代碼庫時,咱們就須要一箇中間的本地代碼庫做爲橋樑。這個本地代碼庫做爲Java程序中本地方法的實現,負責實際調用時的參數類型轉換和返回值傳遞等工做。這個過程是十分的繁瑣的,Java提供了JNA來支持這種狀況。

咱們知道elasticsearch啓動的時候須要檢測當前用戶是不是root用戶,這個檢測是直接調用的底層操做系統的代碼,咱們來看下elasticsearch是怎樣使用JNA實現的。

首先elasticsearch提供了Natives類,做爲調用本地方法的入口,並負責檢測JNA的可用性。

static {
        boolean v = false;
        try {
            // load one of the main JNA classes to see if the classes are available. this does not ensure that all native
            // libraries are available, only the ones necessary by JNA to function
            Class.forName("com.sun.jna.Native");
            v = true;
        } catch (ClassNotFoundException e) {
            logger.warn("JNA not found. native methods will be disabled.", e);
        } catch (UnsatisfiedLinkError e) {
            logger.warn("unable to load JNA native support library, native methods will be disabled.", e);
        }
        JNA_AVAILABLE = v;
    }

檢測JNA是否可用,而後再調用JNANatives的對用方法

static boolean definitelyRunningAsRoot() {
        if (!JNA_AVAILABLE) {
            logger.warn("cannot check if running as root because JNA is not available");
            return false;
        }
        return JNANatives.definitelyRunningAsRoot();
    }

在JNANatives的definitelyRunningAsRoot中,若是是非windows系統,則調用
JNACLibrary.geteuid

/** Returns true if user is root, false if not, or if we don't know */
    static boolean definitelyRunningAsRoot() {
        if (Constants.WINDOWS) {
            return false; // don't know
        }
        try {
            return JNACLibrary.geteuid() == 0;
        } catch (UnsatisfiedLinkError e) {
            // this will have already been logged by Kernel32Library, no need to repeat it
            return false;
        }
    }

elasticsearch使用JNAKernel32Library來封裝對windows的Kernel32的調用,使用 JNACLibrary來封裝對非windows系統的libc的調用

static {
        try {
            Native.register("c");
        } catch (UnsatisfiedLinkError e) {
            logger.warn("unable to link C library. native methods (mlockall) will be disabled.", e);
        }
    }

    static native int mlockall(int flags);

    static native int geteuid();

4、總結

  1. JNI更適合使用本地調用來解決對性能有更高要求的場景,須要咱們本身使用C或者C++來實現處理邏輯。
  2. 對於調用已有的本地庫的方法或者操做系統的方法,使用JNA更爲方便便捷。
相關文章
相關標籤/搜索