오늘은 어디로 갈까...

JPPF(Java Parallel Processing Framework) 본문

낙서

JPPF(Java Parallel Processing Framework)

剛宇 2009. 3. 25. 08:06


1. 소개
 1.1 JPPF
  - JPPF(Java Parallel Processing Framework)은 손쉽게 병렬 처리 프로그램을 만들수 있게 도와주는 프레임워크이다.
  - 단순히 1대의 컴퓨터에서만 작동하는것이아니라, 네트워크로 연결된 수많은 컴퓨터를 사용하여 분산처리할 수 있게 도와준다.
  - 한마디로 요약하면, 오픈 소스 그리드 컴퓨팅 플랫폼(open source Grid Computing platform)이라 할 수 있는것이다.
 1.2 준비물
  - Java 1.5 이상
  - Apache Ant (실행 스크립트가 build.xml로 되어있다. 알아서 해결할 경우 필요없다.)
  - JPFF driver and node binary package (http://sourceforge.net/project/showfiles.php?group_id=135654 에서 다운가능)
    + jppf-full-xxx.zip, jppf-driver-xxx.zip, jppf-node-xxx.zip을 다운 받자.
      (jppf-full-xxx.zip 만 있어도 해결가능하지만 귀차니즘으로 인해 3개 다 받자)
 1.3 Architecture
  - JPPF는 3개의 레이어(Layer)로 구분할 수 있다.
    * client layer : client가 작업(taks) 실행을 요청하는 부분이다.
    * service layer : client로 부터 온 작업 실행 요청을 큐(queue)게 담은 후 사용 가능한 실행자(node)로 분배하는 부분이다. 간단히 말해서 서버 역할을 한다고 보면된다. JPPF Driver가 이 역할을 한다.
    * execution layer : 실제적으로 작업을 실행하는 부분이다. JPPF Node가 이 역할을 한다.
  - 즉, 사용자(client)가 작업 실행 요청을 하면, JPPF Driver로 요청이 넘어가고, JPPF Driver는 사용가능한 JPPF Node를 찾은후 작업들을 넘겨주고 응답 결과를 받는것이다.
  - 1개의 JPPF Driver에는 n개의 JPPF Node가 연결될수 있고, JPPF Driver 끼리 peer to peer topology 를 지원한다.
  - 멋진 그림이 있으니 참고바란다.(http://www.jppf.org/wiki/index.php?title=Architecture)



2. JPPF Driver, Node
  - JPPF Driver, Node 를 실행하지 않고, Local Execution 모드로도 처리가 가능하지만, 원래 취지(?)에 알맞게 원격으로 처리하게 만들어보자.
  2.1 JPPF Driver 실행
   - 다운받은 jppf-driver-xxx.zip을 적당한 디렉토리에 풀자.
   - 압축 푼 디렉토리에 보면 build.xml이 있을 것이다. ant로 실행하자.


  2.2 JPPF Node 실행
   - 다운받은 jppf-node-xxx.zip을 적당한 디렉토리에 풀자.
   - 압축 푼 디렉토리에 보면 build.xml이 있을 것이다. ant로 실행하자.
   - 뭐 컴퓨터가 빵빵하면, 여러개의 Node를 실행해보자. --;


3. JPPF Client 작업 환경 만들기
   - 여기서는 이클립스를 이용해서 client를 실행하겠다.
   - Java Project를 하나 만들자.
   - 다운받은 jppf-full-xxx.zip을 적당한 디렉토리에 풀자.
   - JPPF\build 디렉토리에 보면 jppf-client.jar, jppf-common.jar, jppf-common-node.jar 파일이 있는데, Java Project 하위에 lib 디렉토리를 만들어서 복사해주자.
   - JPPF\lib 디렉토리에 보면 필요한 라이브러리들이 들어있다. commons-logging.jar, log4j.jar를 Java Project 하위의 lib 디렉토리에 복사해주자.


   - Java Project 설정 정보에서 lib 디레토리에 있는 jar들을 클래스패스(classpath)에 추가해주자.

3. JPPF Task 만들기
   - JPPF Driver, Node는 작동하고 있으니, 이제 작업(Task)를 만들어보자.
   - 분산처리를 하기 위해서는 처리할 일(job)을 여러 작업(task)으로 나누어야한다. 자동으로 나눠주면 좋겠지만, 아직까지는 기술이 부족해서 사람이 직접 나눠줘야한다.
   - 작업(Task)은 org.jppf.server.protocol.JPPFTask 추상클래스를 상속받아 만들면 된다. 기본적으로 쓰레드에서 사용하는 Runnable, Callable을 사용 할 수있고, 자체적으로 어노테이션(Annotation) 기능도 지원한다.
package test.jppf;

import org.jppf.server.protocol.JPPFTask;

public class HelloWorld extends JPPFTask {
	public void run() {
		String hello = "헬로우, 월드";
		System.out.println(hello);
		setResult(hello);
	}
}


   - 뭐 별거 없다. JPPFTask 를 상속받아 run() 메소드를 구현하면 된다. 그런데 run() 메소드 어디서 많이 본거 같은데.... 그렇다 JPPFTask는 Runnable 인터페이스를 구현하고 있는것이다. 이 run() 메소는 Runnable 의 것이다.
   - 조금 다른 점은 setResult(hello); 이다. 결과값을 담기 위해 JPPFTask 클래스에 구현되어 있는 놈이다.


4. JPPF Job 실행기 만들기
   - 이제 만든 작업(task)을 실행해보자.
   - 작업(task)를 처리할 일(job)에 추가한 다음 JPPFClient 의 submit() 메소드를 이용하면 실행할 수 있다.
package test.jppf;

import java.io.*;
import java.util.List;

import org.jppf.client.*;
import org.jppf.server.protocol.JPPFTask;

public class HelloWorldRunner {

	public static void main(String[] args) {
		JPPFClient client = null;
		try {
			client = new JPPFClient();
			JPPFJob job = new JPPFJob();
			job.addTask(new HelloWorld());

			List results = client.submit(job);
			for (JPPFTask task : results) {
				if (task.getException() != null) {
					System.out.println("처리 중 에러가 발생했습니다.");
					StringWriter sw = new StringWriter();
					task.getException().printStackTrace(new PrintWriter(sw));
					System.out.println(sw.toString());
				} else {
					System.out.println("정상적으로 처리하였습니다.");
					System.out.println(String.valueOf(task.getResult()));
				}
					
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (client != null) {
				client.close();
			}
				
		}
		System.exit(0);
	}
}

   - 아직 실행하지 말자. 설정 파일을 만들지 않았기 때문에 작동하지 않는다.


5. 설정 파일 만들기
 - Java Project 하위에 config 디렉토리를 만든다. (귀찮으면 만들지 말고 src 아래에 파일을 만들면 된다.)
 - log4j를 위해 적당한 log4j.xml 파일을 만들어주고, jppf 설정 파일인 jppf-client.properties를 만든다.
#------------------------------------------------------------------------------#
# List of drivers this client may connect to                                   #
#------------------------------------------------------------------------------#

jppf.drivers = HelloServer

#------------------------------------------------------------------------------#
# Host name, or ip address, of the host the JPPF driver is running on          #
#------------------------------------------------------------------------------#

HelloServer.jppf.server.host = localhost

#------------------------------------------------------------------------------#
# port number for the class server that performs remote class loading          #
# default value is 11111; uncomment to specify a different value               #
#------------------------------------------------------------------------------#

HelloServer.class.server.port = 11111

#------------------------------------------------------------------------------#
# port number the clients / applications connect to                            #
# default value is 11112; uncomment to specify a different value               #
#------------------------------------------------------------------------------#

HelloServer.app.server.port = 11112

#------------------------------------------------------------------------------#
#  Enable local execution of tasks? Default value is false                     #
#------------------------------------------------------------------------------#

jppf.local.execution.enabled = false

#------------------------------------------------------------------------------#
# Number of threads to use for loacal execution                                #
# The default value is the number of CPUs available to the JVM                 #
#------------------------------------------------------------------------------#

jppf.local.execution.threads = 2


 - "jppf.drivers"는 사용할 서버명이고, 공백(" ")으로 구분해서 여러 서버를 설정할 수 있다. 그 아래 "서버명.jppf.server.host"로 서버 주소를, "서버명.class.server.port"으로 클래스 로딩에 사용할 포트를, "서버명.app.server.port"로 클라이언트 접속 포트를 설정할 수 있다. 그리고 여기는 생략됬지만, "서버명.node.server.port"로 노드(node)가 접속할 포트를 설정할 수도 있다.
 - "jppf.local.execution.enabled"는 로컬 실행을 할것인지 설정하는것인데, 우리는 원격(?)으로 사용할것이므로 "false"로 설정한다.


6. 실행하기
 - 실행하기 전에 몇가지 옵션을 추가해주어야한다.
 - HelloWorldRunner.java 를 선택 후, Run As ->  Run Configurations 가서 옵션을 추가해주자.

 - VM arguments에 "-Djppf.config=jppf-client.properties"를 추가하자

 - Advanced - Add Folders 를 선택해서 config 디렉토리를 클래스패스에 추가해주자


 - Run 을 클릭해서 실행해보자. 문제가 없다면 다음과 같은 결과를 볼 수 있을것이다.
* 실행 결과
[client: driver-1 (xxx.yyy.10.107:11198)] ClassServerDelegate.init(): Attempting connection to the class server
[client: driver-1 (xxx.yyy.10.107:11198)] ClassServerDelegate.init(): Reconnected to the class server
[client: driver-1 (xxx.yyy.10.107:11198)] : Attempting connection to the JPPF task server
[client: driver-1 (xxx.yyy.10.107:11198)] : Reconnected to the JPPF task server
정상적으로 처리하였습니다.
헬로우, 월드


7. 기타
 - addTask(new Annotated(), "첨자1"); @JPPFRunnable 어노테이션을 사용해서 해당 메소드를 실행 할 수 있다. (물론 첨자도 넘겨줄수 있다.)
 - addTask("메소드명", new Pojo(), 첨자1, 첨자2); 형식으로 POJO의 메소드를 실행할 수 있다.
 - addTask(new Runnable());
 - addTask(new Callable());
 - DataProvider 를 이용해서 데이터를 공유(?) 할 수 있다.
 - 직렬화(serializable)가 안되는 클래스일 경우 XStream(http://xstream.codehaus.org/)을 이용해서 XML 형식으로 변환후 처리하는 방법도 존재한다.

8. 투덜투덜
 - 아직 버그가 조금 존재하긴 하지만, 상당히 애쓴(?)흔적이 여기저기서 보인다. ClassLoader 기능도 구현되어있고, JMX도 지원하는 등 여러 멋진 기술을 볼 수 있어 행복하다. 단지 보안쪽이 조금 취약(?)해 보이지만, 뭐 이건 요즘 보안쪽을 공부하고 있는 부작용이라서 그런것일수도... 이런 프레임워크를 만들어봐야, 자바의 희노애락을 느낄 수 있는데, 매일 단순 노동이나 하고 있으니.... 흠흠, 그런데 이걸 사용할만한 곳이 있을까? 국내의 자바세계는, 자바의 처리속도보다 데이터베이스의 응답속도에 의해 성능이 결정되는 구조가 많은데... bruce force attack 용으로 쓸까나 ^^;

* 참고
 http://www.jppf.org/