Flink是目前流計算的隱形王者,在國際國內有有龐大的擁躉。
Nebula是國產圖資料庫的後起之秀,在DBEngines中排名也逐年上升。
將兩者進行結合,可以產生很多應用場景:比如實時計算服務鏈路呼叫關係並將結果存到Nebula中、實時計算業務訪問風控情況並將結果存到Nebula中、實時計算預警發生情況並將結果存到Nebula中等。
將Flink計算完畢後的結果,Sink到Nebula,Nebula官方提供了一個Flink Connector,但是很不易用。
筆者根據專案實際應用情況,寫了一個更簡潔直接的Sink,作為拋磚引玉,歡迎各位Flink及Nebula愛好者共同交流。
一、NebulaUtil
由於Nebula提供的Java Client是非執行緒安全的,所以我們首先封裝一個單例的NebulaUtil,主要程式碼如下:
import lombok.val;
import lombok.var;
/** * Nebula工具類 */ public class NebulaUtil { // Nebula會話 private Session session = null; // Nebula連線池 private NebulaPool pool = new NebulaPool();/** * 獲得Nebula工具類單例 * * @return NebulaUtil */ public static NebulaUtil getInstance() { return NebulaUtilHolder.instance; } /** * 執行NGQL * * @param nGQL NGQL * @return 返回執行結果 */ public ResultSet execute(String nGQL) { try { if (session != null) { return session.execute(nGQL); } } catch (IOErrorException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return null; } /** * 釋放會話 */ public void releaseSession() { // 釋放連線 if (session != null) { session.release(); } // 關閉連線池 pool.close(); } private static class NebulaUtilHolder { private static final NebulaUtil instance = new NebulaUtil(); } private NebulaUtil() { initSession(); } /** * 初始化會話 */ private void initSession() {// 連線地址,多個間用逗號“,”隔開 val host = "127.0.0.1"; val port = 9669; val user = "user"; val password = "password"; val space = "MySpace"; var nebulaPoolConfig = new NebulaPoolConfig(); nebulaPoolConfig.setMaxConnSize(100); var hostAddressList = new ArrayList<HostAddress>(); val hostArray = host.split(","); for (val hostAddress : hostArray) { hostAddressList.add(new HostAddress(hostAddress, port)); } try { pool.init(hostAddressList, nebulaPoolConfig); } catch (UnknownHostException e) { e.printStackTrace(); } try { session = pool.getSession(user, password, false); } catch (NotValidConnectionException e) { e.printStackTrace(); } catch (IOErrorException e) { e.printStackTrace(); } catch (AuthFailedException e) { e.printStackTrace(); } // 切換圖空間 val resp = execute(String.format("USE %s;", space)); if (resp == null || !resp.isSucceeded()) { System.out.println("切換圖空間失敗!" + space); } } }
二、NebulaSink
有了NebulaUtil,實現NebulaSink就非常簡單了,每個方法裡只有幾行程式碼:
import lombok.val;
/** * Sink到Nebula資料庫 */ public class NebulaSink extends RichSinkFunction<List<String>> { /** * 開啟連線 * * @param parameters 配置引數 */ @Override public void open(Configuration parameters) { } /** * 呼叫 * * @param nGQLList NGQL列表 * @param context 上下文 */ @Override public void invoke(List<String> nGQLList, Context context) { for (val nGQL : nGQLList) { NebulaUtil.getInstance().execute(nGQL); } } /** * 關閉連線 */ @Override public void close() throws Exception { super.close(); NebulaUtil.getInstance().releaseSession(); } }
三、將Vertex及Edge資料組裝成NGQL語句
有了NebulaUtil以及NebulaSink後,Sink到Nebula之前,我們主要的工作就是將Vertex及Edge資料,組裝對應的NGQL語句即可。