Java Distributed Objects 3 Masayuki Iwai tailor@ht.sfc.keio.ac.jp 2007 10 25
Table of contents 第1回10月11日木曜日5限 第2回10月19日金曜日6限 Rmi Advanced tutorial Distibuted objectsの目指す世界 Rmi 以前 tutorial Rmi tutorial 第2回10月19日金曜日6限 Rmi Advanced tutorial custom socket package Activatable 第3回10月17日木曜日5限 Jini programming Java Space programming Distributed Objectsの世界
Distibuted objectsの目指す世界 タスクの依頼 自由な連携 take write take write read
Local Method Invocation LocalClass localClass=new LocalClass(); outputObj=localClass.method(inputObj); inputObjはprimitive型でない限りcall by referenceとして渡される。 LocalRuntime LocalMainClass 192.168.0.1
RMI:Remote Method Invocation Remote Classのmethodを Callbyreferenceで呼びたいのがRMI RemoteClass RemoteRuntime method 192.168.0.2 LocalRuntime LocalMainClass 192.168.0.1
RMI:Bの側にCのクラスファイルがない場合 Interfaceを利用したCastだけではだめ BはCを保持しておく必要がないが最終的にはCの情報が必要->NetworkClassLoader Cの情報をダウンロード C NetworkClassServer NetworkClassLoader interface C C C’ A serializable B C 0100101001010 ObjectOutputStream VM_A VM_B
RMI:すこし立ち止まって クラスサーバは果たしてLocalRuntimeにある必要はあるのか? Cの情報をダウンロード C NetworkClassServer NetworkClassServer interface C C C’ A serializable B C 0100101001010 ObjectOutputStream VM_A VM_B
RMI:Marshalled Object MarshalledObject=serialized object+そのロード元 (可能な場合) となるコードベース URL BはCを知る必要がない:webserverは何処にあってもよい:動作はVM_B上 C http/get websrver Cクラスの解決情報 C C C C C’ A serializable B ObjectOutputStream VM_A VM_B
RMI: MarshalledObjectの実装 public final class MarshalledObject implements Serializable { /** * @serial Bytes of serialized representation. If <code>objBytes</code> is * <code>null</code> then the object marshalled was a <code>null</code> * reference. */ private byte[] objBytes = null; * @serial Bytes of location annotations, which are ignored by * <code>equals</code>. If <code>locBytes</code> is null, there were no * non-<code>null</code> annotations during marshalling. private byte[] locBytes = null; * @serial Stored hash code of contained object. * * @see #hashCode }
RMI:Object I/O Streamの2つの問題 オブジェクト単位 public class Account implements Serializable{ public void setMoney(){….} public Money getMony(){….} public void addMony(){….} public void subMony(){….} } =================書き出す側 VM_A=================== Socket s = ss.accept(); ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream()); Account acc= Bank.getAccount(me); oos.writeObject(acc); s.close(); =================読み出す側 VM_B=================== ObjectInputStream ois = new ObjectInputStream(s.getInputStream()); Account acc= (Account) (ois.readObject()); acc.addMony(1000); AccountクラスってRemoteに最新のものあるの? これって変更されるのRemoteのAccountクラスだよね
RMI: Socket/ObjectStreamレベルのことはStabが行う。 Stabクラスはrmic –v1.2 ServerClassのコマンドで生成 実はMarshalledObjectを利用してRMIは実装されている。 コードベースのためWebserverが必要 スタブオブジェクトが必要:動作はVM_A上 C クラスCは必要ない WEBSERVER インタフェースC’ UnicastRemoteObject C C C’ A C_stub C_stub B RMI VM_A VM_B
RMI:SocketProgrammingではない。 =================サーバ側 VM_A=================== public class AccountImple extends UnicastRemoteObject implements IAccount{ } AccountImple acc= Bank.getAccountImple (me); Naming.rebind(“registryhostname/Account_Iwai”,acc); 起動方法 >>java MainClass java.rmi.server.codebase=http://codebasehost/accout.jar =================クライアンド側 VM_B=================== String location = "rmi://registryhostname/Account_Iwai" ; acc=(IAccount)Naming.lookup(location); acc.addMony(1000); Socketプログラミングあらたな問題
Remote インタフェースの実装 public interface MoneyRemote extends Remote { public void increment() throws RemoteException; public void printMoney() throws RemoteException; public int getMoney() throws RemoteException; }
MoneyLocalClient public class MoneyLocalClient { public static void main(String argv[]){ MoneyRemote remotemoney; //インタフェースのみの宣言である点に注意 //codebaseがなければキャスト例外を起すはず try { String location = "rmi://localhost/MoneyServer" ; remotemoney=(MoneyRemote)Naming.lookup(location); System.out.println("Remote obj: " + remotemoney ); System.out.println("money local client:"+remotemoney.getMoney()); remotemoney.increment(); //remote呼び出し remotemoney.printMoney(); //remote呼び出し } catch (Exception e) { System.out.println("err: " + e); e.printStackTrace(); }
MoneyRemoteImpl public class MoneyRemoteImpl extends UnicastRemoteObject implements MoneyRemote { //ここのpublic必須 <- なぜか理由を考えてみてね。 public MoneyRemoteImpl() throws RemoteException { super(); } int money = 0; public void increment() throws RemoteException { money++; public void printMoney() throws RemoteException { System.out.println("Remote Money is:" + money); public int getMoney() throws RemoteException { return money;
RemoteMain public class RemoteMain { public static void main(String argv[]) { if (System.getSecurityManager() == null) { System.setSecurityManager(new RMISecurityManager()); } try { MoneyRemoteImpl money = new MoneyRemoteImpl(); money.increment(); Naming.rebind("rmi://localhost/MoneyServer", money); //実は名前は何でもよい。 System.out.println("MoneyServer bound in registry"); money.printMoney(); } catch (Exception e) { System.out.println(e.getMessage()); e.printStackTrace();
コマンド Stubの生成 Codebaseのためのjar化 Rmiregistryの起動 サーバの起動 クライアントの起動 rmic -v1.2 jp.ac.sfc.keio.sfc.tailor.rmi.test.remote.MoneyRemoteImpl Stubはクライアントにコピーが必要 Codebaseのためのjar化 jar cvf 1rmiMoneyRemoteImpl.jar jp\ac\sfc\keio\sfc\tailor\rmi\test\ Rmiregistryの起動 サーバの起動 java -Djava.rmi.server.codebase=http://www.ht.sfc.keio.ac.jp/~tailor/rg/1rmiMoneyRemoteImpl.jar -Djava.security.policy=securitypolicy.txt jp.ac.sfc.keio.sfc.tailor.rmi.test.remote.RemoteMain クライアントの起動 java -Djava.security.policy=securitypolicy.txt jp.ac.sfc.keio.sfc.tailor.rmi.test.local.MoneyLocalClient
わざとcodebaseでエラーを起す。 java -Djava.rmi.server.codebase=http://www.ht.sfc.keio.ac.jp/~tailor/rgX/ -Djava.security.policy=securitypolicy.txt jp.ac.sfc.keio.sfc.tailor.rmi.test.remote.RemoteMain Rmiregistryのcodebaseに開発コードがないことを確認
rmiregistryの正体 package jp.ac.sfc.keio.sfc.tailor.rmi.registry; import java.rmi.AlreadyBoundException; import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import jp.ac.sfc.keio.sfc.tailor.rmi.test.remote.MoneyRemoteImpl; public class RegistryMain { /** * rmiregistryが何をしているか知るプログラム */ public static void main(String[] args) { MoneyRemoteImpl money; try { money = new MoneyRemoteImpl(); money.increment(); Registry reg = LocateRegistry.createRegistry(1099); reg.bind("MoneyServer", money); } catch (RemoteException e) { // TODO 自動生成された catch ブロック e.printStackTrace(); } catch (AlreadyBoundException e) { }
万能rmiサーバを作成する。 public interface Compute extends Remote { Object executeTask(Task t) throws RemoteException; }
Task import java.io.Serializable; public interface Task extends Serializable { Object execute(); }
TaskMillis //executeはObjectを返しますので、プリミティブ・タイプのlongではなく、そのラッパーのLongが返り値 public class TaskMillis implements Task { public Object execute() { //executeはObjectを返しますので、プリミティブ・タイプのlongではなく、そのラッパーのLongが返り値 return new Long(System.currentTimeMillis()); }
ComputeEngine Compute engine = new ComputeEngine(); public class ComputeEngine extends UnicastRemoteObject implements Compute { public ComputeEngine() throws RemoteException { super(); } public Object executeTask(Task t) { return t.execute(); public static void main(String[] args) { if (System.getSecurityManager() == null) { System.setSecurityManager(new RMISecurityManager()); try { String name = "rmi://localhost/Compute"; Compute engine = new ComputeEngine(); Naming.rebind(name, engine); System.out.println("ComputeEngine bound"); } catch (Exception e) { System.err.println("ComputeEngine exception: " + e.getMessage()); e.printStackTrace();
ComputeClientMillis public class ComputeClientMillis { static void main(String[] args) { if (System.getSecurityManager() == null) { System.setSecurityManager(new RMISecurityManager()); } try { String name = "rmi://localhost/Compute"; Compute comp = (Compute) Naming.lookup(name); // ここからは、処理に応じて変わる。これは、サーバ時計を表示する例。 Long millis = (Long) (comp.executeTask(new TaskMillis())); System.out.println("Remote Date : " + new Date(millis.longValue())); System.out.println("Local Date : " + new Date()); } catch (Exception e) { System.err.println("ComputePi exception: " + e.getMessage()); e.printStackTrace();
実行コマンド rmic -v1.2 jp.ac.sfc.keio.sfc.tailor.rmi.compute.ComputeEngine jar cvf 2rmiCompute.jar jp\ac\sfc\keio\sfc\tailor\rmi\compute\ start rmiregistry java -Djava.rmi.server.codebase=http://www.ht.sfc.keio.ac.jp/~tailor/rg/2rmiCompute.jar -Djava.security.policy=securitypolicy.txt jp.ac.sfc.keio.sfc.tailor.rmi.compute.ComputeEngine java -Djava.security.policy=securitypolicy.txt jp.ac.sfc.keio.sfc.tailor.rmi.compute.ComputeClientMillis
ComputeClientPrime ublic class ComputeClientPrime { public static void main(String[] args) { if (System.getSecurityManager() == null) { System.setSecurityManager(new RMISecurityManager()); } try { String name = "rmi://localhost/Compute"; Compute comp = (Compute) Naming.lookup(name); TaskPrime taskprime =new TaskPrime(); //taskprime.setPrimenum("69143"); taskprime.setPrimenum(args[0]); // ここからは、処理に応じて変わる。これは、サーバ時計を表示する例。 Boolean ansprime = (Boolean) (comp.executeTask(taskprime)); System.out.println("prime : " + ansprime.booleanValue()); } catch (Exception e) { System.err.println("ComputePi exception: " + e.getMessage()); e.printStackTrace();
TaskPrime package jp.ac.sfc.keio.sfc.tailor.rmi.compute; import java.math.BigInteger; public class TaskPrime implements Task { String primenum="69127"; public Object execute() { return new Boolean(getPrime(primenum)); } private boolean getPrime(String prime) { BigInteger n = new BigInteger(prime); if (n.compareTo(BigInteger.ZERO) <= 0) { System.out.println("自然数を入力して下さい"); System.exit(1); System.out.println("入力された数は " + n.toString()); if (n.compareTo(BigInteger.ONE) == 0) { System.out.println("素数ではありません"); return false; else if (n.compareTo(BigInteger.valueOf(2)) == 0) { System.out.println("素数です"); BigInteger r = n.mod(BigInteger.valueOf(2)); if (r.compareTo(BigInteger.ZERO) == 0) { System.out.println("除算可能な数は 2"); System.out.println("素数ではありません"); return false; } BigInteger s = this.bigIntegerSqrt(n); for (BigInteger d = BigInteger.valueOf(3); d.compareTo(s) <= 0; d = d.add(BigInteger.valueOf(2))) { r = n.mod(d); System.out.println("除算可能な数は " + d.toString()); System.out.println("素数です"); return true; public static BigInteger bigIntegerSqrt(BigInteger x){ BigInteger b1 = new BigInteger(x.toString()), b2 = (b1.pow(2).add(x)).shiftRight(1).divide(b1); while(b2.compareTo(b1) < 0){ b1 = new BigInteger(b2.toString()); b2 = (b1.pow(2).add(x)).shiftRight(1).divide(b1); return b1; public String getPrimenum() { return primenum; public void setPrimenum(String primenum) { this.primenum = primenum;
素数計算をさせる java -Djava.security.policy=securitypolicy.txt jp.ac.sfc.keio.sfc.tailor.rmi.compute.ComputeClientPrime 69143 java -Djava.security.policy=securitypolicy.txt jp.ac.sfc.keio.sfc.tailor.rmi.compute.ComputeClientPrime 69149 java -Djava.security.policy=securitypolicy.txt jp.ac.sfc.keio.sfc.tailor.rmi.compute.ComputeClientPrime 69151 java -Djava.security.policy=securitypolicy.txt jp.ac.sfc.keio.sfc.tailor.rmi.compute.ComputeClientPrime 69157
Mission AttendanceTaskクラスを作成しなさい。 execute()メッソッドに サーバの私に 学籍番号,ログイン名,名前 をprintlnする機能を実装 岩井の立てているcompクラスに対して 通信を行え。 rmi://岩井のマシンのIPアドレス/Compute
Activation
RMI準備
Jini: Rmiregistryのホスト名の発見 SocketProgrammingは主に相手のホスト名/IPをあらかじめ知っている。 RMIはrmiregistryをおいている。 Rmiregistryのホスト名と そのため通信相手の登録名を知っていればいい Ex “Accoount_Iwai” どうやってRmiregistryのホスト名をどうやって見つけるのか?
Jiniのレイヤ構造 Jini RMI/HTTP Socket TCP/UDP/IP
Jiniの特徴で本日話すこと Discovery Lease
Jini:Doscovery and Join サービスが登録される時 C LookupService ①自分のIPをネットワークにマルチキャストで流す ②感知 ③感知確認のUnicast応答 (ディスカバリ) ④サービス情報の登録(ジョイン) サービス サービス B C A
LookupServiceの発見(前ページ①~③)までは一緒 Jini:Service Lookup LookupServiceの発見(前ページ①~③)までは一緒 C LookupService ⑤要求がある事を通知(activation?) ④サービス情報検索 ⑥利用プログラムの入手 A B サービス サービス ⑦サービス間と直接やり取り(RMI) C C
サーバ側 Cクラス public class AccountImle extends UnicastRemoteObject implements Account { public int getMony() throws RemoteException { return mony; }
サーバ側 Aクラス public class MainClass implements DiscoveryListener, Serializable { lookupDiscovery = new LookupDiscovery(“Bank”); lookupDiscovery.addDiscoveryListener(this); public void discovered(DiscoveryEvent evnt) { ServiceRegistrar[] regs = evnt.getRegistrars(); AccountImple acc = new AccountImple(); ServiceItem item = new ServiceItem(null, acc , attrs ); regs[i].register(item, Lease.FOREVER); //登録 }
クライアント側 Bクラス ServiceTemplate tmpl = new ServiceTemplate(null, classes , null); acc = (Account)(regs[i].lookup(tmpl)); acc. getMony(); //テンプレートにそうものが見つからなかった //場合は通知依頼を登録できる。 reg[i].notify(template, transitions, tellme, mo, Lease.ANY); 何の通知を依頼できるか? 新規登録、ステータス変化、消去の3種類
Jini:Lease Lookupに登録されたObjectはたまっていく一方なのか? →LookupServiceがわの都合で定期的に強制削除されます。 LookupService Lease expire Lease renew 障害 サービス サービス そこでずっと登録してほしければ サービス側から常にLease延長要求をだす。
JavaSpaces: Spaceプログラミングモデル たまっていくオブジェクトの概念を使った分散システム構築方法 Linda author = "D. Gelernter", title = "{Generative Communication in Linda}", note = " ACM Transactions on Programming Languages and Systems, 7(1):80--112, 1985.", year = "1985" T-Spaces(ibm)
Java Spaces 分散された仮想オブジェクト置き場 内部的にはJiniのLease機構 take write take read
Java Spaces write(obj) read() readIfExist() take() taleIfExist() notify() snapshot() 問題点 ObjectHeavyEaterクラスを作成されたら 広域ネットワークでつかえるか?
Napster:ログインプロセス どうやって広域ネットワークで相手と通信するか? ログインサーバ群 リダイレクトサーバ群 サーバンド ③ログインサーバの指定 ④ID、PASS ②ログインサーバの要求 ⑤自らのサーバント情報の登録 サーバンド ①自らのサーバント情報の構築
Napster:検索プロセス ログインサーバ サーバンド サーバント ①検索要求 ③詳細要求 ④サーバンドのIP、Portなど ②検索結果 ⑤自らのサーバントコネクト要求 ⑥直接ダウンロード
Napster:ダウンロードプロセス ログインサーバ サーバンド サーバント ③逆転送要求+相手サーバントIPアドレス ②逆転送要求+自分のIPアドレス サーバンド サーバント ①自らのサーバントコネクト要求 ④サーバントへのアップロード開始接続要求
Gnutella:GnutellaNetへの参加 WEBやキャッシュからneighborを見つける そのNeighborのさらにneighborをおくってもらう。 自分の子供が7人になるまで安定させる。
Gnutella:用語 Ping Pong Push ファイアウォール化されたサーバントが、ネットワークへのファイルベースのデータを提供することを可能にするメカニズム
Gnutella:用語 Query QueryHit 分散型ネットワークを検索するために必要となる主要なメカニズム。もしローカルのデータの中に検索にふさわしいものが見つかったら、クエリーのデスクリプターを受け取ったサーバントはクエリーヒットを返信。 QueryHit クエリーの返信。一致したクエリーのマッチングを行うデータを手\に入れるために、このデスクリプターは十分な情報を持つ受け取り人を提供
Gnutella:プロトコル
Gnuttela:Lamblaの提案(回線速度階層) MODEM T1 fiber ADSL
Gnutella:The Grid Self-Organizing Protocols 各サーバントに論理的に東西南北のコネクションの概念を持たせる。 自分を基点とする場合は東西南北も4方向へ伝達 北から受けたメッセージは東西南へ 南から受けたメッセージは東西北へ ….
Gnutella:The Grid Self-Organizing Protocols 格子の欠損に弱い
JXTA:オープンなプラットフォーム
JXTA:直接発見 Peer2 Peer3 Peer1 Peer4 BroadCast
JXTA:rendezvous-peer 待ち合わせ場所を使った間接通信 Peer5 Peer6 rendezvous-peer2 Peer3 rendezvous-peer7 Peer1 Peer4 TTL1 TTL3 TTL2
JXTA:rendezvous-peer 下流に転送もする おいらPeer5とPeer6のこと知っているよ。
JXTA:router-Peer Router Peer Message1 Peer2 Peer1 NATの中にいるPeerを外側にPeerと同等な世界に引き上げる仲立ち 内と外の電送手段の差異を吸収 Router Peer Message1 Peer2 Peer1 Router-peerに対する定期的問い合わせで 見つかればpeer1はPeer2からのMessageをみれる。
JXTA:router-peers ① ③ ② Peer1 Peer4 Router Peer2 Router Peer3
JXTA:Point-to-Point Pipe JXTA:Point-to-Point Pipe ピア間の通信チャネルの抽象化 一方向のメッセージの送信
JXTA:Propagate Pipe
Jxta:JXTA Virtual Network Jxta:JXTA Virtual Network ネットワークレベルでの制約を飛び越える
JXTA:優れた抽象化! Peer Discovery Protocol ピアの発見 Peer Resolver Protocol クエリの送受信 Peer Information Protocol ピアの状態 Peer Membership Protocol メンバーシップグループ の管理 Pipe Binding Protocol ピア間のパイプ構築 Peer Endpoint Protocol ピア間の経路の情報
7層モデル ネットワークの抽象化は一般ユーザレベルには、果たして分かりやすいか? そこで。。