1. 程式人生 > >Apache Ignite 學習筆記(二): Ignite Java Thin Client

Apache Ignite 學習筆記(二): Ignite Java Thin Client

前一篇文章,我們介紹瞭如何安裝部署Ignite叢集,並且嘗試了用REST和SQL客戶端連線叢集進行了快取和資料庫的操作。現在我們就來寫點程式碼,用Ignite的Java thin client來連線叢集。 在開始介紹具體程式碼之前,讓我們先簡單的瞭解一下Ignite的thin client,以及背後的binary client protocol(*原本打算把thin client翻譯為精簡客戶端,bianry client protocol翻譯為二進位制客戶端協議,但總讀的不順口,就索性就不翻譯了吧-_-|||*)。

Ignite Thin Client和Binary Client Protocol介紹


為了快取資料的高效能讀寫,Ignite是節點內部需要維護一套高效的資料結構來記錄一些元資料,所以如果在自己的應用程式中啟動一個Ignite的server/client節點是要佔用一些應用程式的CPU/記憶體資源的。另外,Ignite是一個分散式系統,單個節點需要和叢集裡的其他節點通訊,又要消耗一些的網路資源。 因此不同於server/client節點,thin client是一個Ignite輕量化的客戶端。為什麼說它是輕量化的呢,因為thin client並不會加入到Ignite叢集的拓撲中(即thin client的連線,斷開不會導致Ignite叢集拓撲發生變化,也不會觸發相應的事件通知),不會儲存任何叢集的資料或是被做為計算節點接收計算任務。Thin client就是一個純粹的客戶端,它純粹到通過最原始的socket連線到Ignite叢集中的某一個節點進行所有的操作。

在Ignite叢集成功啟動後,我們會在日誌看到“Topology snapshot [ver=1, servers=1, clients=0, CPUs=2, offheap=1.6GB, heap=1.0GB]”資訊。這條日誌裡的clients和thin client不是一回事。按照之前介紹,thin client是不會加入Ignite叢集的拓撲中去,理應不會出現在和拓撲相關的日誌中,所以這裡不要把thin client和叢集中的client節點搞混了。

這個連結詳細的介紹了Binary Client Protocol的資料格式(比如採用的是litter-endian,Ignite的二進位制物件描述使用者資料),訊息格式(定義了訊息頭格式,響應訊息的格式,以及連線時如何進行握手保證版本相容等)和各種快取操作所需的操作編碼和格式。 所以只要你按照Binary Client Protocol的要求,你可以用任何一種語言實現一個thin client。目前Ignite釋出了兩種語言的thin client的實現,一個是基於Java實現,一個是基於.Net實現。

當然thin client雖然輕量化,也有它的問題。 Thin client寫資料時只能先把資料發給其連線的節點,再由該節點將資料傳送給叢集內的其他節點做儲存(同理,讀資料也需要通過該節點才能傳給thin client),這樣thin client和這個節點間的網路很有可能就成為瓶頸。而server/client節點間可以互聯,利用資料分割槽,可以充分使用節點間的頻寬。另外,當前連線節點出故障後,thin client只能隨機選擇一個啟動時已知的節點進行重試,由於其不感知叢集拓撲,即便有新節點加入,thin client也無法知道其存在,更不可能和其進行通訊了。 所以還是要根據自己應用的場景,看看到底是server/client節點還是thin client更合適。

Ignite Java Thin Client例子

上一篇文章中,我們用Dbeaver連線Ignite後,用SQL語句進行建表,插入資料和查詢資料。 在這個例子裡,我們就來看看如何通過Ignite的thin client支援的JCache APIs進行相同的操作。

首先我們先用用maven命令建立一個新的project,然後在pom.xml檔案中新增以下ignite-core的依賴:

<dependencies>
        <dependency>
            <groupId>org.apache.ignite</groupId>
            <artifactId>ignite-core</artifactId>
            <version>${ignite.version}</version>
        </dependency>      
</dependencies>

在看具體程式碼之前,讓我們先來大概瞭解一下這個例子中我們要進行哪些操作以及步驟:

  1. 首先,我們通過thin client連線上我們已經啟動的Ignite叢集。
  2. 然後,通過API建立兩個cache(cache的名字分別為provincecity),一個用來存省份資訊,一個用來存城市資訊。 province快取的key是省份的id,city快取的key是城市的名稱。
  3. 我們分別往兩個cache裡寫入一些資料。
  4. 啟動該程式時,可以從命令列傳入多個城市名稱,在終端我們會打印出該城市所在的省份資訊。

下面是主程式的程式碼:

import org.apache.ignite.Ignition;
import org.apache.ignite.client.ClientCache;
import org.apache.ignite.client.ClientException;
import org.apache.ignite.client.IgniteClient;
import org.apache.ignite.configuration.ClientConfiguration;

public class IgniteThinClientExample {
    private static ClientCache<Integer, Province> provinceCache;
    private static ClientCache<String, City> cityCache;

    public static void main(String[] args) {
        System.out.println();
        System.out.println("Ignite thin client example started.");
        //連線到Ignite叢集,預設埠號為10800
        ClientConfiguration cfg = new ClientConfiguration().setAddresses("192.168.0.110:10800");

        //用java的try-with-resource statement啟動client
        try (IgniteClient igniteClient = Ignition.startClient(cfg)){
            System.out.println();
            System.out.println("Begin create cache and insert data.");
            //建立兩個快取,具體步驟見該函式
            creatCacheAndInsertData(igniteClient);

            System.out.println();
            System.out.println("Begin query cache.");
            //根據輸入開始查詢
            for(String city : args)
            {
                //先用城市名字,查詢city快取
                City c = cityCache.get(city);
                Province p = null;
                if (c != null)
                {
                    //在用城市資料中的province id查詢province快取
                    p = provinceCache.get(c.getProvinceId());
                }
                //輸出查詢結果
                if (c != null && p != null) {
                    System.out.println("Find " + c.getName() + " in province " + p.getName());
                }
                else
                {
                    System.out.println("Cannot find " + city + " in any province.");
                }
            }
        }
        catch (ClientException e) {
            System.err.println(e.getMessage());
        }
        catch (Exception e) {
            System.err.format("Unexpected failure: %s\n", e);
        }
    }

    private static void creatCacheAndInsertData(IgniteClient igniteClient)
    {
        //建立province快取,用來存放省份資訊,該快取以省的id為key
        final String PROVINCE_CACHE_NAME = "province";
        provinceCache = igniteClient.getOrCreateCache(PROVINCE_CACHE_NAME);

        //往province快取中寫入一些資料
        int provinceId = 1;
        final Province on = new Province(provinceId++, "Ontario");
        final Province ab = new Province(provinceId++, "Alberta");
        final Province qc = new Province(provinceId++, "Quebec");

        provinceCache.put(on.getId(), on);
        provinceCache.put(ab.getId(), ab);
        provinceCache.put(qc.getId(), qc);
        System.out.println("Successfully insert all provinces data.");

        //建立city快取,用來存放城市資訊,該快取以城市的名字為key
        final String CITY_CACHE_NAME = "city";
        cityCache = igniteClient.getOrCreateCache(CITY_CACHE_NAME);
        //往city快取寫入一些資料
        int cityId = 1;
        final City toronto = new City(cityId++, "Toronto", on.getId());
        final City edmonton = new City(cityId++, "Edmonton", ab.getId());
        final City calgary = new City(cityId++, "Calgary", ab.getId());
        final City montreal = new City(cityId++, "Montreal", qc.getId());

        cityCache.put(toronto.getName(), toronto);
        cityCache.put(edmonton.getName(), edmonton);
        cityCache.put(calgary.getName(), calgary);
        cityCache.put(montreal.getName(), montreal);
        System.out.println("Successfully insert all city data.");
    }
}

public class City {
    private int id;
    private String name;
    private int provinceId;
    ...
}

public class Province {
    private int id;
    private String name;
    ...
}

成功的編譯project後,我們可以啟動這個client看看程式的輸出結果:

$java -cp $IGNITE_HOME/libs/*:./ignite-thin-client-example-1.0-SNAPSHOT.jar IgniteThinClientExample Toronto Markham Edmonton Calgary

Ignite thin client example started.

Begin create cache and insert data.
Successfully insert all provinces data.
Successfully insert all city data.

Begin query cache.
Find Toronto in province Ontario
Cannot find Markham in any province.
Find Edmonton in province Alberta
Find Calgary in province Alberta

因為我們的city快取中,並沒有Markham的資訊,所以查詢Markham的時候我們也找不到對應的省份資訊。但是查詢Toronto,Edmonton和Calgary的時候,程式是能正確的返回其省份資訊的。

在上面的例子中,有幾個需要注意的地方:

  • 在我們連線Ignite叢集的時候,用的埠號10800。預設配置下,Ignite會監聽該埠,等待thin client的連線。如果需要讓Ignite叢集用不同的埠號,可以修改叢集啟動時的配置檔案,加入以下配置項(如果你是用上一篇文章的方法啟動的Ignite叢集, 配置檔案在$IGNITE_HOME/config/default-config.xml):
<bean id="ignite.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
    <!-- Thin client connection configuration. -->
    <property name="clientConnectorConfiguration">
        <bean class="org.apache.ignite.configuration.ClientConnectorConfiguration">
            <property name="host" value="192.168.0.100"/>
            <property name="port" value="新的埠號"/>
            <property name="portRange" value="30"/>
        </bean>
    </property>  
</bean>
  • 我們的插入和查詢資料的時候,只用到了最簡單快取API--put和get。其實Ignite快取的查詢API比其他快取更豐富,支援scan/SQL/text方式進行查詢,這個我們會在後面的文章慢慢介紹。

  • 我們用城市的名字查詢完city快取後,再拿著province id去查詢province快取。這兩步查詢其實是可以合併成一個SQL的join查詢,在後面介紹SQL查詢時,我們再來看看把兩個快取當做兩張表做join操作。

總結

這篇文章我們介紹了Ignite的thin client,並用Ignite的Java think client實現了一個簡單的例子,解釋如何用thin client連線上Ignite叢集,建立快取,寫入資料以及查詢。 完整的程式碼和maven工程檔案戳這裡

下一篇文章,我們看看如何在自己的Java程式碼裡啟動Ignite叢集及其相關配置。