Home | Papers | Reports | Projects | Code Fragments | Dissertations | Presentations | Posters | Proposals | Lectures given | Course notes |
<< 18. Examination Questions 2000-2001 | 20. Examination 2002-2003: Error-free message replication & Multi-server client representation >> |
19. Examination Questions 2001-2002Werner Van Belle1 - werner@yellowcouch.org, werner.van.belle@gmail.com Abstract : A large collection fo questions for distributed systems.
Reference:
Werner Van Belle; Examination Questions 2001-2002; |
We gebruiken de volgende conventies bij het tekenen van message diagrams.
Welke van de volgende Message Sequence Charts zijn onmogelijk te implementeren met behulp van Java RMI en waarom ? Indien u een bepaalde MSC voor mogelijk acht, doch waarbij de implementatie niet 'triviaal' is, gelieve dan een korte uitleg erbij te geven. U kan (duidelijke !) aanduidingen maken op de tekeningen indien u dat wenst. Schrijf dan wel op elke pagina uw naam. Twee van de MSC's bevatten sockets, in dat geval kan u redeneren in termen van sockets ipv remote method invocations.
Antwoord: nee
Antwoord: nee
Antwoord: ja
Antwoord: Niets mis mee
Antwoord: Ook niets mis mee, required het synchronized keyword
Antwoord: britishNee, na de return aan serverzijde kan niets meer gedaan worden
Antwoord: Niets mis mee, het rare is dat de stub op de server kant gewoon werkt.
Antwoord: Op het einde kan stub2 niet skeleton1 benaderen
Antwoord: Is niet implementeerbaar met RMI omdat een client niet zomaar 3 stubs kan oproepen & omdat de stubs niet zomaar gaan wachten op elkaar.
Antwoord: britishIs implementeerbaar met behulp van een gesharede variabele
Antwoord: nee
Antwoord: dit is correct, de eigenschappen van de socket moeten wel aangepast worden, maar dat terzijde
Antwoord: is niet mogelijk omdat een skeleton nooit meerdere threads in een keer gaat starten
Antwoord: dit is volledig correct
Antwoord: asynchrone call gaan niet
Antwoord:
Antwoord: Stubs doen niet aan multicasting
Antwoord: niet mogelijk
Onderstaand zijn twee varianten van dezelfde vraag.
Het onderstaande is een interface die gebruikt kan worden om een faculteit te berekenen.
{
public int fac(int n, FacCalcer fc) throws RemoteException;
public int answer(int n, int value) throws RemoteException;
}
public class FacApp extends UnicastRemoteObject implements FacCalcer { public FacApp() throws RemoteException { } public int answer(int n, int value) throws RemoteException { return n*value; } public int fac(int n, FacCalcer fc) throws RemoteException { System.out.println("fac("+n+")"); if (n==1) return 1; return fc.answer(n,fac(n-1,this)); } public static void main(String args[]) { FacCalcer fc; FacApp self=null; try { int answer; self=new FacApp(); try { fc=(FacCalcer)Naming.lookup("faccalcer"); answer=fc.fac(4,self); System.out.println("The answer my friend ... "+answer); } catch (NotBoundException e) { System.out.println("faccalcer bound"); Naming.bind("faccalcer",self); } } catch (Exception e) { System.out.println(e); } } }
Dit programma werkt en heeft indien het twee maal gestart wordt de onderstaande uitvoer.
> Java FacApp &
faccalcer bound
> Java FacApp
fac(3)
fac(2)
fac(1)
The answer my friend ... 6
>
Onderstaand is een interface die gebruikt kan worden om een faculteit te berekenen.
{
public int fac(int n, FacCalcer fc) throws RemoteException;
}
public class FacApp extends MulticastRemoteObject implements FacCalcer { public FacApp() throws RemoteException { } public int fac(int n, FacCalcer fc) throws RemoteException { System.out.println("fac("+n+")"); if (n==0) return 1; return n*fc.fac(n-1,this); } public static void main(String args[]) { FacCalcer fc; FacApp self=null; try { int answer; self=new FacApp(); try { fc=(FacCalcer)Naming.lookup("faccalcer"); answer=fc.fac(4,self); System.out.println("The answer my friend ... "+answer); } catch (NotBoundException e) { System.out.println("faccalcer bound"); Naming.bind("faccalcer",self); } } catch (Exception e) { System.out.println(e); } } }
Dit programma werkt en heeft indien het twee maal gestart wordt de onderstaande uitvoer.
> Java FacApp &
faccalcer bound
> Java FacApp
fac(4)
fac(3)
fac(2)
fac(1)
fac(0)
The answer my friend ... 24
>
Een enorm nadeel aan RMI is dat de standaard stubs niet goed overweg kunnen met multi-casts. (Dit is 1 bericht dat naar verscheidene clients tegelijk verzonden wordt). We willen nu een stub ontwikkelen die in staat is automatisch meerdere clients te contacteren voor eenzelfde request. Bijvoorbeeld: een clientprogramma vraagt aan de registry de addressenlijst voor de naam 'foo'. De registry zal 5 addressen weergeven. De stub die hierbij aangemaakt wordt, zal nu bij elke incoming call de 5 addressen contacteren voor het afhandelen van (bijvoorbeeld) de methode 'helloWorld'.
Een tweede probleem dat we met onze 'enhanced' stubs willen oplossen is de mogelijkheid tot het laten openstaan van een connectie. Normaliter zal Java RMI voor elke call steeds opnieuw een socket openen. (Is men braindead bij Sun of zit hier een reden achter ?). Daar dit veel te traag gaat willen we dit niet meer, we willen nu een socketconnectie zo lang mogelijk open laten.
Bespreek waarom transacties noodzakelijk zijn binnen open gedistribueerde systemen. Geef eveneens een voorbeeld.
We hebben een server die de volgende interface aanbied. Alle messages worden asynchroon verstuurd
outgoing messages from server:
color(int x, int y, int val)
transaction(int tx)
lock(int tx, boolean obtained)
transactionAborted(int tx)
transactionComitted(int tx)
Het idee is nu dat we een forward proces schrijven dat zich exact hetzelfde gedraagt als de server, maar in plaats van de functionaliteit zelf te implementeren zou deze forwarder requests moeten doorsturen naar een array van servers. Dit zoals op onderstaande tekening aangeduid is.
We willen de toplogie van een netwerk automatisch detecteren. Hiertoe zullen we op alle machines die deel uitmaken van het netwerk een proces draaien. Al de processen binnen het netwerk kennen elkaar. Als uitvoer van de gehele berekening willen we een 4 dimensionale matrix die aangeeft of 2 connecties met elkaar een geshared deel hebben.
De processen worden allen gestart en hebben exclusieve toegang tot het netwerk. U mag er dus vanuit gaan dat geen enkel ander proces buiten onze processen op het netwerk draaien.
We hebben een proces A dat data bewerkt en uitstuurt naar proces B. Proces B bewerkt de data op zijn manier en stuurt de data verder naar proces C. Deze vorm van parallellisatie word pipelining genoemd. Het is natuurlijk zo dat als proces A rapper data bewerkt dan proces B, proces B langzaam maar zeker gaat verzuipen in zijn backlog.
We willen er nu voor zorgen dat geen enkel proces meer moet verwerken dan het kan verwerken. We willen dit implementeren aan de hand van sockets. Schrijf het protocol neer dat u hiervoor zou gebruiken.
De basisfunctionaliteit van actors word beschreven aan de hand van een aantal klassen:
public interface ActorInterface extends java.rmi.Remote { public void receiveMessage(Message m) throws RemoteException; }
public class Actor extends UnicastRemoteObject implements ActorInterface, Runnable, Remote { protected String actor; private MessageQueue messages; public Actor(String name) throws RemoteException { try { actor=name; Naming.bind(name,this); messages=new MessageQueue(); } catch (Exception e) {...} // the line below starts a new thread that immediatelly invokes run // on this object. After _starting_ run, the control flow is returned new Thread(this).start(); } public void sendMessage(String target, Message m) { try { Remote rr=Naming.lookup(target); System.out.println(actor+" sends message"); ActorInterface r = (ActorInterface)rr; r.receiveMessage(m); } catch (Exception e) {...} } public void receiveMessage(Message m) throws RemoteException { try { System.out.println(actor+" received message"); messages.push(m); } catch (Exception e) {...} } public void run() { while(true) { Message m=messages.pop(); try { System.out.println(actor+" starts handling message"); handleMessage(m); System.out.println(actor+" stops handling message"); } catch (Exception e) {...} } } public void handleMessage(Message m) { System.out.println("Please define a handleMessage(Message m) in your subclass"); } }"); ?>
class MessageQueue { private Message busywith=null; private LinkedList ll = new LinkedList(); /** * Creates a messagequeue, which can be used to implement a synchronous * receive at consumer side and an asynchronous send ad producer side. In * other words. The push is asynchrounous and puts a message in the Q, * while the pop blocks if there is no message available. */ public MessageQueue() { } public synchronized void push(Message o) { ll.addLast(o); notify(); } /** * Blocking pop to retrieve the next message on the Q. If the Q is empty * this method will block until a message is pushed upon the Q. This method * is used to synchronize two threads to each other using a consumer * producer like approach. */ public synchronized Message pop() { try { if (ll.isEmpty()) { busywith=null; wait(); } busywith = (Message)ll.getFirst(); ll.removeFirst(); return busywith; } catch(Exception e) {...} } }
De berichten tussen actors moeten de Message interface implementeren. In het voorbeeld dat we verder gaan uitwerken, zijn er slechts twee messages geimplementeerd:
interface Message extends Serializable { }
public class JuliaRequest implements Message { public String returnTo; public double cx,cy; public int id; public JuliaRequest(String returnImageTo, double ax, double ay, int i) { returnTo=returnImageTo; cx=ax; cy=ay; id=i; } }
public class ImageMessage implements Message { public int xs, ys, id; int[] content; String answerFrom; public ImageMessage(BufferedImage img, int x, int y) { xs=x; ys=y; content=img.getRGB(0, 0, xs, ys, null, 0, xs); } public ImageMessage(BufferedImage img, int x, int y, String from, int i) { this(img,x,y); id=i; answerFrom=from; } public BufferedImage convertToBufferedImage() { BufferedImage image; image=new BufferedImage(xs, ys, BufferedImage.TYPE_INT_RGB); // im.setRGB(0,0,XS,YS,im.getImageData()); image.setRGB(0,0,xs,ys,content,0,xs); return image; } }
Op basis van bovenstaande basis klassen zijn twee actors gedefinieerd: De eerste is een FractalAnimator server, de tweede een FractalCalcer. De animator server zal aan een aantal clients vragen een Julia set te berekenen met bepaalde paramaters. Telkens dergelijke fractalcalcer actor antwoord zal de tekening getoond worden en een nieuwe request opgestuurd. De server klassen:
class FractalBufferedImage extends Canvas { BufferedImage image; int XS=0; int YS=0; FractalBufferedImage() { Frame f = new Frame(); f.setSize(XS,YS); f.add(this); f.show(); } public void setImage(BufferedImage img) { image=img; repaint(0); } public void paint(Graphics g) { if (image!=null) g.drawImage(image,0,0,this); } }
public class FractalAnimator extends Actor { protected FractalBufferedImage painter; protected String clients[]; protected double x, dx; protected int sendid; public FractalAnimator(String name, String args[]) throws RemoteException { super(name); painter=new FractalBufferedImage(); clients=args; x = -1.0; dx = 0.2; sendid = 1; // please note that we send immediatelly two requests to all // participating actors ! askForNextFrames(); askForNextFrames(); } protected void askForNextFrame(String proc) { x+=dx; sendMessage(proc,new JuliaRequest(actor,0.0,x,sendid++)); } protected void askForNextFrames() { int i=1; while(i<clients.length) { askForNextFrame(clients[i]); i++; } } protected boolean showFrame(ImageMessage frame) { painter.setImage(frame.convertToBufferedImage()); } public void handleMessage(Message m) { if (m instanceof ImageMessage) { ImageMessage im=(ImageMessage)m; showFrame(im); askForNextFrame(im.answerFrom); } else super.handleMessage(m); } public static void main(String args[]) { try { new FractalAnimator(args[0],args); } catch (Exception e) {...} } }
/** * calculates a julia set for the given coordinates and sends an answer * back */ public class AnimatedFractalCalcer extends Actor { Color colorscheme[]; int XS = 0, YS = 0, PREC = 7; public AnimatedFractalCalcer(String name) throws RemoteException { super(name); colorscheme=new Color[PREC+1]; for (int i=0; i<PREC; i++) { int phase=5*i/PREC; colorscheme[i]= new Color((phase+0)%6,phase,(phase+0)%6); } colorscheme[PREC]=Color.black; } void drawJuliaBSM(BufferedImage g, double cx, double cy, int w, int h) { double x, y, t; int k; for (double i=-2; i<2; i+=4.0/w) for (double j=-2; j<2; j+=4.0/h) { x = i; y=j; for (k=0; k<PREC; k++) { t = x*x-y*y+cx; y = 2*x*y+cy; x = t; if (x*x+y*y >= 4) break; } g.setRGB((int)((i+2)*w/4), (int)((j+2)*h/4),colorscheme[k].getRGB()); } } public void handleMessage(Message m) { if (m instanceof JuliaRequest) { // if it is a calcrequest we calculate the juliaset JuliaRequest cr=(JuliaRequest)m; BufferedImage img; img=new BufferedImage(XS, YS, BufferedImage.TYPE_INT_RGB); drawJuliaBSM(img, cr.cx, cr.cy, XS, YS); sendMessage(cr.returnTo,new ImageMessage(img,XS,YS,actor,cr.id)); } else super.handleMessage(m); } public static void main(String args[]) { try { new AnimatedFractalCalcer(args[0]); } catch (Exception e) {...} } }We starten deze klassen op de volgende wijze
rmiregistry & java AnimatedFractalCalcer //127.0.0.1/A & java AnimatedFractalCalcer //127.0.0.1/B & java FractalAnimator //127.0.0.1/S //127.0.0.1/A //127.0.0.1/B
Teken de control flow van het volledige programma in detail aan de hand van een MSC. Gedetailleerd betekend inclusief de stubs, skeletons en de rmiregistry.
Java RMI gebruikt een systeem waarbij exceptions worden teruggegeven op het ogenblik een error optreed. Als de server een programmatorische fout bevat zal de skeleton een exception terugsturen naar de client. Als de netwerkconnectie faalt tijdens het sturen van data zal de stub zelf een exception genereren. (Zoals geillustreerd in bovenstaande figuur). Dit systeem lijkt mooi, doch heeft een aantal inherente nadelen. Bespreek hoe een proces in een niet geldige, of moeilijk te kennen toestand kan geraken, na het optreden van een aantal errors. Maak dit duidelijk aan de hand van een MSC !
http://werner.yellowcouch.org/ werner@yellowcouch.org |