顯示具有 GridGain 標籤的文章。 顯示所有文章
顯示具有 GridGain 標籤的文章。 顯示所有文章

2007年12月27日 星期四

設定 GirdGain Wndows Service

環境說明

這個部分要說明如何將 GridGain 設定為 Windows Service,以下說明一下實驗的環境:

GridGain 版本:1.6.1

安裝路徑(%GRIDGAIN_HOME%):C:\gridgain-1.6.1

這個部分有很重要的地方一定要先作,就是在「環境變數 -> 系統變數」中設定 GRIDGAIN_HOME!!

由於安裝 GRIDGAIN 時,會在「環境變數 -> 使用者變數」中設定 GRIDGAIN_HOME,但僅有這個是沒辦法將 GridGain 設定為 Windows service 的,因此在進行下面設定時要將 %GRIDGAIN_HOME% 系統變數補上!


下載 Java Service Wrapper

首先要下載 Java Service Wrapper,可以到 http://wrapper.tanukisoftware.org 下載;由於是要用在 Windows 上,因此要選擇「wrapper-windows-x86-32-x.x.x.rar」。

【註】目前似乎沒有 Windows X86 64 bit 的版本可供下載........

下載完之後,將檔案解開,接著我們以 %WRAPPER_HOME% 作為檔案解開後存放的目錄(假設為 D:\wrapper-windows-x86-32),要進行以下動作:
  1. 複製檔案 %WRAPPER_HOME%\bin\wrapper.exe【D:\wrapper-windows-x86-32\bin\wrapper.exe】 到 %GRIDGAIN_HOME%\bin【C:\gridgain-1.6.1\bin】 目錄中。

  2. 複製目錄 %WRAPPER_HOME%\lib【D:\wrapper-windows-x86-32\lib】 中的檔案 wrapper.jar 與 wrapper.dll 到 %GRIDGAIN_HOME%\lib【C:\gridgain-1.6.1\lib】 目錄中


編輯設定檔

接著要編輯 Java Service Wrapper 設定檔,檔名為「wrapper.conf」(其實可以自己命名)

以下為設定檔內容:(要根據實際的情況進行修改喔!!)
# GridGain Daemon/Service Properties

# Java Application
wrapper.java.command=java

# Java Main class. See the Integration section
# of the documentation for details.
wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperSimpleApp

# Java Classpath (include wrapper.jar) Add class path elements as
# needed starting from 1
wrapper.java.classpath.1=C:\gridgain-1.6.1\libs\wrapper.jar
wrapper.java.classpath.2=C:\gridgain-1.6.1\libs\activation-1.0.2.jar
wrapper.java.classpath.3=C:\gridgain-1.6.1\libs\ant-1.6.5.jar
wrapper.java.classpath.4=C:\gridgain-1.6.1\libs\aspectjrt-1.5.3.jar
wrapper.java.classpath.5=C:\gridgain-1.6.1\libs\aspectjweaver-1.5.3.jar
wrapper.java.classpath.6=C:\gridgain-1.6.1\libs\commons-logging-1.0.3.jar
wrapper.java.classpath.7=C:\gridgain-1.6.1\libs\commons-net-1.4.1.jar
wrapper.java.classpath.8=C:\gridgain-1.6.1\libs\concurrent-1.3.jar
wrapper.java.classpath.9=C:\gridgain-1.6.1\libs\imap-1.4.jar
wrapper.java.classpath.10=C:\gridgain-1.6.1\libs\jakarta-oro-2.0.8.jar
wrapper.java.classpath.11=C:\gridgain-1.6.1\libs\javassist-4.0.4.jar
wrapper.java.classpath.12=C:\gridgain-1.6.1\libs\jboss-serialization-1.0.2.jar
wrapper.java.classpath.13=C:\gridgain-1.6.1\libs\jgroups-all-2.2.9.3.jar
wrapper.java.classpath.14=C:\gridgain-1.6.1\libs\jms-1.1.jar
wrapper.java.classpath.15=C:\gridgain-1.6.1\libs\jmxri-1.2.1.jar
wrapper.java.classpath.16=C:\gridgain-1.6.1\libs\jmxtools-1.2.1.jar
wrapper.java.classpath.17=C:\gridgain-1.6.1\libs\jnpserver-4.0.4.jar
wrapper.java.classpath.18=C:\gridgain-1.6.1\libs\log4j-1.2.15.jar
wrapper.java.classpath.19=C:\gridgain-1.6.1\libs\mailapi-1.4.jar
wrapper.java.classpath.20=C:\gridgain-1.6.1\libs\pop3-1.4.jar
wrapper.java.classpath.21=C:\gridgain-1.6.1\libs\smtp-1.4.jar
wrapper.java.classpath.22=C:\gridgain-1.6.1\libs\spring-2.0.jar
wrapper.java.classpath.23=C:\gridgain-1.6.1\libs\tidy-04aug2000-r7.jar
wrapper.java.classpath.24=C:\gridgain-1.6.1\libs\trove-1.0.2.jar
wrapper.java.classpath.25=C:\gridgain-1.6.1\gridgain-1.6.1.jar

# Java Library Path (location of Wrapper.DLL or libwrapper.so)
wrapper.java.library.path.1=C:\gridgain-1.6.1\libs

# Java Additional Parameters
wrapper.java.additional.1=-ea
wrapper.java.additional.2=-Dcom.sun.management.jmxremote
wrapper.java.additional.3=-Xms256m
wrapper.java.additional.4=-Xmx512m

# Application parameters. Add parameters as needed starting from 1
wrapper.app.parameter.1=org.gridgain.grid.loaders.cmdline.GridCommandLineLoader
wrapper.app.parameter.2=C:\gridgain-1.6.1\config\default-spring.xml

#********************************************************************
# Wrapper Windows Properties
#********************************************************************
# Title to use when running as a console
wrapper.console.title=GridGain-1.6.1

#********************************************************************
# Wrapper Windows NT/2000/XP Service Properties
#********************************************************************
# WARNING - Do not modify any of these properties when an application
# using this configuration file has been installed as a service.
# Please uninstall the service before modifying this section. The
# service can then be reinstalled.

# Name of the service
wrapper.ntservice.name=GridGain-1.6.1

# Display name of the service
wrapper.ntservice.displayname=GridGain-1.6.1

# Description of the service
wrapper.ntservice.description=GridGain Node

# Mode in which the service is installed. AUTO_START or DEMAND_START
wrapper.ntservice.starttype=AUTO_START

# Allow the service to interact with the desktop.
wrapper.ntservice.interactive=false

其中在 classpath 的部分,由於版本不同的關係,在官方網站上的 library 版本與實際安裝有出入,因此我有進行修改!


安裝、啟動、停止、移除 GridGain Service

以上步驟都完成後,就可以安裝 GridGain Service 囉! 選擇「開始 -> 執行」,輸入以下指令執行以下操作:

1、安裝 service
%GRIDGAIN_HOME%\bin\wrapper.exe -i %GRIDGAIN_HOME%\config\wrapper.conf
2、啟動 service
%GRIDGAIN_HOME%\bin\wrapper.exe -t %GRIDGAIN_HOME%\config\wrapper.conf
3、停止 service
%GRIDGAIN_HOME%\bin\wrapper.exe -p %GRIDGAIN_HOME%\config\wrapper.conf
4、移除 service
%GRIDGAIN_HOME%\bin\wrapper.exe -r %GRIDGAIN_HOME%\config\wrapper.conf

安裝完 GridGain service 之後,就可以在服務管理的頁面上進行管理囉!

2007年12月19日 星期三

GridGain 程式開發(9) - Gridify With Failover

在這個部分中,要介紹 GridGain 中 Failover 的機制,以及使用方式的說明,分成以下幾個部分:

Grid Job Failover 機制

在 GridGain 中提供了 Failover 的機制! 這個部分在分散式運算中是相當重要的,讓 job 在無法抗拒的原因下執行失敗後,可以轉移到其他的 node 上執行。

以下稍微說明一下 GridGain 中 Failover 機制的運作方式:
  1. 在 remote grid node 上執行的 Grid Job 發生錯誤或例外

  2. 透過實作 GridTask.result(GridJobResult, List<GridJobResult>) 的方式,決定發生錯誤後的處理方式

  3. 若 GridJobResultPolicy 為 FAILOVER,則 GridGain 會將此 Grid Job 轉派送到其他 node 執行

  4. 確定 Grid Job 有正確的執行完畢,若還是執行失敗,則會繼續 failover 該 job


AspectJ AOP 設定

這個部分在之前都已經提過,因此不再贅述。


範例程式

瞭解 Failover 機制的運作方式後,以下用範例程式來進行說明:

GridifyHelloWorldFailoverExample.java
import org.gridgain.grid.*;
import org.gridgain.grid.gridify.*;

public class GridifyHelloWorldFailoverExample {
public static void main(String[] args) throws GridException {
GridFactory.start();

try {
sayIt("Hello this world!");
System.out.println("\n=========== local 執行完畢 ===========\n");
} finally {
GridFactory.stop(true);
}
} //end main

@Gridify(taskClass=GridifyHelloWorldFailoverTask.class, timeout=60000)
public static void sayIt(String arg) {
System.out.println("\n=========== remote 開始執行 ===========");
System.out.println(">>>> 印出 " + arg);
System.out.println("=========== remote 執行完畢 ===========\n");
} //end sayIt
}
GridifyHelloWorldFailoverTask.java
import org.gridgain.grid.*;
import org.gridgain.grid.gridify.*;
import org.gridgain.grid.resources.*;
import java.util.*;
import java.io.*;

//將 Grid Task 拆成 Grid Job 並送至 remote grid node
public class GridifyHelloWorldFailoverTask extends GridTaskSplitAdapter<GridifyArgument> {
private static final long serialVersionUID = 3675300294101581712L;

//注入(inject) GridTaskSessionResource
//不論是 task 或是分出去的 jobs 都可以看見此 session 與裡面的內容(attribute、checkpoint)
@GridTaskSessionResource
private GridTaskSession ses = null;

@Override
protected Collection<? extends GridJob> split(int gridSize, GridifyArgument arg) throws GridException {
//注入 fail = true 的 attribute 到 Task Session 中
ses.setAttribute("fail", true);

//取得要傳入的字串
String words = ((String)arg.getMethodParameters()[0]);


return Collections.singletonList(new GridJobAdapter<String>(words) {
private static final long serialVersionUID = 1456790456678955617L;

/**
* 執行工作:
* <ol>
* <li>預設 fail 為 false (此時工作可以正常執行)</li>
* <li>取得存在於 Task Session 中的 attribute</li>
* <li>如果 attribute fail 為 true(此時工作無法執行),將其設定為 false 並拋出 GridException 例外</li>
* <li>若拋出例外,Grid Job 停止執行並 failover 到其他 node 執行</li>
* <li>若未拋出例外,則印出傳入的字串</li>
* </ol>
*/

@Override
public Serializable execute() throws GridException {
//預設 fail 為 false (此時工作可以正常執行)
boolean fail = false;

try {
//取得存在於 Task Session 中的 attribute
fail = (Boolean)ses.waitForAttribute("fail");
} catch(InterruptedException e) {
throw new GridException("Got interrupted while waiting for attribute to be set.", e);
}

//如果 fail 為 true
if(fail) {
//將 fail attribute 設定為 false
ses.setAttribute("fail", false);

//拋出 GridException 例外,表示工作失敗
throw new GridException("Example job excepion");
}

//未拋出例外,印出傳入的字串
GridifyHelloWorldFailoverExample.sayIt(this.getArgument());

return null;
} //end execute
});
} //end split

/**
* 此處必須指定當 Grid Job 執行失敗時,所採取的動作<br />
* 設定為 GridJobResultPolicy.FAILOVER 表示執行失敗將會傳到其他 node 繼續執行
*/

@Override
public GridJobResultPolicy result(GridJobResult result, List<GridJobResult> received) throws GridException {
return result.getException() != null ? GridJobResultPolicy.FAILOVER : GridJobResultPolicy.WAIT;
} //end result

@Override
public Object reduce(List<GridJobResult> arg0) throws GridException {
return null;
} //end reduce
}
假設以上程式是運作在兩個 node 的環境上,結果會在 nodeA 產生錯誤訊息(就是程式中拋出的 GridException 例外),接著將 job 轉移到 nodeB 繼續執行,並正確的將字串印出。


而這個部分需要注意的是,程式實作了之前都沒實作過的 GridTask.result(GridJobResult, List<GridJobResult>) method;由於之前都假設 Grid Job 可以正確執行完畢,因此都沒有實作這個部分。

但在實際狀況中,分散式環境本來就較為嚴苛,考量也需要比較多,自然而然 Failover 也是不可缺少的一環!

但是只有 Failover 還是不夠,因為若是一個要執行五天的程式,執行了三天以後發生錯誤,只有 Failover 的話,雖然可以將 job 轉移到其他 node 繼續執行,但是卻得重新來過。

因此之後還要搭配 checkpoint 來達到從 job 中斷處繼續執行的目的,這個部分就留在之後繼續介紹了!

2007年12月18日 星期二

GridGain 程式開發(8) - Gridify With Session

Grid Task Session 運作方式與目的

此範例要介紹在 Grid Task 與 Grdi Job 之間,可以用來作為溝通媒介的「Grid Task Session」。

運作的機制如下:
  1. Grid Task Session 顧名思義,可以知道是屬於 Grid Task 而非 Grid Job

  2. 在 Grid Task Session 中可以加入 attribute 與 checkpoint 資訊,這些則是屬於 Grid Job 的資訊;而 Grid Job 加入 attribute 的用意通常作為與 Grid Task 溝通之用,而 checkpoint 則是記錄執行到何處,以便 job 執行失敗時進行 failover

  3. 每個 Grid Job 都可以檢視其他 job 所擁有的 attribute 與 checkpoint 相關資訊,因此可作為 Grid Job 之間溝通的橋樑

  4. 當然,Grid Task Session 也是可以被 Grid Task,以及所有該 task 拆出來的 Grid Job 所檢視


AspectJ AOP 設定

這個部分跟前面幾篇都是一樣的,因此就不贅述了!


範例程式

了解 Grid Task Session 的運作方式與目的之後,以下就使用程式進行說明:

GridifyHelloWorldSessionExample.java
import org.gridgain.grid.*;
import org.gridgain.grid.gridify.*;

public class GridifyHelloWorldSessionExample {

/**
* @param args
*/

public static void main(String[] args) throws GridException {
GridFactory.start();

try {
sayIt("Hello(1) this(2) world(3)");
System.out.println("\n=========== local 執行完畢 ===========\n");
} finally {
GridFactory.stop(true);
}
} //end main

/**
* 只有第一次呼叫此 method 才會被 GridGain 當作 GridTask 分成 GridJobs
* 之後僅會執行 function 內的動作(印出結果)
* */

@Gridify(taskClass=GridifyHelloWorldSessionTask.class, timeout=3000)
public static void sayIt(String arg) {
System.out.println("\n=========== remote 開始執行 ===========");
System.out.println(">>>> 印出 " + arg);
System.out.println("=========== remote 執行完畢 ===========\n");
} //end sayIt
}
GridifyHelloWorldSessionTask.java
import org.gridgain.grid.*;
import org.gridgain.grid.resources.*;
import org.gridgain.grid.gridify.*;
import java.util.*;
import java.io.*;

//處理 Grid Task 拆開成多個 Grid Jobs 的工作
public class GridifyHelloWorldSessionTask extends GridTaskSplitAdapter<GridifyArgument> {
//注入(inject) GridTaskSessionResource
//不論是 task 或是分出去的 jobs 都可以看見此 session 與裡面的內容(attribute、checkpoint)
@GridTaskSessionResource
private GridTaskSession ses = null;

@Override
protected Collection<? extends GridJob> split(int gridSize, GridifyArgument arg) throws GridException {
//取得傳入的字串,並以空白為間隔,拆為陣列
String[] words = ((String)arg.getMethodParameters()[0]).split(" ");

//產生 Grid Job collection
List<GridJobAdapter<String>> jobs = new ArrayList<GridJobAdapter<String>>(words.length);
for(String word : words) {
jobs.add(new GridJobAdapter<String>(word) {

//注入(inject) GridJobIdResource
//雖然乍看之下 jobID 被設定為 null,但經過注入 @GridJobResource 後
//就會被實際的 GridJobId 給取代(範例:92a5c837-5d64-413c-b254-1a79038f907d)
@GridJobIdResource
private UUID jobID = null;

/**
* 執行工作:
* <ol>
* <li>遠端執行所指定的工作(印出部分字串)</li>
* <li>將所印出的部分字串當作 attribute 加入 task session</li>
* <li>等待其他 Grid Job 也將其所取得的部分字串當作 attribute 加入 task session</li>
* <li>當所有 Grid Job 的 attribute 都設定完後,印出蒐集到的全部字串</li>
* </ol>
*/

@Override
public Serializable execute() throws GridException {
//印出一開始由 task 分配到的部分字串(GridGain 不會將其視為 Grid Task 處理)
GridifyHelloWorldSessionExample.sayIt(this.getArgument());

//將 jobID attribute 加入 GridTaskSession
//因此在 task 與所有 jobs 中都可以看見此 attribute 的內容
ses.setAttribute(jobID, this.getArgument());
try {
//等待所有屬於 task 中的 job 將 attribute 設定到 session 上
for(GridJobSibling sibling : ses.getJobSiblings()) {
//檢查 attribute 是否在 session 中 (Null 代表不存在)
if(ses.waitForAttribute(sibling.getJobId()) == null)
throw new GridException("Failed to get session attribute from job " + sibling.getJobId());
} //end loop
} catch(InterruptedException e) {
throw new GridException("Got interrupted while waiting for session attribute.", e);
}

StringBuilder msg = new StringBuilder();
for(Serializable arg : ses.getAttributes().values())
msg.append(arg).append(' ');

//印出 session 中所有字串的內容(GridGain 不會將其視為 Grid Task 處理)
GridifyHelloWorldSessionExample.sayIt(msg.toString());

return null;
}
});
} //end loop

return jobs;
} //end split

@Override
public Object reduce(List<GridJobResult> arg0) throws GridException {
return null;
} //end reduce
}


重要使用須知

上述的程式碼看起來沒什麼大問題,不過有一個相當重要的地方必須注意! 就是在 GridTaskSession.waitForAttribute(Serializable) 這個 method 上。

仔細注意此 method 所在的位置,位於「Grid Job Sibling 集合的 for loop 中」,加上參數所代入的是 GridJobSibling.getJobId();因此上面那段程式碼的意思為「確定每個 GridJobSibling 都將 JobID 這個 attribute 設定到 session 中」。

這樣說來似乎也覺得沒有什麼,不過重要的是:
若 waitForAttribute(Serializeable) method 中放的不是 GridJobId,而是其他的值呢? 此時該如何運作?
這樣有辦法進行嗎? 目前根據我的理解應該是不行,以下舉個範例:

假設 GridTaskA 拆成 GridJobA + GridJobB + GridJobC,假設在 remote gird node 在處理 GridJobA 的情況下,根據上面那一段 for loop 程式,會執行以下工作:
  1. 取得 GridJobSibling,分別是 GridJobB 與 GridJobC

  2. 指定 session 等待 GridJobB 將 JobID attribute 的加入,此時會等待其他 remote grid node 將 GridJobB 將其 JobID 設定進 session 中

  3. 確定 GridJobB 設定好 attribute

  4. 指定 session 等待 GridJobC 將 JobID attribute 的加入,此時會等待其他 remote grid node 將 GridJobC 將其 JobID 設定進 session 中

  5. Sibling 全部執行結束!

這樣看起來似乎還是沒什麼問題,但是重點來了! 在第二個與第四個流程,代入 GridTaskSession.waitForAttribute 的參數是??

沒錯! 就是 GridJobSibling.getJobId(),但這樣又有啥問題呢?

問題可大了! 這代表「使用者必須想辦法取得 Sibling 的 GridJobID」!!

因此問題慢慢釐清了,由於 GridJobSibling 提供了 getId() method,因此可以透過此 method 取得 GridJobID,所以沒有問題!

但是! 若是想要改用自行設定的值而非 JobID 呢?
恩,很抱歉! GridJobSibling 並沒有提供其他方法可以取得使用者所自訂的值!

因此,GridGain 在官方網站的文件上提到「可以透過 waitForAttribute 這種方式,讓多個 GridJob 在某種程度上達成有順序的執行」,其實還是有其限制在的;也就是說,若要判斷 session 中的 attribute 是由哪一個 GridJob 所設定的,那就必須使用 GridJobId 作為 attribute 的 key,因此在使用上還是需要多注意囉!

最後,看完上面的囉哩八嗦,應該可以了解 Grid Task Session 的使用方式以及注意事項了!

2007年12月17日 星期一

GridGain 程式開發(7) - Gridify With State

接著這個範例,將 grid-enabled method 從原本的程式中抽離出來,自成一個 class。

而這個 class 除了 grid-enabled method 以外,還包含了狀態(state)資訊,以及處理各狀態資訊所需要的 method。


AspectJ AOP 設定

然而,要使用這個功能,AOP 相關的設定也不能忘掉!

此處使用 AspectJ AOP,因此在 eclipse 上就必須要設定相關的啟動參數,因此在「Run Dialog」中設定:
  1. 在 Arguments -> VM arguments 中,設定以下內容:(GRIDGAIN_HOME 要自行取代掉!)
    -javaagent:[GRIDGAIN_HOME]/libs/aspectjweaver-1.5.3.jar

  2. 在 Classpath 中,透過 Advanced 按鈕加入以下 External Folder:
    [GRIDGAIN_HOME]/config/aop/aspectj


範例程式

為了簡單說明,state 資訊僅使用字串來表示,當然也可以是複雜的資料結構,端看使用者所需要的功能為何,以下就用範例程式來說明:

GridifyHelloWorld.java
import org.gridgain.grid.gridify.Gridify;

public class GridifyHelloWorld {
//設定 state 為 String
private String state = null;

// ========== 處理 state 相關 methods ========== (begin)
public String getState() {
return state;
} //end getState

public void setState(String state) {
this.state = state;
} //end setState
// ========== 處理 state 相關 methods ========== (end)

/**
* 透過「@Gridify」annotation 將此 method 變為 grid-enabled
* 並透過 taskClass 關鍵字指定 class 作為將 state 資訊拆開並傳送到 grid node 處理的 GridTask
*/

@Gridify(taskClass=GridifyHelloWorldStateTask.class, timeout=3000)
public void sayIt() {
System.out.println("\n=========== remote 開始執行 ===========");
System.out.println(">>>> 印出 " + this.state);
System.out.println("=========== remote 執行完畢 ===========\n");
} //end sayIt
}
GridifyHelloWorldStateTask.java
import java.io.*;
import java.util.*;
import org.gridgain.grid.*;
import org.gridgain.grid.gridify.*;

/**
* 繼承 GridTaskSplitAdapter,必須實作 split 與 reduce 兩個 method
*
* 【
GridifyArgument】
* 為 grid-enabled method 所屬的 class(此範例為 GridifyHelloWorld) 的 instance 的反射(reflection)
* 透過 reflection< 的機制,可以取回 class instance 相關資訊
* */

public class GridifyHelloWorldStateTask extends GridTaskSplitAdapter<GridifyArgument> {

/**
* 將 task 拆成多個 jobs 並丟到 grid node 去執行
*/

@Override
protected Collection<? extends GridJob> split(int gridSize, GridifyArgument arg) throws GridException {
//取得 grid-enabled method 所屬的 class(GridifyHelloWorld) instance
GridifyHelloWorld hello = (GridifyHelloWorld)arg.getTarget();

String[] words = hello.getState().split(" ");

//產生存放 GridJob 的 List 容器
List<GridJobAdapter<String>> jobs = new ArrayList<GridJobAdapter<String>>(words.length);

//指定 job 所要處理的工作,並加入 List 容器中
for(String word : words) {
jobs.add(new GridJobAdapter<String>(word) {

/**
* 執行 grid-enabled method<br />
* 使用的參數為上面傳入的參數 word (透過 getArgument() 取得)
*/

@Override
public Serializable execute() throws GridException {
GridifyHelloWorld hello = new GridifyHelloWorld();
hello.setState(this.getArgument());
hello.sayIt();

return null;
} //end execute

});
} //end loop

return jobs;
} //end split

/**
* reduce 的目的是將多個 job 所回傳的值匯集起來,並轉為單一物件輸出<br />
* 而這個物件會由 GridTaskFuture.get() 所取得<br />
* 此範例中不需回傳值,因此直接回傳 null
*/

@Override
public Object reduce(List<GridJobResult> arg0) throws GridException {
return null;
}
}
GridifyHelloWorldStateExample.java
import org.gridgain.grid.*;
import org.gridgain.grid.gridify.*;

public class GridifyHelloWorldStateExample {
public static void main(String[] args) throws GridException {
GridFactory.start();

try {
GridifyHelloWorld hello = new GridifyHelloWorld();

hello.setState("Hello this World!");

hello.sayIt();

//此行僅會在本地端執行
System.out.println("\n=========== local 執行完畢 ===========\n");
} finally {
GridFactory.stop(true);
}
}
}
由於這幾個範例程式也是使用了 AOP 機制,因此在 sayIt() method 的呼叫也是容易產生混淆! 以下稍微說明一下程式執行流程:
  1. 產生 GridifyHelloWorld instance,並設定此 instance 的 state 資訊

  2. 呼叫 grid-enabled sayIt() method,但不會印出任何 state 資訊,而是由 aspectJ 呼叫 GridGain,並根據 GridifyHelloWorldStateTask 中的 split 實作來將 task 拆成多個 jobs(以空白為間隔拆開 state 資訊)

  3. 接著 GridGain 會將包含 grid-enabled method 的 class 序列化(serialize)後,傳到 remote grid node 準備執行

  4. remote gird node 將其反序列化(deserialize)之後,根據此 class 產生新的 instance,並根據接收到的字串重新設定 state 資訊

  5. 當 state 資訊設定完成,最後再呼叫 sayIt() method,此時 GridGain 不會再度將其視為 task 而只是將 state 資訊輸出

最後,透過這種方式,使用者就可以將 business logic 以另外開發成 module 的方式去寫了! 不需要通通嵌在原本包含控制 grid 的相關程式之中,開發起來也較為簡潔易懂!!


更簡單的開發方式

同樣的,以下將上面程式修改為較容易理解的版本,修改後的執行結果也是相同的!

GridifyHelloWorld.java
import org.gridgain.grid.gridify.Gridify;

public class GridifyHelloWorld {
private String state = null;

public void setState(String state) {
this.state = state;
} //end setState

public String getState() {
return this.state;
}

// ***************** 修改的部分 ***************** (Begin)
@Gridify(taskClass=GridifyHelloWorldStateTask.class, timeout=3000)
public void splitGridTask() {
//這裡只要加入足夠的 annotation 資訊即可
//不需要任何實作,只是告知 GridGain 作 split task 的動作
} //end splitGridTask

public void sayIt() {
System.out.println("\n=========== remote 開始執行 ===========");
System.out.println(">>>> 印出 " + this.state);
System.out.println("=========== remote 執行完畢 ===========\n");
} //end sayIt
// ***************** 修改的部分 ***************** (End)
}
GridifyHelloWorldStateTask.java
僅為 GridTask 拆開成為 GridJob 的部分,因此不需更改!
GridifyHelloWorldStateExample.java
import org.gridgain.grid.*;

public class GridifyHelloWorldStateExample {

public static void main(String[] args) throws GridException {
GridFactory.start();

try {
GridifyHelloWorld hello = new GridifyHelloWorld();

hello.setState("Hello this world!");

// ***************** 修改的部分 ***************** (Begin)
//將 GridTask 拆成多個 GridJob,並執行工作
hello.splitGridTask();
// ***************** 修改的部分 ***************** (End)

System.out.println("\n=========== local 執行完畢 ===========\n");
} finally {
GridFactory.stop(true);
}
}
}

修改成以上這個版本之後,應該是會比較容易瞭解了!

GridGain 程式開發(6) - Gridify With Custom Task

此範例是使用 @Gridify annotation 搭配 AOP 的方式(此處使用 AspectJ),指定要處理的物件(此處為 Hello World 字串)、以及負責處理物件的 GridTask class。

透過此方式,使用者可以自行決定要針對物件進行哪些處理工作。

而與之前範例不同的是,此處在 @Gridify annotation 中就指定了 taskClass,因此不需要透過 GridFactory 的 getGrid() method 取得 grid instance(實作了 Grid Interface),再執行 execute(String taskName, Object arg) method 來執行 grid task。


AspectJ AOP 設定

此處使用 AspectJ AOP,因此在 eclipse 上就必須要設定相關的啟動參數,因此在「Run Dialog」中設定:
  1. 在 Arguments -> VM arguments 中,設定以下內容:(GRIDGAIN_HOME 要自行取代掉!)
    -javaagent:[GRIDGAIN_HOME]/libs/aspectjweaver-1.5.3.jar

  2. 在 Classpath 中,透過 Advanced 按鈕加入以下 External Folder:
    [GRIDGAIN_HOME]/config/aop/aspectj


範例程式

以下為範例程式碼:

GridifyHelloWorldTaskExample.java
import org.gridgain.grid.*;
import org.gridgain.grid.gridify.*;;

public class GridifyHelloWorldTaskExample {

/**
* 執行 Grid 程式
* @param 命令列參數 此範例中並沒有使用上
* @throws GridException 錯誤發生時,會拋出的例外
*/

public static void main(String[] args) throws GridException {
GridFactory.start();

try {
sayIt("Hello this world!");
} finally {
GridFactory.stop(true);
}
}

/**
* 透過「@Gridify」annotation 將此 method 變為 grid-enabled
* 並透過 taskClass 關鍵字指定 class 作為將字串拆開並傳送到 grid node 處理的 GridTask
* @param msg 作為要作為遠端處理的字串
*/

@Gridify(taskClass=GridifyHelloWorldTask.class, timeout=3000)
public static void sayIt(String msg) {
System.out.println("\n=========== remote 開始執行 ===========");
System.out.println(">>>> 印出 " + msg);
System.out.println("=========== remote 執行完畢 ===========\n");
} //end sayIt
}

GridifyHelloWorldTask.java
import java.io.*;
import java.util.*;

import org.gridgain.grid.*;
import org.gridgain.grid.gridify.*;

/**
* 將 task 拆成多個 job,必須 extends GridTaskSplitAdapter 這個 abstract class<br />
* 其中有兩個 method 必須實作,分別是 split 及 reduce
* 而此 GridTaskSplitAdapter 已經將 job 與 node 的配對資訊隱藏,讓程式開發更為簡易
*/
public class GridifyHelloWorldTask extends GridTaskSplitAdapter<GridifyArgument> {

/**
* split 是透過參數列中的兩個參數將 task 分開成多個 job<br />
* 並指定每個 job 的處理工作
* @param gridSize
目前 Grid Node 的數量
* @param arg 要被拆開的字串<br />
* (其型態是根據 GridTaskSplitAdapter 所用的型態,此處為 GridifyArgument)
* GridifyArgument 所指的是加上 @Gridify annotation 的 method instance
*/
@Override
protected Collection<? extends GridJob> split(int gridSize, GridifyArgument arg) throws GridException {
//將傳入的字串以空格做區隔存成 List 中的各元素
//透過「GridifyArgument.getMethodParameters()[0]」 取得第一個參數(其實也只有一個參數)
//GridifyArgument instance 中包含了足夠的資訊讓 GridGain 可以透過 reflection 機制在 grid node 上執行工作
String[] words = ((String)arg.getMethodParameters()[0]).split(" ");

//產生存放 GridJob 的 List 容器
List<GridJobAdapter<String>> jobs = new ArrayList<GridJobAdapter<String>>(words.length);

//指定 job 所要處理的工作,並加入 List 容器中
for(String word: words) {
jobs.add(new GridJobAdapter<String>(word) {

/**
* 執行 grid-enabled method<br />
* 使用的參數為上面傳入的參數 word (透過 getArgument() 取得)
*/

@Override
public Serializable execute() throws GridException {
//此處只是使用 class 中的 static method 作為輸出字串之用
//透過 AOP 機制,就不會認為此處的呼叫為另一個 GridTask(crosscutting),因此只會正常執行
GridifyHelloWorldTaskExample.sayIt(this.getArgument());
return null;
} //end execute

});
} //end loop

return jobs;
}

/**
* reduce 的目的是將多個 job 所回傳的值匯集起來,並轉為單一物件輸出<br />
* 而這個物件會由 GridTaskFuture.get() 所取得<br />
* 此範例中不需回傳值,因此直接回傳 null
*/

@Override
public Object reduce(List<GridJobResult> arg0) throws GridException {
return null;
}
}

由於以上的範例,乍看之下並不是很容易明白,因此以下說明一下程式執行的流程:
  1. 首先在 main() 中呼叫了 sayIt() method

  2. 由於此 method 加上了 @Gridify annotation,此時並不會執行 print 的工作,而是 aspectJ 會呼叫 GridGain,根據 GridifyHelloWorldTask 中的 split 的實作將 task 拆成多個 jobs (IoC 發生,因此不會將結果印出,取而代之的是將 task 拆開)

  3. 接著會將 sayIt() method 以及其所在的 class 序列化(serialize)後傳到 remote grid node 準備來執行

  4. 在 remote grid node 又再次呼叫了 sayIt() method,此時 GridGain 不會再次進行 split 的動作(不會再有 IoC 的動作發生),而會讓程式以一般的方式執行,把結果印出


這個範例由於加上了 AOP 的機制,因此看似沒有上一個這麼直覺;主要的困難點就在於在 GridifyHelloWorldTaskExample class 中呼叫已經 grid-enabled 的 sayIt() method,卻在 GridifyHelloWorldTask class 中又再次呼叫了 sayIt() method,不免讓人有點混淆!

【註】
其實本來就可以直接呼叫的,因為 sayIt 是 static method,只是第二次呼叫不會觸發 IoC 的動作!

雖然呼叫了兩次,不過在 GridTask 裡面的呼叫,不會有 crosscutting(亦即 IoC) 發生, 因此只是一般的執行而已,不會再度將呼叫視為 GridTask 進行處理。

至於為何第二次呼叫不會發生 crosscutting,這個部分就由 GridGain 來處理了,使用者就不需要傷這個腦筋囉!


更簡單的開發方式

說明完 GridGain 程式運作的方式後,大家應該可以想到一個更簡單明瞭的開發方式,讓程式碼看起來更為容易閱讀,以下將修改原本的程式:

GridifyHelloWorldTaskExample.java
import org.gridgain.grid.*;
import org.gridgain.grid.gridify.*;

public class GridifyHelloWorldTaskExample {

public static void main(String[] args) throws GridException {
GridFactory.start();

try {
//呼叫 method 將 task 拆成多個 jobs
splitGridTask("Hello this world!");

System.out.println("\n=========== local 執行完畢 ===========\n");
} finally {
GridFactory.stop(true);
}
}

@Gridify(taskClass = GridifyHelloWorldTask.class, timeout = 3000)
public static void splitGridTask(String arg) {
//這裡只要加入足夠的 annotation 資訊即可
//不需要任何實作,只是告知 GridGain 作 split task 的動作
} //end splitGridTask
}
GridifyHelloWorldTask.java
import java.io.*;
import java.util.*;
import org.gridgain.grid.*;
import org.gridgain.grid.gridify.*;

public class GridifyHelloWorldTask extends GridTaskSplitAdapter<GridifyArgument> {

@Override
protected Collection<? extends GridJob> split(int gridSize, GridifyArgument arg) throws GridException {
String[] words = ((String)arg.getMethodParameters()[0]).split(" ");

List<GridJobAdapter<String>> jobs = new ArrayList<GridJobAdapter<String>>(words.length);

for(String word : words) {
jobs.add(new GridJobAdapter<String>(word) {

//也可以將原本 sayIt() 中的內容搬到這邊
@Override
public Serializable execute() throws GridException {
System.out.println("\n=========== remote 開始執行 ===========");
System.out.println(">>>> 印出 " + this.getArgument());
System.out.println("=========== remote 執行完畢 ===========\n");

return null;
}

});
} //end loop

return jobs;
}

@Override
public Object reduce(List<GridJobResult> arg0) throws GridException {
return null;
}
}

修改成上面這個版本之後,應該是會比原本的更容易了解!