分散フレームワークChristieを用いたリモートエディタの実装
|
Takahiro Ikki, Shinji Kono
琉球大学
|
研究目的, 背景
- ペアプログラミングなどでは同時に複数人が一つのファイルを編集することができるリモートエディタが有効である。
- 既存のリモートエディタアプリケーションとしてVisual Stdio Codeのlive share機能があげられる。
- 編集に参加するユーザーがそれぞれ好きなエディタが使えるアプリケーションを作成する。
- 本研究室で開発している分散フレームワークChristieはGearという概念の性質上リモートエディタと相性が良い。
リモートエディタの概要説明
- 本研究で作成するリモートエディタはChristieの機能を用いて通信環境を構成する。
- 同期編集セッションに接続したユーザーは自身のマシン上でエディタを使って編集対象ファイルを開く。
- ユーザーが起こしたファイルへの変更を、命令コマンドとして接続しているサーバー(ハブ)へ送信&実行させる。
- 命令コマンドはサーバーへ集められ、サーバーは受け取ったコマンドを他の接続ノードへ送信し、実行させる。
テスト用テキストエディタ
- 通信の構成を行うChristieはjava言語で作成されているため、javaのswingを用いてテキストエディタを制作した。
- エディタ部分の入力、削除の取得はDocument Listenerクラスを使った。
- insertUpdate、removeUpdateメソッドがそれぞれ挿入、削除を検知した時に動作する。
- このエディタはファイルの内容をオフセット番号で取り扱っている。
DocumentListenerの記述部分
public class MyDocumentListener implements DocumentListener {
@Override
public void insertUpdate(DocumentEvent e) {
Document doc = e.getDocument();
loc = e.getOffset();
System.out.println(loc);
}
@Override
public void removeUpdate(DocumentEvent e) {
Document doc = e.getDocument();
sendLoc = e.getOffset();
System.out.println("delete " + sendLoc);
}
@Override
public void changedUpdate(DocumentEvent e) {
}
}
コマンドパターンの解説
- リモートエディタの通信では、各ノード(参加ユーザのエディタ)がそれぞれ自身のファイルの変更内容を他のノードに送信する。
- コマンドパターンとは命令を一つのオブジェクトとして表現するプログラム手法である。
- 命令を表すクラスを作成し、インスタンスを作成と同時に命令の中身を入力することで命令を作成する。
- リモートエディタにおいては「オフセットn番目に に 文字列 “A”を入力した」という変更を命令にして送信する。
- コマンドパターンの利点として、
- ChristieのGearの概念と相性がいい。
- 命令に必要な内容をまとめて送信するため、時間差による相違の発生が防げる。
- オブジェクトとして取り扱えるため管理が行いやすい。
package christie.example.RemoteTake;
import org.msgpack.annotation.Message;
@Message
class RTCommand {
public String line;
public String cmd;
public int offset;
public RTCommand () {}
public RTCommand(String cmd, String line, int i) {
this.cmd = cmd;
this.line = line;
this.offset = i;
}
@Override
public String toString() {
return "RTCommand{" +
"line='" + line + '\'' +
", cmd='" + cmd + '\'' +
", offset=" + offset +
'}';
}
}
コマンドパターン実装の際に起こった問題
- クラスを他ノードに送信するためには、クラスをシリアライズして送信する必要がある。
- コマンドの送信にはmsgpackクラスとjavassistを利用している。
- しかし、送信がうまく行えなかったため、原因を調査した。
- javaのバージョン進行のため、msgpackバージョン0.6.12が非対称となっていた。
- msgpackの最新バージョン0.8.20はシリアライズ機能が含まれなくなった。
- 以上の原因に対処するため
- javassistのバージョンを最新版へ変更した。
- シリアライズする命令クラスに対し、クラスをpublicに変更した。
- 以上の対処によりコマンドパターンでの命令実装を行うことができた。
- 自身でシリアライズ機能をChristieに内蔵してしまえば、これらのパッケージは不要になる。
編集位置の相違
- 同期編集のセッションでは命令コマンドの送信のすれ違いにより、ノードごとのファイル状態が異なってしまうことがある。
- EditorAとEditorBはそれぞれの命令を自身のエディタバッファに施してから命令を送信するため、お互いバッファ状態が異なる状態で受け取った命令を実行してしまう。
編集の相違の解消
- 同期編集のセッションはスター型の通信接続で行うため、サーバー対複数ノードの通信間で相違が発生する。
- 編集の相違を防ぐためには二つの処理を行う必要がある。
- サーバーとノード間の命令のすれ違いが発生したことを検知する。
- すれ違いが発生した際に、オフセットのズレを修正する。
- サーバーが正しいファイルの状態を保持するためサーバーの状態にノードが合わせる必要がある。
命令コマンドに番号をつけ相違を解消する
- 命令コマンド番号には以下の特性がある。
- 全てのノード(サーバーを含める)は事前に処理した命令コマンドの番号を記憶している。
- 新しく作られた命令コマンドは作られたノードの命令実行済み番号+1 を自身の命令コマンド番号とする。
- ノードは自身が作成したか、他のノードに送られてきたかに関わらず命令コマンドを実行したら命令実行済み番号をその命令コマンドの番号と同じにする。
- もしノードが送られてきた命令コマンドを見て、そのコマンドが自身の実行済み番号と同値以下の場合、送信元のノードとの命令のすれ違いが発生していることが分かる。
すれ違いが発生した際の処理
- 全てのノードは自分が実行した命令コマンドを記録しており、後からオフセットや文字列を取り出すことができる。
- すれ違いが発生した時点からの命令の中身を集計、参照しすれ違いで送られてきたコマンドを修正する。
- insertを例とすると
- 受け取ったコマンドのオフセット > 受信コマンドとすれ違ったコマンドのオフセット のとき、受診したコマンドのオフセットに+1 する。
- 受け取ったコマンドのオフセット <= 受信コマンドとすれ違ったコマンドのオフセット のとき、受診したコマンドのオフセットを変えずにそのまま実行できる。
スター型通信
- Christieにはノードの通信接続を行うTopologyManagerという機能がある。
- 同期通信はスター型での接続を行う。
- スター型通信の利点は
- サーバーが正しいファイル状態を保持するため、整合性を保つことができる。
- どこかのノードが切断されても、要害の範囲をそのノードのみに抑えることができる。
- 新しいノードが参加した、もしくは復帰の際にはサーバーのファイル状況を参照するのみで参加、復帰ができる。
Christie
- Christieは当研究室で開発している、信頼性を重視した分散フレームワークである.
- 現在はjava上で開発されているが、別言語(CbC)で構成されたGearsOSに組み込む予定があるため,それに向けて書き換え可能な構成となっている。
- ChristieではデータをGearという単位で分割して記述を行う。
- CodeGear(以下CG)
- スレッドやクラスに相当し、javaの継承を用いて記述する。keyに全てのDGが格納された際に動作する。
- DataGear(以下DG)
- DGは変数に相当し、CG内でアノテーションを用いてデータを取り出せる。
- CodeGearManager(以下CGM)
- ノードに相当し, DG, CG, DataGearManagerの管理をする.
- DataGearManager(以下DGM)
- DGを管理するものであり, putという操作にて変数(DG)をkeyに格納する。
Christieのコード例
package christie.example.HelloWorld;
import christie.codegear.CodeGearManager;
import christie.codegear.StartCodeGear;
public class StartHelloWorld extends StartCodeGear {
public StartHelloWorld(CodeGearManager cgm) {
super(cgm);
}
public static void main(String[] args){
CodeGearManager cgm = createCGM(10000);
cgm.setup(new HelloWorldCodeGear());
cgm.setup(new FinishHelloWorld());
cgm.getLocalDGM().put("helloWorld","hello");
cgm.getLocalDGM().put("helloWorld","world");
}
}
ChristieDaemon.listen: bind to /0:0:0:0:0:0:0:0:10000
hello world
DGのアノテーション
- DGを取り出す際にはCG内で宣言した変数にアノテーションをつける。DGアノテーションには
Take、Peek、TakeFrom、PeekFrom、の4つがある。
- Take
- Peek
- 先頭のDGを読み込むが、DGが消去されない。そのため特に操作をしない場合、同じデータを参照し続ける。
- TakeFrom
- Remote DGM nameを指定することで、その接続先のDGM からTake操作をおこえる。
- PeekFrom
- Remote DGM nameを指定することで、その接続先のDGM からPeek操作をおこえる。
package christie.example.HelloWorld;
import christie.annotation.Peek;
import christie.annotation.Take;
import christie.codegear.CodeGear;
import christie.codegear.CodeGearManager;
public class HelloWorldCodeGear extends CodeGear {
@Take
String helloWorld;
@Override
protected void run(CodeGearManager cgm) {
System.out.print(helloWorld + " ");
cgm.setup(new HelloWorldCodeGear());
cgm.getLocalDGM().put(helloWorld,helloWorld);
}
}
まとめとこれから
- 本研究発表ではリモートエディタの開発とそれに伴う技術について述べた。現時点で実装できた構成は以下である。
- リモートエディタの基本となる命令のやり取り部分のコマンドパターン実装。
- 編集相違を防ぐためのアルゴリズムの発案と検証。
- 現時点では最低限のセッションを動かすまでの最低限の実装は終わっていない。これから取り組まなければならない課題として以下が挙げられる。
- スター型Topologyの接続を動的に行わせる。
- 編集するファイルの共有方法
- ファイルをそのまま送信すると、負担が大きいと予想される。
- 既存のエディタを同期通信に対応させる。
- 以上の課題の課題に取り組み、これからも実装を続けていきたい。