view final_main/chapter2/chapter2.tex @ 15:c7ab31269230

update some file
author ichikitakahiro <e165713@ie.u-ryukyu.ac.jp>
date Sat, 15 Feb 2020 18:37:06 +0900
parents 7a840fd996a1
children 7293b6481e32
line wrap: on
line source

%\input{/Users/e155753/.tex/setup}

%%文書開始****************************
\begin{document} 
%%**************************************
\chapter{リモートエディタ}
リモートエディタとは他のマシン上に存在するファイルのバッファを別デバイスから開いて編集, 保存することができる機能である.
 
本研究ではこのリモートエディタを複数人が同時に同じファイルを編集し, その上変更がリアルタイムに反映されるように設計する. 
この章では同期式リモートエディタの実装の上で踏んだプロセスや,  開発の上で問題となる点と解決策について説明する。

\section{document listenerによる編集オフセット番号の読み取り}
    本研究の通信部分を構成する分散フレームワークChrisitie はjavaで開発する. 
したがってエディタ同士の基本通信環境の構成のため, Chrisitie と同様にjava 言語で作成したエディタのインスタンスを使い, 異なるマシン同士の同期の実現を目指した. 
自作エディタは java. swingの機能で構成されており, コードをオフセット番号で取り扱っている. 

追記または削除されたオフセット位置とその内容の取得はDocumentListenrを使用した. 
DocumentListenerのクラスはswingで実装したエディタ部分の入力と削除を検知し, 動作するメソッドであり, DocumentEvent内に入力されたオフセットとその長さや文字列が入力されるため, それをChrisitie側で検知し処理を行った. 

insertUpdateメソッドではエディタのバッファに入力が行われた際に自動的に実行され, removeUpdateメソッドは同様にバッファ内の文字のいずれかが削除が行われた際に実行される. 
他ノードから送信されてきた命令によるバッファの変更によっても実行され, 意図しないループが発生したため, 受信した命令では実行されないように記述をおこなった. 

コード\ref{code:DocumentListener}はinsertUpdate, removeUpdateの記述部分である. 
 
 \newpage
    
\lstinputlisting[caption=DocumentListenerのコード部分, label=code:DocumentListener]{./src/DocumentListener.java}


\section{Command パターンによる命令オブジェクトの作成}
リモートエディタを実装する上において, 各エディタは自身に起きたバッファの変更を他ノードに送信する必要がある. 
この変更の送り合いをCommand パターンとして実装した. 
Command パターンとは, 命令を一つのオブジェクトとして表現する方法である. 
コマンドパターンの利点として,

\begin{itemize}
\item インスタンスを利用して命令を作成するため, 後述のChristieのGearの概念と相性が良い. 
\item 命令に必要な内容をまとめて送信するため, 相違の発生を防ぐことができる. 
\item 命令の管理が行いやすい, 行列に並ばせ命令の順番を管理したり, 命令の際, 実行, 取り消しが容易になる. 
\end{itemize}

といった点が挙げられる. 
ソースコード:\ref{code:Command}は書き込み, 送信を行う際の命令をクラスとして作成したものである. 
このクラスのインスタンスを命令オブジェクトとして送信し合う. 
実際にChristieで命令オブジェクトの送受信を実装した解説は第四章にて行う. 

\lstinputlisting[caption=Commandパターンで実装した命令クラス, label=code:Command]{./src/Command.java}


\section{命令オブジェクトを実装する際に起きた問題}
インスタンス化した命令を他ノードに送信する際にエラーが発生し, 送信に失敗してしまうという問題が発生した.
クラスの送信の際のシリアライズはmsgpackクラスを利用している. 
msgpackクラスは,シリアライズしたいクラスにMessage アノテーションをつけることにより, シリアライズ化を行う.
 原因を調査した結果, 以下の原因が見つかった.
 
\begin{itemize}
\item Christieのjavaバージョンは11を使用していたが, msgpackバージョン0.6.12はjava11に対して対応していなかった.
\item msgpackの最新版0.8.20はシリアライズ機能が含まれなくなった.
\end{itemize}

以上の原因に対処するために以下のことを行った.

\begin{itemize}
\item Christieのjavaバージョンを8まで下げ, msgpackバージョン0.6.12を動作できるようにした.
\item シリアライズする命令クラスに対し, フィールドをpublic にした.
\item javassistのバージョンを最新版へ変更した.
\end{itemize}

javaのバージョンを下げたのは応急的な処置となってしまったが, これらの処置により問題なくCommandパターンでの命令実装を行うことができた. 
javaのバージョンに左右されずリモートエディタを実装するには, シリアライズの機能について他のパッケージを使うか, 自身で作成する必要が生まれた。

\newpage

\section{編集位置の相違}
セッション中のエディタ間の通信で生じうる, 編集結果の相違について説明する. 
エディタ同士のコマンドの送信はそれぞれが独立して行うため, 編集対象の領域にエディタ間で相違が生じる場合がある.

 例としてエディタが一対一の接続となっている時に発生しうる相違を図\ref{fig:difference} を使用して解説する.
 編集対象は各オフセット番号に同じ値の数字が入っているものとする. 
 EditorA ではオフセット番号 3 の 3 という 要素を削除 (テキストエディタ上のため削除されたオフセットにはその後ろの要素が繰り上げられる.), EditorB では オフセット番号 2 に A という要素を挿入するという編集をしたとする. 
 
 この編集を共通プロトコルとして互いに送信しあった際, 本来編集する予定だったオフセットの中身が異なってしまい編集結果に違いが生じてしまう. 
 これらの問題を解決することのできるエディタ同士の通信手法を作成しなければならない.

\begin{figure}[H]
\centering
  \fbox{
   \includegraphics[scale=0.6]{./images/difference_offset.pdf}
  }
\caption{通信のすれ違いによる編集位置の相違}
\label{fig:difference}
\end{figure}

\section{編集位置の相違解消方法}
編集するオフセットに相違が発生する条件として, サーバーとノードがお互いにコマンドを送り合った際, その命令コマンドが相手に到着する前に相手が自身のバッファに変更を加えてしまった場合に起きる. 
したがって, 相違の解消に必要なことは

\begin{itemize}
\item サーバーとノード間のコマンド送信のすれ違いが発生したということを検知する方法
\item すれ違いが発生した際に編集したオフセットのズレを修正する方法
\end{itemize}

が挙げられる. 

そこで, すれ違いが発生したか否かを検知するために、ノード同士が送信し合うコマンドにそれぞれ番号を割り振る方法を考案した.
図\ref{fig:Fix}を用いて解説する.

以下の図はコマンド番号を実装し, すれ違いを検知する際の動作の想定図である.
また, 本説明のコマンドは文字列の書き込み命令であり, バッファの指定されたオフセットにinsertされるため, それ以降のオフセット内の文字列は, 一つずつ後方オフセットへ移動する.

命令コマンドはクラスのインスタンスを用いて作られており, 中には1オフセット分(文字数分)の文字列, 入力するオフセット, コマンド番号, コマンドを発行したノード名が記録されている. 

二つのノードserver とnode は同じファイルのバッファを開いており, そのバッファには何も記述されていないとする.
また, serverが送信するコマンドは他のノードから送られてきたコマンドであるとする.
加えて各サーバー, ノードは実行したコマンドをスタック領域に保持しておいている.

コマンドとコマンド番号について以下の特性が存在する.
\begin{itemize}
\item 各ノードは最後に実行した数値を変数(図ではserverがcNum, nodeがnodeCNum) に保持している.
\item いずれかのノードがコマンドを発行したら, そのコマンドのコマンド番号は前に実行したコマンドの番号+1となる. そしてそれは送信されてきたコマンドか自分が発行したコマンドであるかは問わない.
\item 送信されてきたコマンドのコマンド番号が自身が保持しているコマンド番号+1でなければ、自身が先に発進したコマンドとすれ違いが発生していることが判明できる. (保持コマンド番号より同値以下の場合)
\end{itemize}

\newpage

つまり, 送信されたコマンドNo.102を実行したらそれぞれのノードは実行済みコマンド番号を102へ書き換えなければならない.
そして続いて自らがバッファに変更を加えたとき, その変更をコマンドNo103として作成し, 送信と実行済みコマンド番号を103に書き換える処理が必要となる.

以上の処理でコマンド送信のすれ違いを検知する.

\begin{figure}[H]
\centering
  \fbox{
   \includegraphics[scale=0.85]{./fig/FixCommand.pdf}
  }
\caption{コマンド番号を利用した相違の解消}
\label{fig:Fix}
\end{figure}

\newpage

コマンドのすれ違いを検知した際の処理は以下のように行う.
\begin{itemize}
\item 前提としてノードよりサーバーの整合性を維持したいため優先度は常にサーバーが高い.例えば同じオフセットにそれぞれのノードが別の文字列に変更した場合, サーバーノード共にサーバー側が発進したコマンドの文字列が書き込まれる.
\item すれ違いが発生したら, 各ノードはコマンドを記録しているスタックを参照し, 受け取ったコマンドのオフセットのズレを集計しそのコマンドを修正する.
\end{itemize}

図上ではserverからのinsert B for offset:2 とnodeからのinsert Z for offset:2の命令がすれ違いを起こしている. 
この場合, serverのバッファ状態が優先されるため, server側がnodeからの命令のoffset位置を調整してやらなければならない. 
この処理はserverと全ての接続しているノード間で独立して行われる.

コマンドのオフセットの修正は
\begin{itemize}
\item 受け取ったコマンドのオフセット > 受信コマンドとすれ違ったコマンドのオフセット のとき, 受診したコマンドのオフセットに+1する. 
\item 受け取ったコマンドのオフセット < 受信コマンドとすれ違ったコマンドのオフセット のとき, 受診したコマンドのオフセットを変えずにそのまま実行できる. 
\item 受け取ったコマンドのオフセット = 受信コマンドとすれ違ったコマンドのオフセット のとき, サーバー側の文字列が優先される. 
\end{itemize}
と言った方法で行うことができる.
これを削除, 文字列の置き換えにもそれぞれ対応する方法を作ることによりズレの修正を行う.

上記の修正方法のテストコードを試みた.
構成としては現時点では問題が見られていないが, 実装した際に初めて判明する通信速度や複数の独立した処理による影響も対処しなければならない可能性がある.

また, これからの実装を考えると
\begin{itemize}
\item 既存のエディタ(emacsやvim)のバッファ処理はオフセット単位で行われていない.
\item コマンドを保持し続けるとメモリ容量に無駄が発生する.
\item コマンドのput(送信)失敗についての想定ができていない.
\end{itemize}
といった点を修正する必要がある. 









%%文書終了****************************
\end{document}