comparison paper/chapter4.tex @ 69:4f31182c8244

fixed
author Nobuyasu Oshiro <dimolto@cr.ie.u-ryukyu.ac.jp>
date Sat, 01 Feb 2014 22:41:24 +0900
parents d770a2b534b3
children 4e8bfd65768f
comparison
equal deleted inserted replaced
68:01fadc801c18 69:4f31182c8244
15 15
16 \section{Alice のトポロジーマネージャーの利用} 16 \section{Alice のトポロジーマネージャーの利用}
17 17
18 \subsection{トポロジーマネージャーの起動} 18 \subsection{トポロジーマネージャーの起動}
19 Alice を用いてサーバノードでトポロジーの形成を行う方法を述べる. 19 Alice を用いてサーバノードでトポロジーの形成を行う方法を述べる.
20 Alice のトポロジーマネージャーの起動は\ref{src:alice_dot}の様に行う. 20 Alice のトポロジーマネージャーの起動は\ref{src:alice_ntm_run}の様に行う.
21 (\ref{src:alice_ntm_run}).
22 \begin{lstlisting}[frame=lrbt,label=src:alice_ntm_run,caption=Alice によるネットワークトポロジーマネージャーの起動,numbers=left] 21 \begin{lstlisting}[frame=lrbt,label=src:alice_ntm_run,caption=Alice によるネットワークトポロジーマネージャーの起動,numbers=left]
23 % java -cp Alice.jar alice.topology.manager.TopologyManager -p 10000 -conf ./topology/tree5.dot 22 % java -cp Alice.jar alice.topology.manager.TopologyManager -p 10000 -conf ./topology/tree5.dot
24 \end{lstlisting} 23 \end{lstlisting}
25 -p オプションはトポロジーマネージャーが開くポートの番号, -conf オプションには dot ファイルのパスを渡す. 24 -p オプションはトポロジーマネージャーが開くポートの番号, -conf オプションには dot ファイルのパスを渡す.
26 25
43 \end{lstlisting} 42 \end{lstlisting}
44 43
45 node0 や node1 はサーバノードの名前を示す. 44 node0 や node1 はサーバノードの名前を示す.
46 サーバノードの間にはラベルがあり, Alice 上ではこのラベル 45 サーバノードの間にはラベルがあり, Alice 上ではこのラベル
47 に指定される文字列(キー)を使うことで他のサーバノードのデータへアクセスすることができる. 46 に指定される文字列(キー)を使うことで他のサーバノードのデータへアクセスすることができる.
48 node0 -> node1 はサーバノード同士の繋がりを示している. 47 node0 \verb|->| node1 はサーバノード同士の繋がりを示している.
49 次に続く label="child1" は, node0 が node1 のデータに"child1"という文字列を使うことでアクセス 48 次に続く label="child1" は, node0 が node1 のデータに"child1"という文字列を使うことでアクセス
50 できることを示す. 49 できることを示す.
51 50
52 dot ファイルを読み込んだ Alice のトポロジーマネージャーに対して, サーバノードは 51 dot ファイルを読み込んだ Alice のトポロジーマネージャーに対して, サーバノードは
53 誰に接続を行えばよいかを訪ねる. 52 誰に接続を行えばよいかを訪ねる.
104 \subsection{Alice によるプログラミング} 103 \subsection{Alice によるプログラミング}
105 AliceはDataSegment(データ)とCodeSegment(タスク)単位でプログラミングを行うことを述べた. 104 AliceはDataSegment(データ)とCodeSegment(タスク)単位でプログラミングを行うことを述べた.
106 CodeSegmentには計算に必要なDataSegmentが登録される. 105 CodeSegmentには計算に必要なDataSegmentが登録される.
107 そしてDataSegmentが準備され次第CodeSegmentによる計算が実行される. 106 そしてDataSegmentが準備され次第CodeSegmentによる計算が実行される.
108 DataSegmentの取得は文字列のキーを使うことで行える. 107 DataSegmentの取得は文字列のキーを使うことで行える.
109 以下のコードにCodeSegmentの例を示す. 108 コード\ref{src:cs_sample}にCodeSegmentの例を示す.
110 \begin{lstlisting}[frame=lrbt,label=src:syslog_nfconntrack,caption=CodeSegmentの実行,numbers=left] 109 \newpage
110 \begin{lstlisting}[frame=lrbt,label=src:cs_sample,caption=CodeSegmentの実行,numbers=left]
111 public class TestCodeSegment extends CodeSegment { 111 public class TestCodeSegment extends CodeSegment {
112 public Receiver arg1 = ids.create(CommandType.TAKE); 112 public Receiver arg1 = ids.create(CommandType.TAKE);
113 113
114 public TestCodeSegment() { } 114 public TestCodeSegment() { }
115 115
128 cs.arg1.setKey("local", "count"); // setKey API 128 cs.arg1.setKey("local", "count"); // setKey API
129 cs.ods.put("local", "count", 0); 129 cs.ods.put("local", "count", 0);
130 } 130 }
131 } 131 }
132 \end{lstlisting} 132 \end{lstlisting}
133 これは, 数字を1から10まで出力を行い終了するプログラムである.
134 コードの説明を行う. 133 コードの説明を行う.
134 このプログラムは, 数字を1から10まで出力を行い終了するプログラムである.
135 17行目から19行目の処理が最初に行われる. 135 17行目から19行目の処理が最初に行われる.
136 まずTestCodeSegmentというCodeSegmentのインスタンスcsを生成する. 136 まずTestCodeSegmentというCodeSegmentのインスタンスcsを生成する.
137 csはarg1というReceiverクラスのフィールドを保持しており, Receiverクラスは 137 csはarg1というReceiverクラスのフィールドを保持しており, Receiverクラスは
138 DataSegmentを受けとるためのクラスである. 138 DataSegmentを受けとるためのクラスである.
139 arg1に対しsetKey APIを使うことで, 使用したいDataSegmentのキー"count"を登録することができる. 139 arg1に対しsetKey APIを使うことで, 使用したいDataSegmentのキー"count"を登録することができる.
251 251
252 \subsection{TreeOperationLogのシリアライズ} 252 \subsection{TreeOperationLogのシリアライズ}
253 TreeOperationLogをシリアライズ可能な形にするにあたって気をつけなければならないのが, フィールドを 253 TreeOperationLogをシリアライズ可能な形にするにあたって気をつけなければならないのが, フィールドを
254 シリアライズ可能にする部分である. 254 シリアライズ可能にする部分である.
255 TreeOperationLogはTreeOperationをいくつも保持し, TreeOperationはNodePathとNodeOperationを保持するものであった. 255 TreeOperationLogはTreeOperationをいくつも保持し, TreeOperationはNodePathとNodeOperationを保持するものであった.
256 そのため, これら全てシリアライズ可能な形にしなければならない. 256 そのため, これら全てをシリアライズ可能な形にしなければならない.
257 257
258 基本的にこれらの実装は, フィールドを全てプリミティブなものだけにすればよい. 258 基本的にこれらの実装は, フィールドを全てプリミティブなものだけにすればよい.
259 MessagePackはListを扱うこともできるため, TreeOperationLogで継承されていたIterableの挙動もListを使うことで 259 MessagePackはListを扱うこともできるため, TreeOperationLogで継承されていたIterableの挙動もListを使うことで
260 実装を行うことができた. 260 実装を行うことができた.
261 261
262 \subsection{ログに対する情報の追加} 262 \subsection{ログに対する情報の追加}
263 TreeOperationLogをシリアライズ可能な形にした後, 問題が発生した. 263 TreeOperationLogをシリアライズ可能な形にした後, 問題が発生した.
264 それは, TreeOperationLog事態は木の名前を保持していないというものである. 264 それは, TreeOperationLog自体は木の名前を保持していないというものである.
265 そのため, TreeOperationLogだけを受け取っても, そのログがどの木に対して行われるのか 265 そのため, TreeOperationLogだけを受け取っても, そのログがどの木に対して行われるのか
266 わからなかった. 266 わからなかった.
267 そこで, TreeOperationLogの情報だけでなく, 木の名前とUUID, それとtimestampの情報も付与 267 そこで, TreeOperationLogの情報だけでなく, 木の名前とUUID, それとtimestampの情報も付与
268 してシリアライズが可能なNetworkTreeOperationLogの実装を行った. 268 してシリアライズが可能なNetworkTreeOperationLogの実装を行った.
269 269
294 294
295 しかし, この時気をつけなければならないことがある. 295 しかし, この時気をつけなければならないことがある.
296 それは, \verb|ods.put|の処理をレスポンスを返すスレッドの中で行うと, レスポンスが悪くなる可能性が 296 それは, \verb|ods.put|の処理をレスポンスを返すスレッドの中で行うと, レスポンスが悪くなる可能性が
297 あることだ. 297 あることだ.
298 そのため, \verb|ods.put|を行うのは別のThreadにしたほうがよい. 298 そのため, \verb|ods.put|を行うのは別のThreadにしたほうがよい.
299 以下のコードはcommitに成功した後に, NetworkTreeOperationへと変換したログを別スレッド渡し 299 以下のコードはcommitに成功した後に, NetworkTreeOperationへと変換したログを別スレッドに渡し
300 て処理させるコードである. 300 て処理させるコードである.
301 \begin{lstlisting}[frame=lrbt,label=src:logconvert_and_execute,caption=NetworkTreeOperationをputするために別スレッドを立ち上げる,numbers=left] 301 \begin{lstlisting}[frame=lrbt,label=src:logconvert_and_execute,caption=NetworkTreeOperationをputするために別スレッドを立ち上げる,numbers=left]
302 NetworkTreeOperationLog netLog = new NetworkTreeOperationLog(_uuid, _treeName,newLog); 302 NetworkTreeOperationLog netLog = new NetworkTreeOperationLog(_uuid, _treeName,newLog);
303 CodeSegment cs = new LogPutCodeSegment(netLog); 303 CodeSegment cs = new LogPutCodeSegment(netLog);
304 cs.execute(); 304 cs.execute();
305 \end{lstlisting} 305 \end{lstlisting}
306 306
307 LogPutCodeSegmentの実装は次のようになっている. 307 LogPutCodeSegmentの実装は次のようになっている.
308 \begin{lstlisting}[frame=lrbt,label=src:,caption=putを行うためだけのCodeSegmentの用意,numbers=left] 308 \begin{lstlisting}[frame=lrbt,label=src:,caption=putを行うためだけのCodeSegmentの用意,numbers=left]
309 // LogPutCodeSegment Class 309 public class LogPutCodeSegment extends CodeSegment{
310 NetworkTreeOperationLog log; 310 NetworkTreeOperationLog log;
311 public LogPutCodeSegment(NetworkTreeOperationLog _log) { 311 public LogPutCodeSegment(NetworkTreeOperationLog _log) {
312 log = _log; 312 log = _log;
313 } 313 }
314 314 @Override
315 @Override 315 public void run() {
316 public void run() { 316 ods.put("log", log);
317 ods.put("log", log); 317 }
318 } 318 }
319 \end{lstlisting} 319 \end{lstlisting}
320 上で述べた問題は, ベンチマークテストなど, 大量の負荷をかけたさいに発生する. 320 上で述べた問題は, ベンチマークテストなど, 大量の負荷をかけたさいに発生する.
321 負荷とはJungleのデータに変更を加わることである. 321 負荷とはJungleのデータに変更が加わることである.
322 データの変更により大量のログが生成される. 322 多数のデータの変更により大量のログが生成される.
323 そのため, \verb|ods.put|によりDataSegmentの"log"にアクセスが集中してしまい, レスポンスが 323 そのため, \verb|ods.put|によりDataSegmentの"log"にアクセスが集中してしまい, レスポンスが
324 悪くなっていた. 324 悪くなっていた.
325 \verb|ods.put|を行うタイミングには気をつけなければず, 上記のコードにしても改良の余地はある. 325 \verb|ods.put|を行うタイミングには気をつけなければず, 上記のコードにしても改良の余地はある.
326 326
327 \subsection{他サーバノードへのログの送信} 327 \subsection{他サーバノードへのログの送信}
331 他サーバノードに対してデータを送るときに必要なことは, そのサーバノードのDataSegmentにアクセスする 331 他サーバノードに対してデータを送るときに必要なことは, そのサーバノードのDataSegmentにアクセスする
332 キーである. 332 キーである.
333 Aliceでは接続を行っている相手のDataSegmentのキーのリストは"\_CLIST"キーを使うことで取得することができる. 333 Aliceでは接続を行っている相手のDataSegmentのキーのリストは"\_CLIST"キーを使うことで取得することができる.
334 "\_CLIST"キーで得られるリストを使って他サーバノードへとデータをputするコードを次に示す. 334 "\_CLIST"キーで得られるリストを使って他サーバノードへとデータをputするコードを次に示す.
335 335
336 \newpage
336 \begin{lstlisting}[frame=lrbt,label=src:log_put,caption=他サーバノードへのログの送信部分,numbers=left] 337 \begin{lstlisting}[frame=lrbt,label=src:log_put,caption=他サーバノードへのログの送信部分,numbers=left]
337 public class LogUpdateCodeSegment extends CodeSegment { 338 public class LogUpdateCodeSegment extends CodeSegment {
338 Receiver log = ids.create(CommandType.TAKE); 339 Receiver log = ids.create(CommandType.TAKE);
339 Receiver clist = ids.create(CommandType.PEEK); 340 Receiver clist = ids.create(CommandType.PEEK);
340 341
343 clist.setKey("_CLIST");; 344 clist.setKey("_CLIST");;
344 } 345 }
345 346
346 List<String> list = clist.asClass(List.class); 347 List<String> list = clist.asClass(List.class);
347 for (String node : list) { 348 for (String node : list) {
348 ods.put(node, log.key, log.getVal()); 349 ods.put(node, log.key, log.getVal()); // Send datasegment to other node
349 } 350 }
350 : 351 :
351 \end{lstlisting} 352 \end{lstlisting}
352 変数logはNetworTreeOperationLogが入っている. 353 12行目の\verb|ods.put(node, log.key, log.getVal())|が他サーバノードへデータを送る部分になる.
354 変数logにはNetworTreeOperationLogが入っている.
353 変数listには"\_CLIST"により得られたデータが入っている. 355 変数listには"\_CLIST"により得られたデータが入っている.
354 それは文字列のListとなっている. 356 それは文字列のListとなっている.
355 listの中身を1つずつ受け取り, \verb|ods.put|する際に引数として渡してやることで, 他サーバノードの 357 listの中身を1つずつ受け取り, \verb|ods.put|する際に引数として渡してやることで, 他サーバノードの
356 DataSegmentにアクセスが行える. 358 DataSegmentにアクセスが行える.
357 359
358 listの具体的な内容は, 図\ref{fig:tree_topology}で組まれたトポロジーでいうところの, "parent", "child1", "child2" 360 listの具体的な内容は, 図\ref{fig:tree_topology}で組まれたトポロジーでいうところの, "parent", "child1", "child2"
359 の文字列となっている. 361 の文字列のListとなっている.
360 もし子どもとなるサーバノードがないときは"parent"だけがListには入れられる. 362 もし子どもとなるサーバノードがないときは"parent"だけがListには入れられる.
361 363
362 364
363 \subsection{ログの受信とデータ反映} 365 \subsection{ログの受信とデータ反映}
364 次は受け取ったログからデータの編集を行う部分の実装を行う. 366 次は受け取ったログからデータの編集を行う部分の実装を行う.
365 Jungleはのデータを変更する手段として木構造データ毎にTreeEditorクラスが提供される. 367 Jungleはのデータを変更する手段として木構造データ毎にTreeEditorクラスが提供される.
366 このTreeEditorを使用し, ログに入っているTreeOperationを1つ1つ取り出し同じ編集を行わせる. 368 このTreeEditorを使用し, ログに入っているTreeOperationを1つ1つ取り出し同じ編集を行わせる.
367 例えば次のようになる. 369 例えば次のようになる.
368 \begin{lstlisting}[frame=lrbt,label=src:data_edit,caption=ログを受け取ってのデータの反映,numbers=left] 370 \begin{lstlisting}[frame=lrbt,label=src:data_edit,caption=ログを受け取ってのデータの反映,numbers=left]
369 // Receiver log <- "log"キーから取得できるデータが張っている 371 // Receiver log <- "log"キーから取得できるデータが入っている
370 // Jungle jugnle 372 // Jungle jugnle
371 NetworkTreeOperationLog netLog = log.asClass(NetworkTreeOperationLog.class); // NetworkTreeOperationLogへのコンバート 373 NetworkTreeOperationLog netLog = log.asClass(NetworkTreeOperationLog.class);
372 String treeName = netLog.getTreeName(); 374 String treeName = netLog.getTreeName();
373 JungleTree tree = jungle.getTreeByName(treeName); 375 JungleTree tree = jungle.getTreeByName(treeName);
374 TreeEditor editor = tree.getLocalTreeEditor(); // Editor の取得 376 TreeEditor editor = tree.getLocalTreeEditor(); // Editor の取得
375 for (TreeOperation op : netlog) { 377 for (TreeOperation op : netlog) {
376 NodePath path = op.getNodePath(); 378 NodePath path = op.getNodePath();
383 } 385 }
384 \end{lstlisting} 386 \end{lstlisting}
385 7行目で取り出されたTreeOperationからさらにNodePathとNodeOperationを取り出しているのが8行目と9行目になる. 387 7行目で取り出されたTreeOperationからさらにNodePathとNodeOperationを取り出しているのが8行目と9行目になる.
386 最後にedit関数にTreeEditorとNodePath, それとNodeOpeartionを引き渡している. 388 最後にedit関数にTreeEditorとNodePath, それとNodeOpeartionを引き渡している.
387 edit関数は次のようになる. 389 edit関数は次のようになる.
388 \begin{lstlisting}[frame=lrbt,label=src:data_edito2,caption=edit関数の実装,numbers=left] 390 \begin{lstlisting}[frame=lrbt,label=src:data_edit2,caption=edit関数の実装,numbers=left]
389 Either<Error, JungleTreeEditor> edit(JungleTreeEditor editor, NodePath path, 391 Either<Error, JungleTreeEditor> edit(JungleTreeEditor editor, NodePath path,
390 NodeOperation nodeOp, int pos) { 392 NodeOperation nodeOp, int pos) {
391 String key = "; 393 String key = "";
392 Command c = nodeOp.getCommand(); 394 Command c = nodeOp.getCommand();
393 switch (c) { 395 switch (c) {
394 case PUT_ATTRIBUTE: 396 case PUT_ATTRIBUTE:
395 key = nodeOp.getKey(); 397 key = nodeOp.getKey();
396 ByteBuffer value = nodeOp.getValue(); 398 ByteBuffer value = nodeOp.getValue();
409 対応するTreeEditorのAPIを書くことで対応できる. 411 対応するTreeEditorのAPIを書くことで対応できる.
410 412
411 %\subsection{ローカルのデータ編集専用のTreeEditorの用意} 413 %\subsection{ローカルのデータ編集専用のTreeEditorの用意}
412 %データ編集が行われ, 414 %データ編集が行われ,
413 415
416 \newpage
414 \section{永続性の実装} 417 \section{永続性の実装}
415 次は, ログの書き出しによる永続性の実装について述べる. 418 次は, ログの書き出しによる永続性の実装について述べる.
416 第3章でJungleはWriterいう永続性実装のための機能が元々用意されていることを述べた. 419 第3章でJungleはWriterいう永続性実装のための機能が元々用意されていることを述べた.
417 永続性で考えなければならないことは, どのようなデータをどんなデータ表現で書き込むかということである. 420 永続性で考えなければならないことは, どのようなデータをどんなデータ表現で書き込むかということである.
418 今回の実装ではログであるTreeOperationLogを書き出す. 421 今回の実装ではログであるTreeOperationLogを書き出す.
419 また, TreeOperationLogの情報を保持しつつ, MessagePackでシリアライズできるクラスとして 422 また, TreeOperationLogの情報を保持しつつ, MessagePackでシリアライズできるクラスとして
420 NetworkTreeOperationLogの実装を行った. 423 NetworkTreeOperationLogの実装を行った.
421 424
422 つまり, WriterでNetworkTreeOperationLogを書き出すようにすればよい. 425 つまり, WriterでNetworkTreeOperationLogを書き出すようにすればよい.
423 以下にログをディスクへ書き出すためのクラスPersistentChangeListWriterの実装を示す. 426 以下にログをディスクへ書き出すためのクラスPersistentChangeListWriterの実装を示す.
424 \begin{lstlisting}[frame=lrbt,label=src:,caption=NetworkTreeOperationをディスクへ書き出す,numbers=left] 427 \begin{lstlisting}[frame=lrbt,label=src:writer,caption=NetworkTreeOperationをディスクへ書き出す,numbers=left]
425 public class PersistentChangeListWriter implements ChangeListWriter { 428 public class PersistentChangeListWriter implements ChangeListWriter {
426 429
427 MessagePack msgpack; 430 MessagePack msgpack;
428 OutputStream out; 431 OutputStream out;
429 432
455 これにより木の編集が行われるたびにNetworkTreeOperationLogが書き込まれていく. 458 これにより木の編集が行われるたびにNetworkTreeOperationLogが書き込まれていく.
456 読み込みたいときはMessagePackを使ってディスクから読み込み, データ分散実装と同じの方法で木の編集を行っていく 459 読み込みたいときはMessagePackを使ってディスクから読み込み, データ分散実装と同じの方法で木の編集を行っていく
457 ことができる(Listing\ref{src:data_edit}, \ref{src:data_edit2}). 460 ことができる(Listing\ref{src:data_edit}, \ref{src:data_edit2}).
458 461
459 462
460 \section{掲示板プログラムにおけるマージの実装} 463 \newpage
464 \section{Mergeの実装}
461 Jungle に分散実装を行った後の問題としてデータ衝突がある. 465 Jungle に分散実装を行った後の問題としてデータ衝突がある.
462 他のサーバノードから送られてくるデータが既に手元で変更を加えた木構造を対象とした 466 他のサーバノードから送られてくるデータが既に手元で変更を加えた木構造を対象とした
463 場合に発生する問題である. 467 場合に発生する問題である.
464 Jungle ではこれをアプリケーション毎にマージを実装することで解決させる. 468 Jungle ではこれをアプリケーション毎にMergeを実装することで解決させる.
469
470 \section{掲示板プログラムにおけるデータ衝突}
465 471
466 今回分散実装を行い, 例題として掲示板プログラムを用意した. 472 今回分散実装を行い, 例題として掲示板プログラムを用意した.
467 掲示板プログラムに実装を行ったマージについて述べる. 473 掲示板プログラムに実装を行ったMergeについて述べる.
468 まず Jungle を用いた掲示板プログラムのデータ保持方法を図\ref{fig:merge2}に示す. 474 まず Jungle を用いた掲示板プログラムのデータ保持方法を図\ref{fig:merge2}に示す.
469 \begin{figure}[htpb] 475 \begin{figure}[htpb]
470 \begin{center} 476 \begin{center}
471 \includegraphics[scale=0.70]{figures/merge2.pdf} 477 \includegraphics[scale=0.70]{figures/merge2.pdf}
472 \caption{Jungle による掲示板プログラムのデータ保持方法} 478 \caption{Jungle による掲示板プログラムのデータ保持方法}
497 \end{center} 503 \end{center}
498 \end{figure} 504 \end{figure}
499 505
500 \newpage 506 \newpage
501 507
508 \subsection{掲示板プログラムにおけるMerge}
509
502 図\ref{fig:merge_imp2}の server node0 の木の状態にするのが理想である. 510 図\ref{fig:merge_imp2}の server node0 の木の状態にするのが理想である.
503 掲示板のへの書き込みの表示は, 書き込みされた時間が早い順に表示されるようにしたい. 511 掲示板のへの書き込みの表示は, 書き込みされた時間が早い順に表示されるようにしたい.
504 これを timestamp を利用することで行う. 512 これを timestamp を利用することで行う.
505 他サーバノードから来たデータに関しては, timestamp を参照し, 次に自分の保持している 513 他サーバノードから来たデータに関しては, timestamp を参照し, 次に自分の保持している
506 木の子ノードの timestamp と比べていくことでデータの追加する場所を決める. 514 木の子ノードの timestamp と比べていくことでデータの追加する場所を決める.
507 これが今回実装を行った掲示板システムにおけるマージになる. 515 これが今回実装を行った掲示板システムにおけるMergeになる.
508 516
509 %単一サーバで動いている時の Jungle はただ子ノードとして後ろに追加するだけだが, 分散 517 %単一サーバで動いている時の Jungle はただ子ノードとして後ろに追加するだけだが, 分散
510 %環境下においては timestamp に従い子ノードを追加する位置を決めるようにする. 518 %環境下においては timestamp に従い子ノードを追加する位置を決めるようにする.
511 519
512 520