Java >> Java tutorial >  >> Java

Java RMI – Java Remote Method Invocation Eksempel

1. Hvad er RMI

I denne artikel vil vi tale om RMI java-eksempler og fjernprocedurekald. (Remote Method Invocation) er en objektorienteret måde til RPC (Remote Procedure Call) til at realisere kommunikation mellem distribuerede objekter i et distribueret computermiljø. Det tillader et objekt at påkalde metoder på et eksternt objekt.

Java RMI, en Java-implementering af fjernmetodekald, som er en objektorienteret måde at foretage et fjernprocedurekald på, består af flere API'er under java.rmi pakke. Det tillader et Java-program, der kører på én virtuel Java-maskine (klient), at påberåbe sig metoder på en anden virtuel Java-maskine (server). Det understøtter især overførsel af serialiserede Java-objekter fra maskine til maskine over netværket, hvilket gør automatisk styring af distribuerede objekter til virkelighed. Ved hjælp af Java RMI bliver kompleksiteten ved at arbejde med lokale og eksterne objekter minimal. Typesikkerheden bevares og distribueret affaldsindsamling (DGC) bliver mulig.

Diagrammet nedenfor viser kernekomponenterne i Java RMI, og hvordan Java RMI fungerer. Vi har forenklet diagrammet og vil gennemgå alle de tekniske detaljer i afsnit 3, når vi bygger et eksempel.

2. Hvornår bruges Java RMI

Fra diagrammet ovenfor kan vi se, at Java RMI dybest set er en klient-server-model. Det kan bruges, når vi vil kommunikere med forskellige virtuelle Java-maskiner. Disse virtuelle Java-maskiner kan være på forskellige værter eller på den samme vært. Ud over fordelene nævnt i det foregående afsnit er en fordel ved Java RMI, at vi ikke behøver at genopfinde hjulet. Forretningslogikken kan implementeres og vedligeholdes ét sted og genbruges andre steder. Men da invokationsprocessen involverer serialisering/deserialisering og netværkstransport, er den langsom og ikke særlig pålidelig. Uventede fejl, såsom netværksfejl, kan opstå under påkaldelsen.

3. Opbygning af en simpel produktinformationstjeneste ved at bruge Java RMI

Forestil dig, at vi har en produktbeholdning til et e-handelswebsted. Produktbeholdningen indeholder ti tusinde produkter. Hver af dem har et unikt id og navn. Vores personale skal slå produktinformation op på forskellige enheder fra forskellige steder, såsom desktops, mobiltelefoner eller håndholdte terminaler. For at imødekomme disse krav kan vi bygge en produktinformationsserver, der indeholder opslagslogikken og produktinformationsklienter, der er installeret på forskellige enheder for at slå produktinformation op fra serveren. Lad os begynde at bygge det trin for trin ved hjælp af Java RMI.

3.1 Definition af kontrakten

For at kunne kommunikere mellem en RMI-server og en RMI-klient skal vi definere en kontrakt, der er kendt for begge ender. Java-grænseflader bruges til dette formål. RMI-fjerngrænseflader skal udvide java.rmi.Remote-grænsefladen. ProductInfoService-grænsefladen definerer metoder, vi gerne vil eksponere for klienter eksternt. For at gøre tingene enkle, definerer vi én metode getProductInfoById(int id) som returnerer produktinformationen efter det givne produkt-id. Bemærk, at metoden skal kaste java.rmi.RemoteException .ProductInfoService.java

public interface ProductInfoService extends Remote {
    /**
     * The name used in the RMI registry.
     */
    static final String SERVICE_NAME = "ProductInfoService";

    /**
     * Get product info by the given Id.
     * 
     * @param id the product id
     * @return a ProductInfo instance
     * @throws RemoteException
     */
    ProductInfo getProductInfoById(int id) throws RemoteException;
}

3.2 Oprettelse af produktinformationsserveren

Når vi har defineret kontrakten, kan vi begynde at bygge produktinformationsserveren. Der er to dele, der skal bygges:serviceimplementeringen og RMI-serveren.

I vores eksempel er ProductInfoServiceImpl klasse implementerer ProductInfoService og udvider java.rmi.server.UnicastRemoteObject klasse. Ved at udvide java.rmi.server.UnicastRemoteObject klasse, ProductInfoServiceImpl kan eksportere et eksternt objekt med JRMP (Java Remote Method Protocol) og få en stub, der kommunikerer til det fjerne objekt. For det første definerer vi en POJO ProductInfo med to felter:id og name . Bemærk, at ProductInfo skal implementere java.io.Serializable og vi skal sikre os, at ProductInfo klasse på både server- og klientsiden har samme serialVersionUID . Ellers vil serialisering og deserialisering mislykkes under fjernkaldet.ProductInfo.java

public class ProductInfo implements Serializable {
    // important: make sure the class on both client and server sides have the same value
    private static final long serialVersionUID = 1L;

    private int id;

    private String name;

    /**
     * Constructor.
     * 
     * @param id
     * @param name
     */
    public ProductInfo(int id, String name) {
        super();
        this.id = id;
        this.name = name;
    }

    /**
     * @return the id
     */
    public int getId() {
        return id;
    }

    /**
     * @param id the id to set
     */
    public void setId(int id) {
        this.id = id;
    }

    /**
     * @return the name
     */
    public String getName() {
        return name;
    }

    /**
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "ProductInfo [id=" + id + ", name=" + name + "]";
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + id;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        ProductInfo other = (ProductInfo) obj;
        if (id != other.id)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }
}

Derefter i implementeringen af ​​getProductInfoById(int id) metode, returnerer vi blot en ny ProductInfo-instans med id'et og navnet.ProductInfoServiceImpl.java

public class ProductInfoServiceImpl extends UnicastRemoteObject implements ProductInfoService {

    private static final long serialVersionUID = 1L;

    /**
     * Constructs the service.
     * 
     * @throws RemoteException
     */
    protected ProductInfoServiceImpl() throws RemoteException {
        super();
    }

    /**
     * Get the product info by the given id.
     * 
     * @param id the product id
     * @return a ProductInfo instance
     */
    public ProductInfo getProductInfoById(int id) throws RemoteException {
        return new ProductInfo(id, "Sample Product");
    }
}

Produktinformationsserveren konstruerer en instans af ProductInfoService og registrerer den i RMI-registret. RMI-registret er et separat program, der leveres med JDK, og du kan køre det fra kommandolinjen ved at skrive rmiregistry . Den kører som standard ved port 1099. For nemheds skyld starter vi RMI-registret programmatisk på den lokale vært ved port 1099. RMI-serveren kører i ca. 10 sekunder og venter på enhver RMI-anmodning fra klienter. Derefter lukker den RMI-registret ned og afsluttes.ProductInfoServer.java

public class ProductInfoServer {
    /**
     * @param args
     */
    public static void main(String[] args) {
        System.out.println("ProductInfoServer is starting...");

        try {
            // create a RMI registry on localhost at port 1099
            Registry registry = LocateRegistry.createRegistry(1099);

            System.out.println("RMI registry is running on port 1099");

            // create an instance of the service object
            ProductInfoService service = new ProductInfoServiceImpl();

            System.out.println("Binding ProductInfoService...");

            // bind it in the RMI registry
            registry.rebind(ProductInfoService.SERVICE_NAME, service);

            System.out.println("ProductInfoService is ready.");

            System.out.println("Wait for 10 seconds for any incoming client call before terminating the RMI registry...");

            // sleep 10 seconds
            Thread.sleep(10000);

            // unbind the service object
            registry.unbind(ProductInfoService.SERVICE_NAME);

            // remove the service object from the registry
            UnicastRemoteObject.unexportObject(service, true);

            System.out.println("Shutting down the RMI registry...");

            // shut down the registry
            UnicastRemoteObject.unexportObject(registry, true);

            System.out.println("ProductInfoServer has stopped.");
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(-1);
        }
    }
}

Kompiler og kør produktinformationsserveren fra IDE eller kommandolinjen, vi kan se følgende output på standardoutput.

ProductInfoServer is starting...
RMI registry is running on port 1099
Binding ProductInfoService...
ProductInfoService is ready.
Wait for 10 seconds for any incoming client call before terminating the RMI registry...
Shutting down the RMI registry...
ProductInfoServer has stopped.

3.3 Oprettelse af en RMI-klient

Når produktinformationsserveren er oppe at køre, hvordan kan vi så bruge produktinformationstjenesten leveret af serveren? En RMI-klient kommer i spil. Vi opretter en RMI-klient ved navn ProductInfoClient for at finde fjerntjenesteobjektet og kalde dets metode. I klienten bruger vi java.rmi.Naming klasse for at opnå en reference til ProductInfoService fjernobjekt i RMI-registret, der kører på den lokale vært ved port 1099. Så kan vi blot kalde getProductInfoById(int id) metode med et id "123" og udskriv de returnerede produktoplysninger til standardoutput.ProductInfoClient.java

public class ProductInfoClient {
    /**
     * @param args
     */
    public static void main(String[] args) {
        try {
            System.out.println("ProductInfoClient> get product info with id '123'...");
            // looks up the registry by service name and returns a stub
            ProductInfoService productInfoService = (ProductInfoService) Naming.lookup(ProductInfoService.SERVICE_NAME);
            // invoke the remote method via the stub
            ProductInfo productInfo = productInfoService.getProductInfoById(123);
            System.out.println("ProductInfoClient> production info received: " + productInfo.toString());
        } catch (Exception e) {
            System.err.println("ProductInfoClient> RemoteDate exception: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

Kompiler og kør produktinformationsklienten fra IDE eller kommandolinjen, vi kan se følgende output på standardoutput.

ProductInfoClient> get product info with id '123'...
ProductInfoClient> production info received: ProductInfo [id=123, name=Sample Product]

4. Java RemoteException

Da der er risiko for netværksproblemer under fjernopkald, er en undtagelse kaldet RemoteException kan ske. Hvis metodekaldet resulterer i, at en undtagelse bliver kastet, indikeres undtagelsen til den, der ringer. For mere information om Java RMI RemoteException og hvordan man håndterer det korrekt, se venligst dette eksempel:java.rmi.RemoteException – Sådan løses RemoteException

core java eksempel rmi
Java tag