본문 바로가기
개발 관련 정보/디자인패턴

4-1.RMI란

by MonoSoft 2023. 3. 16.
728x90
반응형

RMI란

 

1. Java RMI란?
1.1 개요

RMI(Remote Method Invocation)란?

1. 분산되어 존재하는 객체 간의 메시지 전송(메소드를 호출하는 것 포함)을 가능하게 하는 프로토콜

 

현대 시스템 개발은 3계층 시스템이 주류로 이루고,

이 3계층인 데이터베이스, 애플리케이션, 클라이언트 모두 객체를 통하여

구현하고 저장하는 일이 많아졌습니다.

그래서 이렇게 분산되어 존재하는 객체간의 메시지 전송을 할 필요가 생겼고

이를 가능케 하는 것이 RMI 프로토콜입니다.

 

2. RMI 자체는 분산객체 간의 통신을 구현하는 모든 프로토콜을 의미함. (CORBA, DCOM …)

RMI는 Java RMI 이전부터 쓰여지던 단어로써 분산객체를 개념을 구현하는

CORBA, DCOM 등을 포함하는 의미입니다.

 

RPC(Remote Procedure Call)란?

1. UNIX에서 가장 많이 사용되는 통신 프로토콜.

2. 네트워크에 연결되어 있는 다른 컴퓨터에 존재하는 함수를 실행해, 분산시스템을 구현.

3. 객체지향이 아닌, 순차적인 언어로 개발된 애플리케이션의 함수만을 호출.

 

왜 Java RMI를 쓰는가?

 

1. 구현하기 쉽다.

보통 네트워크 프로그래밍을 하려면 보통 소켓을 많이 사용합니다.

하지만 소켓으로는 프로토콜을 구현하기가 매우 까다롭고 힘이 듭니다.

하지만 Java RMI는 Socket 통신 자체를 하부에 숨기고, 상위 레벨에서 수행하여,

분산객체 간의 데이터 전송을 메소드를 부르는 것과 같은 방법으로

구현하기 때문에 훨씬 구현하기 쉽습니다.

 

2. 신뢰성이 보장된다.

신뢰성이 보장됩니다. 상위 레벨의 통신계층에서 수행하기 때문에

통신에 대한 신뢰성이 확보되고 또한 자바 자체에서 제공하는 라이브러리이기 때문에 또 그렇습니다.

 

3. Java 플랫폼을 사용한다.

자바 플랫폼을 사용한다는 점입니다.

자바의 RMI는 JVM에서 언제든 활용하고, 사용할 수 있습니다.

단, 동종의 Java가 아니면 제약이 따를 수 있습니다. ( ex. sun java 와 gnu java 간의 RMI)

 

1.2 구조 및 과정

* Server는 먼저 이름을 가지고 Registry에 bind를 해야합니다.

* Client가 원격 참조를 설정하기 위해 레지스트리에서 서버 이름을 조회합니다.

* stub은 파라미터를 skeleton에게 직렬화 하여 원격 메소드를

호출하고 결과를 다시 직렬화 하여 stub에게 돌려줍니다.

 

2. Java RMI 프로그래밍

 

2.1 Java RMI 구성

위 그림처럼 서버 쪽 PC에는 공통인터페이스,

구현클래스, 스켈레톤(스텁) 클래스, 서버실행 클래스가 필요하고

클라이언트 쪽 PC에는 공통인터페이스, 스텁 클래스, 클라이언트 실행 클래스가 필요합니다.

 

2.2 Server 프로그래밍

- 공통 인터페이스

 

// SampleI.java

package gym.dev;

import java.rmi.Remote;

import java.rmi.RemoteException;

public interface SampleI extends Remote

{

int Add(int num1, int num2) throws RemoteException;

String Echo(String Msg) throws RemoteException; 

}

 

Server 쪽 프로그래밍을 해보겠습니다.

현재 보시는 코드는 인터페이스 입니다.

 

클라이언트의 객체가 서버의 객체가 갖고 있는

메소드를 부를 때, 이 인터페이스를 사용하여 부르게 됩니다.

 

인터페이스는 클라이언트와 서버 모두 같은 것을 갖고 있어야 합니다.

패키지명은 gym.dev로 하였습니다.

 

주의할 점은 패키지명은 클라이언트와 서버, 인터페이스

모두 동일하게 사용하여야 합니다.

 

한가지 더 봐야할 점은든 원격 인터페이스는

Remote 클래스를 상속하여야 하고 선언되는 메소드들은

모두 RemoteException 예외 클래스를 던져야 합니다.

그래야만 통신을 할 수 있습니다. 서버에 구현하는 메소드는

간단하게 덧셈을 하는 Add 메소드와 입력된 값을 되돌려주는

Echo 메소드를 구현하였습니다.

 

- 구현 클래스 & 스켈레톤 클래스 & 서버 실행 클래스

 

// Server.java

package gym.dev;

import java.rmi.registry.Registry;

import java.rmi.registry.LocateRegistry;

import java.rmi.RemoteException;

import java.rmi.server.UnicastRemoteObject;

 

public class Server implements SampleI {

public Server() {}

// 함수 구현

public String Echo(String Msg) {

return Msg;

}

 

public int Add(int num1, int num2) {

int sum = num1 + num2;

return sum;

}

// 서버 실행 메인

public static void main(String args[]) {

try {

// 원격 객체를 export하여 통시하는 stub를 생성 및 접속할 수 있게끔 한다.

Server obj = new Server();

SampleI stub = (SampleI) UnicastRemoteObject.exportObject(obj, 0);

// RMI registry에 원격 객체(인터페이스가 상속)의 stub을 gongyumin와 bind 한다.

Registry registry = LocateRegistry.getRegistry();

registry.bind("gongyumin", stub);

 

// 서버 준비 완료

System.err.println("Server ready");

} catch (Exception e) {

System.err.println("Server exception: " + e.toString());

e.printStackTrace();

}

}

}

 

서버 클래스를 프로그래밍 하겠습니다.

우선 Server.java 파일에 방금 만들었던 SampleI 인터페이스를 Server.java에 구현합니다.

그리고 인터페이스 함수들을 구현할 때에는 RemoteException 객체를 던지지 않아도 됩니다. 

왜냐하면 인터페이스에서 이미 RemoteException을 throw로 처리했기 때문에

사용하는 클라이언트 쪽에서 예외를 처리해 주어야 합니다.

 

그리고 main 함수를 살펴보면, 29~30라인에서

서비스를 공급하는 원격 객체를 생성합니다. 

 

그리고 원격 객체는 Java RMI Runtime으로 export되어야 합니다.

이 과정을 통해 stub이 만들어 집니다.

 

위 코드에서 만들어진 서버 측 stub을 클라이언트에서 찾을 수 있도록 

RMI registry에 등록해주어야합니다. 

그 부분을 수행하는 코드는 33~34라인 입니다.

 

이 부분에 설명드리고 넘어가겠습니다. 이 부분에 LocateRegistry.getRegistry() 함수는

오버로딩 되어 있어서 인자가 없이 호출 된다면, 디폴트 포트인 1099를 사용하고,

포트를 설정하고 싶다면 숫자 값을 넣어줄 수 있습니다. 

 

아래에서 클라이언트에서도 이 함수를 사용할 때, 다시 설명드리겠습니다.

그리고 아랫 줄에서 gongyumin라는 이름으로 bind하여 stub 즉, 원격 객체를 등록합니다.

 

2.3 Client(User 포함) 프로그래밍

- 스텁 클래스 & 클라이언트 실행 클래스

 

// Client.java

package gym.dev;

import java.rmi.registry.LocateRegistry;

import java.rmi.registry.Registry;

public class Client {

public static void main(String[] args) {

String host = (args.length < 1) ? null : args[0];

try {

// Rmi registry에 서버 IP, port를 설정한다.

Registry registry = LocateRegistry.getRegistry(host);

// Rmi registry.lookup을 통해 gongyumin이름을 찾아 stub을 가져온다.

SampleI stub = (SampleI) registry.lookup("gongyumin");

// server의 함수를 호출한다.

String ResultEcho = stub.Echo("Hello World");

int ResultAdd = stub.Add(125, 175);

System.out.println("ResultEcho : " + ResultEcho);

System.out.println("ResultAdd : " + ResultAdd); 

} catch (Exception e) {

System.err.println("Client exception: " + e.toString());

e.printStackTrace();

}

}

}

 

이어서 Client 쪽 프로그래밍에 대해 설명 드리겠습니다.

Client는 2가지로 방법으로 나눠서 Client에 User까지

포함한 것과 분리된 것으로 나눠 설명하겠습니다.

 

여기서 Client만을 의미하는 것은 스텁클래스를 뜻합니다.

User가 뜻하는 것은 서버 쪽 메소드를 호출하고

리턴된 결과 값을 사용하는 사용자를 뜻합니다.

 

우선 Client에 User가 포함된 케이스에 대해 설명 드리겠습니다.

클라이언트 측에도 서버 쪽과 같은 인터페이스 파일이 필요합니다.

하지만, 인터페이스 파일은 서버에서 만들었던 것이 있기에 프로그래밍 설명은 생략하겠습니다.

 

다음으로 Clinet.java를 설명하겠습니다.

15 라인을 보시면, Server쪽에서 보셨던 LocateRegistry.getRegistry() 함수입니다.

오버로딩이 되어 있기 떄문에, 서버 측의 IP와 포트번호를 적어줍니다.

이 역시 디폴트 시, localhost를 사용하고 포트는 1099를 사용합니다.

그 후에, 18라인에서 gongyumin를 lookup하여 stub을 찾아오면 연결이 된 것 입니다.

21~22라인을 보면, 서버의 메소드를 stub을 통하여 호출할 수 있습니다.

 

2.4 Client, User 프로그래밍

- 스텁 클래스

// Client.java

// 패키지 및 생성자 중략…

public String CallClientEcho ( String host,int port,String Msg

)

{

try {

// Rmi registry에 서버 IP, port를 설정한다.

Registry registry = LocateRegistry.getRegistry(host, port);

// Rmi registry.lookup을 통해 gongyumin이름을 찾아 stub을 가져온다.

SampleI stub = (SampleI) registry.lookup("gongyumin");

 

// server의 Echo 함수를 호출한다.

String ResultEcho = stub.Echo(Msg);

// User에게 결과 값 리턴

return ResultEcho; 

} catch (Exception e) {

return "Occur Exception";

}

}

 

Colored by Color Scripter

 

이번에는 Client와 User를 구분하려 프로그래밍 해보겠습니다.

먼저 스텁 클래스인 Client.java를 보겠습니다.

앞에서 설명했던 10라인, 13라인, 16라인은 3부분은 변하지 않았습니다.

하지만, User가 호출 할 메소드를 생성합니다.

파라미터로는 IP, Port번호를 받고 서버로부터 받은 결과 값은 리턴해 줍니다.

현재 소스에서는 echo함수만 보이지만 add 함수는 소스가 중복되는 부분이 많아 생략하였습니다.

- 클라이언트 실행 클래스

 

import gym.dev.Client;
public class User

{

private User() {}

public static void main(String[] args)

{

String host = (args.length < 1) ? null : args[0];

if (host == null)

{

host = "localhost"; // 또는 "127.0.0.1"

}

int port = 1099;

// Client 객체 선언

Client client = new Client(); 

String EchoResult = client.CallClientEcho(host, port, "Hello World!");

int AddResult = client.CallClientAdd(host, port, 125, 175);

System.out.println("EchoResult : " + EchoResult); 

System.out.println("AddResult : " + AddResult); 

}

이번에는 실행 클래스인 User 부분 프로그래밍 부분입니다.

17~ 20 라인 처럼 Client class를 import 하고, Client를 객체 선언 후, 호출하여 사용하면 됩니다.

 

3. Java RMI 실행하기

3.1 Server - Class로 실행

커맨드

javac -d <클래스 파일 생성 위치> <서버 자바> <인터페이스 자바>

Windows) start rmiregistry <포트번호> 

Unix) rmiregistry <포트번호> &

java -classpath <클래스 경로> -Djava.rmi.server.codebase=file:<스텁 클래스 위치>/ <서버 자바 클래스>

Colored by Color Scripter

Windows 실행 예제

javac -d H:\rmiSeminar\Server Server.java SampleI.java

start rmiregistry

java -classpath . -Djava.rmi.server.codebase=file:H:\rmiSeminar\Server\ gym.dev.Server

​실행시, class로 실행하는 방법과 jar로 실행하는 방법이 나뉩니다.

평소에 개발은 class로 하는게 편하지만 배포시에는 jar가 용이하기에

두 방법 모두 소개하겠습니다.

우선 서버 측에서 class로 실행하는 법에 대해 알아보겠습니다.

위쪽 스크립트처럼 진행을 하면 됩니다. 자세히 보면 우선, 컴파일을 합니다.

 

그 다음 rmi registry를 실행시키는데, 주의할 것은 Windows와 Unix에 커맨드에 차이가 조금 있습니다.

윈도우즈는 “start rmiregistry” 인 반해, 유닉스 계열은 “rmiregistry &”로 입력합니다.

그리고 또 주의해야하는 부분이 있습니다. 바로 6번 라인 부분입니다.

Codebase 부분에서 주소 끝에 슬러쉬 혹은 역슬러쉬로 입력하셔야 스텁 클래스를 찾아옵니다.

그렇지 않으면 에러가 발생합니다.

 

3.2 Server - jar로 실행

javac -d <클래스 파일 생성 위치> <서버 자바> <인터페이스 자바>

Jar –cvf <jar파일 이름>.jar <클래스 폴더 이름>

Windows) start rmiregistry <포트번호> 

Unix) rmiregistry <포트번호> &

java -classpath <jar의 경로> -Djava.rmi.server.codebase=file:<스텁 jar 위치> <실행할 클래스 이름>

 

Windows 실행 예제

( 현재 커맨드의 위치 : H:\rmiSeminar\Server )

 

javac -d H:\rmiSeminar\Server Server.java SampleI.java

jar -cvf Server.jar gym

start rmiregistry

java -classpath .;Server.jar -Djava.rmi.server.codebase=file:H:\rmiSeminar\Server\Server.jar gym.dev.Server

서버 측에서 jar로 실행시에는 3번 라인에서 처럼 jar를 생성하고 클래스 패스에 jar의 경로를 써줘야 합니다.

아까 주의해야했던 8번라인 codebase에서 jar를 사용할 경우는 닫지 않아야 합니다.

그리고, jar 사용시에 연결자를 주의하면서 커맨드를 실행해야 합니다.

윈도우즈는 연결자로 세미콜론(;)을, 유닉스계열은 연결자로 콜론(:)을 쓰기 때문입니다.

이외에 내용은 스크립트를 참조하시면 될 것 같습니다.

 

3.3 Client(User 포함) - Class로 실행

 

커맨드

 

javac -d <클래스 파일 생성 위치> <클라이언트 자바> <인터페이스 자바>

java -classpath <클래스 경로> <클라이언트 자바 클래스>

 

Windows 실행 예제

( 현재 커맨드의 위치 : H:\rmiSeminar\Client(include User) )

javac -d .Client.java SampleI.java

java -classpath . gym.dev.Client

 

3.4 Client, User - Class로 실행

커맨드

javac -d <클래스 파일 생성 위치> <클라이언트 자바> <인터페이스 자바>

javac -classpath <생성한 클래스 위치> <유저 자바>

java -classpath <클래스 경로> <클라이언트 자바 클래스>

Windows 실행 예제

( 현재 커맨드의 위치 : H:\rmiSeminar\Client(not include User) )

 

javac -d . Client.java SampleI.java

javac -classpath . User.java

java -classpath . User

 

3-4, User를 포함한 Client는 컴파일 후 실행하면 무리가 없습니다.

3-5, Client와 User가 분리된 쪽은 주의해야할 부분이 있습니다.

1번 라인에서 클라이언트 자바와 인터페이스 자바를 컴파일합니다.

2반 라인에서 1번라인에서 생성된 클래스와 유저 자바를 함께 컴파일해야 합니다.

왜냐하면, 유저 자바에서 클라이언트를 import하고 있기 때문입니다.

그 후 나머지는 무리없이 하실 수 있을 거라 생각됩니다.

 

3.5 Client(User 포함) - jar로 실행

커맨드

javac -d <클래스 파일 생성 위치> <클라이언트 자바> <인터페이스 자바>

Jar –cvf <jar파일 이름>.jar <클래스 폴더 이름>

java -classpath <jar 경로> <실행할 클래스 이름>

 

Windows 실행 예제

( 현재 커맨드의 위치 : H:\rmiSeminar\Client(include User) )

 

javac -d . Client.java SampleI.java

jar -cvf Client.jar gym

java -classpath .;Client.jar gym.dev.Client

 

3.6 Client, User - jar로 실행

커맨드

javac -d <클래스 파일 생성 위치> <클라이언트 자바> <인터페이스 자바>

Jar –cvf <jar파일 이름>.jar <클래스 폴더 이름>

javac -encoding euc-kr -classpath <jar파일 이름>.jar <유저 자바>

java -classpath <jar 경로> <실행할 클래스 이름>

 

Windows 실행 예제

javac -d . Client.java SampleI.java

jar -cvf Client.jar gym

javac -encoding euc-kr -classpath Client.jar User.java

java -classpath .;Client.jar User

 

Jar로 실행 역시

3-6, 3번 라인에서 생성한 jar파일을

5번 라인처럼 유저 자바와 함께 컴파일한다는 부분만 주의하면 되겠습니다.

3.7 Java RMI 실행 화면

 

마지막으로 커맨드에서 실행한 화면 캡쳐를 보여드리며 Java RMI 포스팅을 마치겠습니다.

<서버 측 커맨드>

<서버 측 디렉토리>

<클라이언트, 유저 측 커맨드>

<클라이언트, 유저 측 디렉토리>

 

728x90
반응형

'개발 관련 정보 > 디자인패턴' 카테고리의 다른 글

5.facade 패턴  (0) 2023.03.21
4-2.Prototype 패턴  (0) 2023.03.17
3.객체지향 - 패턴 이해를 위한 객체지향 개념 - OOP  (0) 2023.03.14
2.Factory 패턴  (0) 2023.03.13
1.MVC (Model - View - Controller)  (0) 2023.03.10

댓글