Java調用elasticsearch本地代碼的操作方法
雖然Java虛擬機為開發人員屏蔽瞭底層的實現細節,使得開發人員不用考慮底層操作系統的差異性。不過在某些應用程序中,還是免不瞭要直接與底層操作系統上的原生代碼進行交互。今天我們就來看一下Java對本地調用提供的支持。
一、為什麼要進行本地調用
1.基於性能的考慮
Java語言從其運行速度上來說,在大多數方面是慢於底層操作系統上原生的C和C++等語言的。這主要是由於Java虛擬機這個中間層次的存在。如果完全用Java語言實現的性能無法達到程序的預期要求,可以選擇把部分重要且耗時的代碼用C或C++來實現。
2.基於某些特殊的需求
Java平臺提供的標準類庫的功能很強大,包括瞭在開發中可能遇到的大部分功能。但是仍然有一些功能無法用標準API來實現,主要是一些與底層硬件平臺直接交互的功能。Java虛擬機沒有把這一部分功能暴露給運行在其上的程序。如果需要這方面的功能,那麼隻能使用原生代碼來進行開發。
3.與已有的使用原生代碼編寫的程序之間進行集成。
如果Java程序需要與底層操作系統上由C和C++語言開發的程序進行交互,那麼可以進行本地調用。
我們平時的開發更多的情況是後邊兩種情況;在elasticsearch中基本上是屬於第二種情況。
二、使用JNI實現本地調用
針對以上提到的各種情況,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
三、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();
四、總結
JNI更適合使用本地調用來解決對性能有更高要求的場景,需要我們自己使用C或者C++來實現處理邏輯。對於調用已有的本地庫的方法或者操作系統的方法,使用JNA更為方便便捷。
到此這篇關於Java調用elasticsearch本地代碼的文章就介紹到這瞭,更多相關Java調用elasticsearch內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- None Found