如何實現集羣的。java
測試代碼編寫node
咱們首先寫一個demo,該demo在沒有terracotta的環境下執行一次,看看結果
咱們首先先寫一個簡單的多線程代碼(咱們這個例子制定共享TerracottaDemo類的demo對象,它包含的count和yale對象也就隨之被整個集羣共享了):linux
package yale.terracotta.demo; public class TerracottaDemo implements Runnable { private static TerracottaDemo demo = new TerracottaDemo(); private Object yale = new Object(); private int count = 0; @Override public void run() { while (true) { synchronized (yale) { count++; System.out.println(Thread.currentThread().getName() + " count:" + count); } try { Thread.sleep((int) (1000 + Math.random())); } catch (Exception e) { e.printStackTrace(); } } } public static void main(String[] args) { new Thread(demo).start(); new Thread(demo).start(); } }
該class文件存放在:web
執行,開啓不一樣的進程進行執行,看看結果:算法
上述執行環境是在win下,經過以上的執行狀況咱們能夠看出,正常狀況下各個進程調用各自JVM中的對象,並無任何的共享express
下載、安裝
下載Terracotta,下載前須要註冊賬號才能進行下載:
http://terracotta.org/:apache
從上面的信息中,咱們能夠看到,註冊成功後進行郵件驗證,驗證成功後點擊網站」open source」後,能夠對其產品Ehcache、Quartz、BigMemory下載,咱們下載terracotta (目前最高版本: terracotta-3.6.2.tar.gz、terracotta-ee-3.6.2-installer.jar)後:
安裝方法一:解壓相應的tar文件到相應的目錄便可(Linux版本)便可
安裝方法二:經過java –jar terracotta-ee-3.6.2-installer.jar
其實解壓後的文件夾中,包含了相應的ehcache、quartz的相關產品緩存
Terracotta Server配置方式
一、 單機,無持久化:服務器把集羣要管理的數據保存在內存中,當數據量大於服務器可用內存的時候,會發生內存溢出錯誤。這種模式通常只在開發中使用;
二、 單機,持久化:服務器把集羣要管理的數據保存在硬盤中,利用服務器上的內存做爲緩存,以提升經常使用數據的訪問速度。當數據量大於服務器可用內存的時候,服務器會把不經常使用數據從內存中移除,這樣就不會發生內存溢出問題。當服務器宕機,而後被重新啓動之後,硬盤中的數據被重新激活,這樣集羣中共享的數據不會丟失。這種配置提供了必定的災難恢復(Fail over)的能力,可是仍是沒法作到高可用性(HA);
三、 雙機或者多機鏡像(mirroring):通常由兩臺或者多臺物理服務器互爲鏡像。其中一臺做爲主服務器支持集羣運行。其它備用服務器只是對數據作鏡像,而且監視主服務器的狀態。當主服務器發生故障宕機的時候,其中一臺備用服務器自動升級爲主服務器,接管整個集羣的支撐工做。這樣一來整個集羣還繼續正常運行,不會受任何影響。這種配置能夠實現高可用性。通常對於這種配置模式,咱們還把服務器數據配置爲持久化模式,可是若是內存數量不是問題,用戶也能夠選擇非持久化;
四、 服務器陣列分片模式(Server Array Striping):這是Terracotta FX系列產品獨有的高端企業級特性,它主要用於提升集羣性能。當集羣中數據量和數據訪問頻率過高的時候,能夠配置多臺服務器,分別負責一部分集羣數據的服務。好比集羣共享數據達到1G個對象,若是用5臺服務器作分片,每一臺服務器能夠負責2千萬個對象。這樣就實現了Terracotta服務器的負載均衡。這種數據分片的策略,也就是說哪一個數據對象保存在哪一個服務器上,對開發人員和實施維護人員是徹底透明的。當服務器吞吐量不能知足要求的時候,用戶能夠考慮修改代碼,對共享數據和應用系統中的數據訪問算法進行優化;也能夠簡單地增長陣列分片服務器數量。後者每每是性價比比較高的方式。用戶還能夠考慮讓兩臺服務器互爲鏡像,讓多個鏡像再組合成陣列分片。這樣每一個鏡像作到高可用性,多個鏡像在一塊兒,實現集羣性能的提升;服務器
配置Terracotta集羣
環境的準備工做(此次咱們在linux下進行,3臺服務器上進行,主節點服務器(192.168.2.11),子節點(192.168.2.十一、192.168.2.2一、192.168.2.221),如今咱們把這個代碼打jar包後放在下面配置的集羣上,讓多個JVM共同訪問一個計數器)
一、 建立tc-config.xml文件,存放到terracotta根目錄下(能夠經過config-samples文件夾下的tc-config-express-reference.xml文件進行修改),該文件是描述client節點在TC Server中行爲的惟一信息,也是咱們的程序做爲Terracotta Client節點添加時主要的內容(爲了可以讓任何節點都在不修改的狀況下都能成爲主節點,我在配置文件中配置了一些冗餘的信息,以及在每一個節點都創建了相同的文件夾):
每一個節點都有紅框中的文件夾網絡
配置文件(一個完整的tx-config.xml文件,附帶有文件屬性說明,每一個節點內容都同樣,建立好後,能夠直接拷貝到其餘節點):
<?xml version="1.0" encoding="UTF-8" ?> <tc:tc-config xmlns:tc="http://www.terracotta.org/config" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.terracotta.org/schema/terracotta-6.xsd"> <!-- # 配置TERRACOTTA CLUSTER的優化屬性,能夠放在這裏,也能夠放在TC.PROPERTIES文件中,TERRACOTTA依如下次序讀取屬性配置 # 一、安裝JAR # 二、本XML文件的tc-config節 # 三、tc.properties文件 # 四、系統屬性定義 --> <tc-properties> <!-- <property name="l1.cachemanager.percentageToEvict" value="10"/> <property name="l1.cachemanager.sleepInterval" value="3000"/> <property name="l1.cachemanager.criticalThreshold" value="90"/> <property name="l1.cachemanager.threshold" value="70"/> <property name="l1.cachemanager.monitorOldGenOnly" value="true"/> --> <property name="l2.nha.dirtydb.autoDelete" value="true" /> <property name="l1.cachemanager.enabled" value="true" /> <property name="logging.maxLogFileSize" value="1024" /> </tc-properties> <!-- SYSTEM這一節記錄一些影響Terracotta全局的數據 --> <system> <!--default:development can setup "production" --> <configuration-model>development</configuration-model> </system> <!-- Servers節點內,用來指定Servers Array裏全部服務器,TC Server經過子節點<dso-port>來配置服務監聽端口爲9510, 使TC client與DSO模式協同工做 --> <servers> <server host="192.168.2.11" name="oraclerac1" bind="192.168.2.11"> <!--當配置以持久方式(persistent)保存數據時候的數據存放地址--> <data>/usr/java/terracotta/server-data</data> <!--日誌存放地址--> <logs>/usr/java/terracotta/server-logs</logs> <index>/usr/java/terracotta/server-index</index> <!--供客戶端調用的端口--> <dso-port>9510</dso-port> <!--供jmx調用的端口--> <jmx-port>9520</jmx-port> <!--server間的監聽端口--> <l2-group-port>9530</l2-group-port> <!-- 一個空的<authentication/>表明使用JAVA默認的JMX認證方式,須要修改:$JAVA_HOME/jre/lib/management/jmxremote.password 增長一行 用戶 密碼 # $JAVA_HOME/jre/lib/management/jmxremote.access, 增長一行 用戶 readwrite # 同時要執行 # 一、chmod 500 jmxremote.password 二、chown <啓動TC-SERVER的用戶> jmxremote.password --> <authentication /> <!-- # 定義terracotta http server 訪問用戶管理文件名,文件格式爲 # username: password [,rolename ...] # rolename目前只有statistics,容許收集統計數據 <http-authentication> <user-realm-file>/usr/java/terracotta/realm.properties</user-realm-file> </http-authentication> --> <dso> <!-- 定義在server 啓動後多少秒內,能夠鏈接? --> <client-reconnect-window>120</client-reconnect-window> <!-- 定義DSO對象的持久性保存方式 # temporary-swap-only-方式只臨時使用下磁盤,比permanent-store方式要快些 # permanent-store-方式只有變化當即寫入磁盤,更有利於SERVER異常後的數據恢復。 # 默認爲temporary-swap-only方式 --> <persistence> <mode>permanent-store</mode> </persistence> <garbage-collection> <!-- 配置分佈式JVM垃圾的回收方式,true表明自動回收,false模式下只有在'run-dgc'腳本被調用的狀況纔回收 --> <enabled>true</enabled> <!-- 配置爲TRUE在分佈式垃圾回收的時候是否寫額外信息到日誌中,有利於系統優化 --> <verbose>false</verbose> <!-- 分佈式垃圾回收時間間隔,單位秒 --> <interval>3600</interval> </garbage-collection> </dso> </server> <server host="192.168.2.21" name="oraclerac2"> <data>/usr/java/terracotta/server-data</data> <logs>/usr/java/terracotta/server-logs</logs> <index>/usr/java/terracotta/server-index</index> <dso-port>9510</dso-port> <jmx-port>9520</jmx-port> <l2-group-port>9530</l2-group-port> <authentication /> <dso> <client-reconnect-window>120</client-reconnect-window> <persistence> <mode>permanent-store</mode> </persistence> <garbage-collection> <enabled>true</enabled> <verbose>false</verbose> <interval>3600</interval> </garbage-collection> </dso> </server> <server host="192.168.2.221" name="dataguard"> <data>/usr/java/terracotta/server-data</data> <logs>/usr/java/terracotta/server-logs</logs> <index>/usr/java/terracotta/server-index</index> <dso-port>9510</dso-port> <jmx-port>9520</jmx-port> <l2-group-port>9530</l2-group-port> <authentication /> <dso> <client-reconnect-window>120</client-reconnect-window> <persistence> <mode>permanent-store</mode> </persistence> <garbage-collection> <enabled>true</enabled> <verbose>false</verbose> <interval>3600</interval> </garbage-collection> </dso> </server> <ha> <!--下面的mode咱們選用了networked-active-passive方式, 表示DSO數據是存放在不一樣的TC Serer上的, 數據的同步經過 網絡數據來交換完成,該模式下的active和passive其實是經過 狀態檢查和投票產生的, 而另一種方式disk-based-active-passive表示 TC serers的DSO數據是存放在同一個存儲設備上的, 不一樣的TC serers 經過網絡文件系統等方式在配置文件的<data>屬性中被引用,該模式下的active和 passive是經過disk lock來完成的 --> <mode>networked-active-passive</mode> <networked-active-passive> <!--心跳檢查間隔,單位秒--> <election-time>5</election-time> </networked-active-passive> </ha> <update-check> <!--運行時候是否進行Terracotta版本檢查,會鏈接Terracotta.org--> <enabled>true</enabled> <!--檢查間隔天數,默認爲7--> <period-days>10</period-days> </update-check> </servers> <!--設置影響全部鏈接到系統的client--> <clients> <!--告訴dso把TC client的日誌放在哪裏,可使用參數 %h表明hostname, %i表明IP地址, 默認爲啓動client的目錄的相對目錄,也可使用絕對路徑--> <logs>/usr/java/terracotta/client-logs/pojo/%i</logs> </clients> <application> <dso> <!-- 定義那些class應該有terracotta來構建,即應該在jvm進行cluster和共享,能夠經過定義包含(include)及 排除 (exclude)兩種方式來配置 --> <instrumented-classes> <!--添加自定義的對象/類被共享,可是這個類中有的字段是被描述成"transient"的,仍是應該 保持"transient"字段應有的特性, 經過設置<honor-transient>爲'true',已經聲明成"transient"的 字段他們的狀態和值不會在不一樣應用的實例間可用,只有本地的 應用實例能夠建立,讀,寫這些字段, 若是應用程序有對其依賴的包,此處還需進行添加 --> <include> <class-expression> yale.terracotta.demo.TerracottaDemo </class-expression> <!--若是設置爲false,那麼全部標示爲臨時對象(transient)的類都要求使用terracotta來構建--> <honor-transient>true</honor-transient> <!-- 定義在裝載類時候要執行的動做: 若是是java類方法,使用method,注意method不能有參數,調用腳本, 使用execute 若是配置了onload,那麼method和execute 2者必須配置一種 <on-load><method></method></on-load> --> </include> </instrumented-classes> <!-- 列出臨時屬性field,即不須要在cluster、shared的屬性列表 <transient-fields> <field-name>xx.yy.zz</field-name> <field-name>xx.yy.zz</field-name> </transient-fields> --> <!-- 告知DSO哪些應用在你的web容器中使用DSO,對於session內共享對象是否使用auto-lock模式自動進行管理, 能夠經過設置session-locking值來決定,若是設置爲false,就不進行auto-lock自動模式管理,而是須要應用進行控制, 但不管哪一種模式,經過HttpSession對象進行操做,好比setAttribute(), setMaxInactiveInterval()仍然自動會鎖 <web-applications> <web-application>yale_app</web-application> <web-application session-locking="false">yale_app1</web-application> </web-applications> --> <roots> <root> <!--變爲全局變量--> <field-name> yale.terracotta.demo.TerracottaDemo.demo </field-name> <!-- <root-name></root-name> <distributed-methods> <method-expression></method-expression> </distributed-methods> 使這些字段「transient」,這樣這些值就只能在本地上是可用的 <transient-fields> <field-name></field-name> </transient-fields> --> </root> </roots> <!-- 分佈式方法調用,當某個method在一個JVM被調用後,整個cluster下jvm都調用此method,經常使用於事件監聽 <distributed-methods> 設置爲false,那麼只有在method歸屬對象在jvm已經建立,method才被調用,默認爲true <method-expression run-on-all-nodes="false">xx.yy.zz</method-expression> </distributed-methods> --> <!-- 能夠經過將應用放在同一應用組中來共享class,但必須將應用放在不一樣Terracotta節點中, 好比放在不一樣web server實例中, 目前Terracotta不支持在同一節點中共享不一樣應用的class 同時能夠經過named-classloader指定class 裝載類 <app-groups> <app-group name="petstore-group"> <web-application>yale_app</web-application> <web-application>yale_app1</web-application> <named-classloader>Standard.system</named-classloader> </app-group> </app-groups> --> <!-- 默認爲TURE,啓用 mutations方式來影射共享對象 <dso-reflection-enabled>true</dso-reflection-enabled> --> <!-- 本節用於設置自定義的鎖,鎖能夠分爲自動鎖(autolock)和命名鎖(named-lock) # 鎖的級別能夠分爲: # 一、寫鎖write # 二、同步寫鎖synchronous-write # 三、讀鎖read # 四、併發鎖 concurrent # 其中併發鎖必定要當心使用, 併發容許同時寫一個對象。 --> <locks> <!-- 對一個已經聲明爲共享的對象進行操做,告訴DSO,當調用這些對象的時候, 假設給它加上 了一把持久的鎖。 autolock鎖能夠將你指望的方法,經過java的同步機制(block和method)來進行管理, 對於沒有定義爲synchronized的對象,須要設置auto-synchronized=true,好比<autolock auto-synchronized=true> name-lock 徹底依賴於java的synchronization機制,能夠對鎖進行命名以方便管理 例子中給TerracottaDemo.run()方法定義了自動鎖(autolock)。 他告訴Teraccotta當這個方法對共享的數據加鎖的時候(TerracottaDemo.yale對象是共享的),使得這個鎖在整個集羣範圍內生效。 這樣一來集羣中任何一個線程鎖住這個對象的時候,其它任何線程都要等這個鎖解除 後才能訪問被保護的數據(TerracottaDemo.count)。 這樣計數器的訪問也就在整個集羣中獲得了保護 --> <autolock> <method-expression> void yale.terracotta.demo.TerracottaDemo.run() </method-expression> <lock-level>write</lock-level> </autolock> </locks> </dso> </application> </tc:tc-config>
二、 拷貝tc-config.xml(上面已經存在該文件了)文件到各個linux服務器上(存放在terracotta根目錄下)
三、 把上面的線程的代碼例子打成jar包,拷貝到各個linux服務器上(存放在terracotta根目錄下)
主節點操做如下命令(192.168.2.11):
進入到$TC_HOME/bin目錄,執行start-tc-server.sh,未執行參數-f<tc-config.xml>啓動時,啓動程序會使用tc.jar包裏自帶的默認配置文件’com/tc/config/schema/setup/default-config.xml’:
咱們不採用上面的啓動方式,咱們啓動指定的配置文件:
咱們能夠看到terracotta server已經啓動成功
子節點操做如下命令(192.168.2.十一、192.168.2.2192.168.2.221):
咱們依次啓動3個子節點服務器後,能夠看到控制檯打印的結果(控制檯顯示客戶端已經成功鏈接到服務器192.168.2.11:9510,咱們能夠看到計數器仍然在累加,在全局範圍內共享):
可見計數器已經在集羣中被3個Java程序實例所共享。每一個程序有兩個線程訪問計數器。這樣整個集羣中實際上有6個線程在同時累加計數器, 從上面能夠看到,整個Java代碼沒有做任何改動。只是增長了一個tc-config.xml文件,從tc-config.xml文件中的配置內容能夠看出,terracotta仍是作了不少的工做的,並且已經比較完善,其實無論它是結合本身的產品ehcache、quartz進行整合,仍是結合apache下的相關產品進行整合,terracotta能夠整合的產品較多,所以咱們也沒有必要一個一個去搭建,它們的整合過程只是在配置的方式上有所不一樣,其實咱們在深刻了解它的原理後在進行其餘產品的整合,其實都是一個簡單的過程。