모든 구성 요소가 동일한 컴퓨터에 있는 동일한 Java 가상 머신의 동일한 힙 공간에서 실행된다면 가장 쉬울 것입니다. 그러나 실제로는 클라이언트가 Java를 실행할 수 있는 장치일 뿐이라면 그러한 단일 상황에 직면하지 않는 경우가 많습니다. 하다? 보안상의 이유로 서버의 프로그램만 데이터베이스에 액세스할 수 있다면 어떻게 될까요?
대부분의 경우 동일한 힙에 있는 두 개체 간에 메서드 호출이 발생한다는 것을 알고 있습니다. 서로 다른 컴퓨터에 있는 개체에 대해 메서드를 호출하려면 어떻게 해야 할까요?
일반적으로 우리는 소켓의 입출력 스트림을 통해 한 컴퓨터에서 다른 컴퓨터로 정보를 얻고, 다른 컴퓨터의 소켓 연결을 연 다음, 데이터를 쓰기 위해 OutputStream을 얻습니다. 컴퓨터, 다른 Java 가상 머신의 객체 메소드는 무엇입니까? 물론 통신 프로토콜을 직접 정의하고 설계하여 호출한 다음 실행 결과를 소켓을 통해 다시 전송할 수 있으며 로컬 시스템에서 메서드를 호출하는 것과 같을 수도 있습니다. as other heaps)), 그러나 이는 일반적인 호출과 같아야 합니다.
이것이 바로 RMI가 우리에게 제공하는 것입니다.
원격 프로시저 호출 설계
생성할 항목은 서버, 클라이언트, 서버 보조시설, 클라이언트 보조시설 4가지 입니다.
1. 클라이언트 및 서버 응용 프로그램을 만듭니다. 서버 응용 프로그램은 클라이언트가 호출할 메서드가 포함된 개체인 원격 서비스입니다.
2. 클라이언트 및 서버 측 도우미를 생성하면 클라이언트와 서버의 모든 기본 네트워크 입력/출력 세부 정보를 처리하여 클라이언트와 프로그램이 로컬 호출을 처리하는 것처럼 보이게 됩니다.
보조 시설의 작업 보조 시설은 실제로 통신을 수행하는 개체로, 클라이언트 개체가 원격 메서드를 호출하는 것처럼 보이지만 실제로는 메서드 A를 호출하는 것입니다. 소켓 및 스트리밍 세부 정보를 로컬로 처리하는 프록시 서버 측에서 서버의 보조 기능은 소켓을 통해 클라이언트 기능의 요청을 연결하고 패키지 정보를 구문 분석한 다음 실제 서비스를 호출하므로 서비스 개체의 경우 이는 After입니다. 로컬 서비스에서 보조 기능을 호출하면 반환 값을 가져오고 이를 래핑한 후 (소켓의 출력 스트림을 통해) 클라이언트의 보조 기능으로 다시 보냅니다. 클라이언트의 보조 기능은 정보의 압축을 풀고 이를 클라이언트 개체로 전송합니다.
메소드를 호출하는 과정
1. 클라이언트 객체는 보조 시설 객체에서 doBigThing()을 호출합니다.
2. 클라이언트의 부대시설은 통화정보를 패키징하여 네트워크를 통해 서버의 부대시설로 전송합니다.
3. 서버 측 보조 설비는 클라이언트 측 보조 설비의 정보를 디코딩하여 이를 사용하여 실제 서비스를 호출합니다.
이 프로세스를 설명하는 다이어그램은 다음과 같습니다.
JavaRMI는 클라이언트측 및 서버측 도우미 개체를 제공합니다.
Java에서 RMI는 클라이언트 측 보조 시설을 실제 서비스처럼 보이게 만드는 방법도 알고 있습니다. 즉, RMI는 클라이언트 호출에 대해 동일한 방법을 제공하는 방법도 알고 있습니다. .
또한 RMI는 클라이언트가 클라이언트(실제 서비스 에이전트)를 찾고 얻을 수 있도록 하는 서비스 쿼리 및 보조 기능을 포함하여 실행에 필요한 모든 인프라를 제공합니다.
RMI를 사용하면 네트워크나 입/출력 프로그램을 작성할 필요가 없습니다. 원격 메소드에 대한 클라이언트의 호출은 동일한 JVM(Java Virtual Machine)에서의 메소드 호출과 동일합니다.
일반 호출은 RMI 호출과 약간 다릅니다. 클라이언트에게는 이 메소드 호출이 로컬로 보이지만 클라이언트 보조 기능은 결국 소켓 및 스트림을 통해 호출됩니다. 에이전트는 이를 원격 정보로 변환합니다. 중간 정보가 JVM(Java Virtual Machine)에서 JVM(Java Virtual Machine)으로 전송되는 방식은 보조 기능 개체에서 사용하는 프로토콜에 따라 다릅니다.
RMI를 사용할 때 JRMP 또는 IIOP 프로토콜을 결정해야 합니다. JRMP는 Java 간의 원격 호출을 위해 설계되었습니다. 반면 IIOP는 Java를 호출할 수 있게 해줍니다. 객체나 다른 유형의 원격 메소드의 경우 CORBA는 일반적으로 RMI보다 더 까다롭습니다. 왜냐하면 양쪽 끝이 Java가 아닌 경우 끔찍한 변환 및 대화 작업이 많이 발생하기 때문입니다.
우리는 Java-to-Java 작업에만 관심이 있으므로 매우 간단한 RMI를 사용합니다.
RMI에서는 클라이언트 측 보조 시설을 스텁이라고 하고, 서버 측 보조 시설을 스켈레톤이라고 합니다.
원격 서비스를 만드는 방법
1.원격 인터페이스 생성
원격 인터페이스는 클라이언트가 원격으로 호출할 수 있는 메소드를 정의합니다. 이는 스텁과 서비스 모두 이 인터페이스를 구현합니다.
2. 원격 인터페이스 구현
이는 실제 실행 클래스입니다. 인터페이스에 정의된 메서드를 구현합니다. 클라이언트가 호출할 개체입니다.
3. rmic을 사용하여 스텁 및 스켈레톤 생성
클라이언트와 서버 모두에는 이러한 클래스를 생성하거나 이러한 클래스의 소스 코드를 생성할 필요가 없습니다. 이는 JDK에 연결된 rmic 도구를 실행할 때 자동으로 처리됩니다.
4. RMIregistry(rmiregistry)를 시작합니다.
rmiregistry는 전화번호부와 같으며 사용자는 여기에서 프록시(클라이언트의 스텁/도우미 개체)를 가져옵니다.
5. 원격 서비스 시작
서비스 객체는 실행을 시작해야 합니다. 서비스를 구현하는 클래스는 서비스 인스턴스를 시작하고 이를 RMIRegistry에 등록한 후에만 사용자에게 서비스를 제공할 수 있습니다.
서버 코드
인터페이스 정의
/**
*
*MyRemote.java
*
* 기능: TODO
* 클래스명 : MyRemote.java
*
* ver. 캐릭터 홀더 및 신규 콘텐츠가 업데이트되었습니다.
*──────────────────────────────────────────
* V1.00 2013-3-19 Su Ruo 모듈의 첫 번째 버전
*
* Copyright (c) 2013 dennisit Corporation All Rights Reserved.
*
* 이메일:<a href="mailto:[email protected]">이메일 보내기</a>
*
*
* Remote는 표시된 인터페이스로 메소드가 없음을 의미합니다. 그러나 RMI에는 특별한 의미가 있으므로 이 규칙을 따라야 합니다.
* 여기서는 확장이 사용되며 인터페이스는 다른 인터페이스를 상속할 수 있습니다.
*
*/
공개 인터페이스 MyRemote는 Remote{를 확장합니다.
/**
* 원격 인터페이스는 클라이언트가 원격으로 호출할 수 있는 메서드를 정의합니다. 즉, 클라이언트가 수행하는 다형성 클래스입니다.
* 이 인터페이스를 구현하는 스텁을 모바일화하고, 이 스텁이 네트워크 및 입출력 작업을 수행하므로 다양한 일이 발생할 수 있음
* 문제는 클라이언트가 이러한 유형의 위험을 인식하기 위해 예외를 처리하거나 선언하는 것입니다. 메소드가 인터페이스에서 예외를 선언하는 경우 메소드를 호출하십시오.
* 모든 프로시저는 이 예외를 처리하거나 다시 선언해야 합니다.
*
* 원격 메소드의 매개변수와 반환값은 기본적이거나 직렬화 가능해야 합니다.
* 패키지는 네트워크를 통해 전송되며, 직렬화를 통해 완료되면 반환값은 동일하므로 커스텀 타입을 사용하면 동일하다.
*, 직렬화되어야 합니다.
* @반품
* @throws RemoteException
* 인터페이스의 모든 메소드는 RemoteException을 선언해야 합니다.
*/
public String sayHello()는 RemoteException을 발생시킵니다.
}
/**
*
*MyRemoteImpl.java
*
* 기능: TODO
* 클래스 이름: MyRemoteImpl.java
*
* ver. 캐릭터 홀더 및 신규 콘텐츠가 업데이트되었습니다.
*──────────────────────────────────────────
* V1.00 2013-3-19 Su Ruo 모듈의 첫 번째 버전
*
* Copyright (c) 2013 dennisit Corporation All Rights Reserved.
*
* 이메일:<a href="mailto:[email protected]">이메일 보내기</a>
*
* 원격 서비스 객체가 되기 위해서는 객체가 원격 관련 기능을 가지고 있어야 합니다. 가장 간단한 방법은 UnicastRemoteObject를 상속받는 것입니다.
* (java.rmi.server에서) 이 상위 클래스가 작업을 처리하도록 합니다.
*
*/
공개 클래스 MyRemoteImpl은 UnicastRemoteObject를 확장하고 MyRemote를 구현합니다.
/**
* 상위 클래스의 생성자가 예외를 선언하므로 생성자가 위험한 프로그램 코드를 호출한다는 의미이므로 생성자를 작성해야 합니다.
*
* UnicastRemoteObject에는 작은 문제가 있으며 해당 생성자는 RemoteException을 발생시킵니다. 이를 처리하는 유일한 방법은 다음과 같습니다.
* 클래스가 초기화되면 부모 클래스가 RemoteException을 선언할 수 있는 위치가 있도록 자체 구현에 대한 생성자를 선언합니다.
* 생성자는 반드시 호출됩니다. 상위 클래스의 생성자가 예외를 발생시키면 우리가 선언해야 하는 사용자 정의 생성자도 예외를 발생시킵니다.
* @throws RemoteException
*/
protected MyRemoteImpl()이 RemoteException을 발생시킵니다.
}
/**
* 발신 인터페이스의 모든 메소드를 구현하지만 RemoteException을 선언할 필요는 없습니다.
*/
@보수
공개 문자열 sayHello(){
return "서버가 rmi hello world라고 말합니다!";
}
공개 정적 무효 메인(String[] args) {
노력하다 {
/**
* 이미 원격 서비스가 있으므로 원격 사용자가 해당 서비스에 액세스할 수 있도록 허용해야 합니다. 이를 초기화하고 RMI 레지스트리에 추가하면 됩니다.
* (실행 중이어야 합니다. 그렇지 않으면 이 프로그램이 실패합니다.) 객체를 등록할 때 RMI 시스템은 레지스트리에 스텁을 추가합니다.
* 클라이언트가 필요로 하는 것이기 때문에 서비스를 등록하려면 java.rmi.Naming의 rebind()를 사용하세요.
*/
MyRemote 서비스 = new MyRemoteImpl();
/**
* 원격 객체를 생성한 후 정적 Naming.rebind()를 사용하여 연결을 생성합니다. 등록된 이름은 클라이언트 쿼리에 제공됩니다.
*/
Naming.rebind("원격 Hello World", service);
} 잡기(예외 e) {
e.printStackTrace();
}
}
}
공개 무효 exec(){
노력하다 {
/**
* 클라이언트는 해당 메소드를 호출해야 하기 때문에 스텁 객체를 얻어야 합니다. 이는 RMI 레지스트리에 따라 다릅니다.
* 이름이 일치하는 서비스를 찾으려면 동일한 디렉터리를 검색하세요.
* 클라이언트는 RMIRegistry를 쿼리하고 스텁 객체를 반환합니다.
* Naming.lookup("rmi://127.0.0.1/Remote Hello World");
* 매개변수 설명
* rmi://127.0.0.1/Remote Hello World
* 127.0.0.1은 호스트 이름 또는 호스트 IP 주소를 나타냅니다.
* Remote Hello World는 등록된 이름과 동일해야 합니다.
*
*/
MyRemote 서비스 = (MyRemote)Naming.lookup("rmi://127.0.0.1/Remote Hello World");
문자열 tmp = service.sayHello();
System.out.println(tmp);
} 잡기(예외 e) {
e.printStackTrace();
}
}
공개 정적 무효 메인(String[] args) {
새로운 MyRemoteClient().exec();
}
}
JDK와 함께 제공되는 rmic 도구는 서비스 구현을 기반으로 스텁과 스켈레톤이라는 두 개의 클래스를 생성하며 명명 규칙에 따라 원격 구현 이름 뒤에 _Stub 또는 _Skeleton을 추가합니다. rmic에는 뼈대를 생성하지 않거나 생성된 클래스의 소스 코드를 관찰하거나 IIOP를 통신 프로토콜로 사용하는 등 여러 가지 옵션이 있습니다. 생성된 클래스는 현재 디렉토리에 배치됩니다. rmic은 구현된 클래스를 찾을 수 있어야 합니다. 따라서 구현이 있는 디렉토리에서 rmic를 실행해야 할 수도 있습니다(실제로는 패키지 디렉토리 구조와 전체 이름을 고려해야 할 수도 있습니다. 단순화를 위해 여기서는 패키지가 사용되지 않습니다).
rmiregistry를 시작하려면 명령줄을 호출하세요. 가장 쉬운 방법은 클래스의 디렉터리에서 실행하는 것입니다.
실행 스크린샷은 다음과 같습니다
알아채다:
클라이언트는 인터페이스를 사용하여 스텁에서 메소드를 호출합니다. 클라이언트의 Java 가상 머신에는 스텁 클래스가 있어야 하지만 클라이언트는 프로그램 코드에서 스텁 클래스를 참조하지 않습니다.
서버에는 스텁과 스켈레톤은 물론 서비스 및 원격 인터페이스도 있어야 합니다. 스텁이 RMIRegistry에 연결된 실제 서비스로 대체되기 때문입니다.
RMI 사용 시 흔히 발생하는 실수:
1. 원격 서비스를 시작하기 전에 rmiregistry를 시작하는 것을 잊었습니다(서비스를 등록하려면 Naming.rebind()를 사용하기 전에 rmiregistry를 시작해야 합니다).
2. 매개변수와 반환 유형을 직렬화하는 것을 잊었습니다(컴파일 중에는 감지되지 않으며 실행 중에만 감지됩니다).
3. 클라이언트에게 스텁 클래스를 넘겨주는 것을 잊어버리세요
RMI는 원격 서비스 작성 및 실행에 매우 적합하지만 대규모 엔터프라이즈 수준 애플리케이션의 경우 RMI만 사용하여 트랜잭션 관리, 대규모 동시 처리, 보안 및 데이터베이스 관리 등. 이를 위해서는 EnterpriseApplicationServer를 사용해야 합니다.
JavaEE 서버에는 웹 서버와 EJB(Enterprise JavaBeans) 서버가 포함되어 있습니다. EJB 서버는 RMI 호출과 서비스 계층 사이에서 작동합니다.
JINI에서의 RMI 적용
Jini는 RMI도 사용하지만(다른 프로토콜도 사용할 수 있음) 몇 가지 주요 기능이 더 있습니다.
1. 적응형 검색
2. 자가 치유 네트워크
RMI 클라이언트는 먼저 원격 서비스의 주소와 이름을 얻어야 합니다. 클라이언트의 쿼리 프로그램 코드에는 원격 서비스의 IP 주소나 호스트 이름(RMIRegistry가 있기 때문에)과 서비스에 의해 등록된 이름이 포함되어야 합니다.
하지만 JINI를 사용할 때 사용자는 단 한 가지, 즉 서비스에서 구현하는 인터페이스만 알면 됩니다!
Jini는 RMIRegistry보다 더 강력하고 적응성이 뛰어난 조회 서비스를 사용합니다. 왜냐하면 Jini는 쿼리 서비스가 온라인 상태가 되면 IP 멀티캐스트 기술을 사용하여 전체 네트워크에 정보를 전송하기 때문입니다. 쿼리 서비스가 브로드캐스트된 후 온라인 상태가 되며, 클라이언트는 전체 네트워크에 메시지를 보내 쿼리할 수도 있습니다.
서비스가 온라인 상태가 되면 네트워크에서 JINI 쿼리 서비스를 동적으로 탐색하고 등록을 신청합니다. 등록 시 서비스는 직렬화된 개체를 쿼리 서비스로 보냅니다. 이 개체는 RMI 원격 서비스의 스텁이 될 수 있습니다. 네트워크 장치의 드라이버 또는 클라이언트에서 실행될 수 있는 서비스 자체도 등록됩니다.
적응형 탐색의 작동 방식
1.Jini 쿼리 서비스는 네트워크에서 시작되며 IP 멀티캐스트 기술을 사용하여 자체적으로 홍보됩니다.
2. 시작된 또 다른 Jini 서비스는 새로 시작된 쿼리 서비스에 등록을 시도하게 되는데, 이름이 아닌 함수, 즉 구현된 인터페이스를 등록한 후, 직렬화된 객체를 쿼리 서비스로 보냅니다.
3. 인터넷 고객은 ScientificCalculator를 구현하기 위해 무엇인가를 얻고 싶어하지만 어디서 찾을 수 있는지 모르기 때문에 쿼리 서비스에 문의합니다.
4. 쿼리 서비스는 쿼리 결과에 응답합니다.
자가 치유 네트워크 운영
1. 특정 Jini 서비스는 등록이 필요하며 쿼리 서비스는 임대를 제공합니다. 새로 등록된 서비스는 정기적으로 임대를 갱신해야 합니다. 그렇지 않으면 쿼리 서비스는 서비스가 오프라인 상태인 것으로 가정합니다. 사용 가능한 서비스 네트워크 상태를 완료합니다.
2. 서비스 종료로 인해 서비스가 오프라인 상태이므로 임대가 업데이트되지 않고 쿼리 서비스가 시작됩니다.