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 中斷處繼續執行的目的,這個部分就留在之後繼續介紹了!

沒有留言:

張貼留言