jsch 라이브러를 이용하여 자주 쓰는 기능을 작성

Feature

  • ssh 접속하여 shell 명령을 실행 시키고 stdout 결과를 String List 가져온다
  • 여러 개의 shell 명령을 순차적으로 실행 시키고 stdout 결과를 가져온다
  • 로컬 파일을 ssh로 복사한다
  • ssh로 원격으로 파일을 로컬로 가져온다.
  • ssh key쌍(개인키와 공개키)을  생성한다.

Requirements

Code

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.KeyPair;
import com.jcraft.jsch.Session;

public class JSchUtil {
	private String hostname;
	private String username;
	private String identity=null;
	private String password=null;
	private boolean isDebugMode=false;

	public void enableDebug(){
		isDebugMode=true;
	}

	public void disableDebug(){
		isDebugMode=false;
	}

	public String getHostname() {
		return hostname;
	}

	public void setHostname(String hostname) {
		this.hostname = hostname;
	}

	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}

	public String getIdentity() {
		return identity;
	}

	public void setIdentity(String identity) {
		this.identity = identity;
		this.password =null;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
		this.identity=null;
	}

	public JSchUtil(){ }

	public JSchUtil(String username,String hostname){
		this.username = username;
		this.hostname = hostname;
	}

	public void setPortForwardingL(int port,String host,int hostport){

	}

	private Session getSession() throws JSchException{
		JSch jsch=new JSch();
		if (identity!=null) {
			jsch.addIdentity(identity);
			//jsch.setKnownHosts(new ByteArrayInputStream(hostname.getBytes()));
		}

		Session session=jsch.getSession(username, hostname, 22);
		session.setConfig("StrictHostKeyChecking", "no");
		if (password!=null)	session.setPassword(password);
		return session;
	}

	public String exec(String command){
		return exec(new String[] {command}).get(0);
	}
	public List<String> exec(List<String> commands){
		return exec(commands.toArray(new String[]{}));
	}


	public List<String> exec(String[] commands) {
		List<String> ret = new ArrayList<String>();
		try{
			Session session = getSession();
			session.connect();
			for (String command:commands){
				ChannelExec channelExec = (ChannelExec) session.openChannel("exec");
				channelExec.setPty(true);
				if (isDebugMode) System.out.println("command : "+command);
				channelExec.setCommand(command);
				InputStream inputStream = channelExec.getInputStream();
				//InputStream ext = channelExec.getExtInputStream();
				InputStream err = channelExec.getErrStream();
				channelExec.connect(3000);

				if (isDebugMode) System.out.print("stdout : ");
				String output="";
				byte[] buf = new byte[1024];
				int length;
				while ((length=inputStream.read(buf))!=-1){
					output+=new String(buf,0,length);
					if (isDebugMode) System.out.print(new String(buf,0,length));
				}
				if (isDebugMode) System.out.println("\nerr : "+IOUtils.toString(err));
				ret.add(StringUtils.chop(output));
				channelExec.disconnect();
			}
			session.disconnect();
		}catch(Exception e){
			e.printStackTrace();
		}
		return ret;
	}


	/* ---- generate ssh keypair  --------- */

	public Map<String,String> keyGen(String algorithm, String passphrase,String comment){
		String privateKeyString = "";
		String publicKeyString = "";

		int type=0;
		if(algorithm.toLowerCase().equals("rsa")) type=KeyPair.RSA;
		else if(algorithm.toLowerCase().equals("dsa"))type=KeyPair.DSA;
		else {
			System.err.println("does not support "+algorithm+" algorithm");
			return null;
		}

		JSch jsch=new JSch();
		try{
			KeyPair kpair=KeyPair.genKeyPair(jsch, type);
			kpair.setPassphrase(passphrase);
			ByteArrayOutputStream priout = new ByteArrayOutputStream();
			ByteArrayOutputStream pubout = new ByteArrayOutputStream();
			kpair.writePrivateKey(priout);
			kpair.writePublicKey(pubout, comment);

			privateKeyString = priout.toString();
			publicKeyString = pubout.toString();
			if (isDebugMode) {
				System.out.println("Private Key : \n"+privateKeyString);
				System.out.println("Public Key : \n"+publicKeyString);
				System.out.println("Finger print: "+kpair.getFingerPrint());
			}
			kpair.dispose();
		}
		catch(Exception e){
			e.printStackTrace();
			//System.out.println(e);
		}

		Map<String,String> map = new HashMap<String,String>();
		map.put("privateKey",privateKeyString);
		map.put("publicKey",publicKeyString);
		return map;
	}

	/* Scp ----------------------- */

	public String scpFrom(String rfile){
		String str ="";
		try{
			File lfile = File.createTempFile("temp", ".tmp");
			BufferedReader br = new BufferedReader(new FileReader(lfile));
			scpFrom(rfile,lfile);
			String line;
			while((line=br.readLine())!=null) str+=line+"\n";
			br.close();
			lfile.delete();
		}catch(IOException e){
			e.printStackTrace();
			return null;
		}
		return str;
	}
	public void scpFrom(String rfile,File lfile){
		//usage: java ScpFrom user@remotehost:file1 file2
		FileOutputStream fos=null;
		try{

			Session session = getSession();
			// username and password will be given via UserInfo interface.
			session.connect();

			// exec 'scp -f rfile' remotely
			String command="scp -f "+rfile;
			Channel channel=session.openChannel("exec");
			((ChannelExec)channel).setCommand(command);

			// get I/O streams for remote scp
			OutputStream out=channel.getOutputStream();
			InputStream in=channel.getInputStream();

			channel.connect();

			byte[] buf=new byte[1024];

			// invoke '\0'
			buf[0]=0; out.write(buf, 0, 1); out.flush();

			while(true){
				int c=checkAck(in);
				if(c!='C'){
					break;
				}

				// read '0644 '
				in.read(buf, 0, 5);

				long filesize=0L;
				while(true){
					if(in.read(buf, 0, 1)<0){
						// error
						break;
					}
					if(buf[0]==' ')break;
					filesize=filesize*10L+(long)(buf[0]-'0');
				}

				String file=null;
				for(int i=0;;i++){
					in.read(buf, i, 1);
					if(buf[i]==(byte)0x0a){
						file=new String(buf, 0, i);
						break;
					}
				}

				//System.out.println("filesize="+filesize+", file="+file);

				// invoke '\0'
				buf[0]=0; out.write(buf, 0, 1); out.flush();

				// read a content of lfile

				fos=new FileOutputStream(lfile);
				int foo;
				while(true){
					if(buf.length<filesize) foo=buf.length;
					else foo=(int)filesize;
					foo=in.read(buf, 0, foo);
					if(foo<0){
						// error
						break;
					}
					fos.write(buf, 0, foo);
					filesize-=foo;
					if(filesize==0L) break;
				}
				fos.close();
				fos=null;

				if(checkAck(in)!=0){
					return;
				}

				// invoke '\0'
				buf[0]=0; out.write(buf, 0, 1); out.flush();
			}

			session.disconnect();
		}
		catch(Exception e){
			System.out.println(e);
			try{if(fos!=null)fos.close();}catch(Exception ee){}
		}
	}

	public void scpTo(String content, String rfile){
		try{
			File tfile = File.createTempFile("prefix", ".tmp");
			FileWriter fw = new FileWriter(tfile);
			fw.write(content);
			fw.close();
			scpTo(tfile,rfile);
			tfile.delete();
		}catch(IOException e){
			e.printStackTrace();
			return;
		}
	}

	public void scpTo(File lfile, String rfile){
		// ScpTo file1 user@remotehost:file2

		FileInputStream fis=null;
		try{
			Session session = getSession();
			session.connect();

			boolean ptimestamp = true;

			// exec 'scp -t rfile' remotely
			String command="scp " + (ptimestamp ? "-p" :"") +" -t "+rfile;
			Channel channel=session.openChannel("exec");
			((ChannelExec)channel).setCommand(command);

			// get I/O streams for remote scp
			OutputStream out=channel.getOutputStream();
			InputStream in=channel.getInputStream();

			channel.connect();

			if(checkAck(in)!=0){
				//System.exit(0);
				return;
			}

			String filename = lfile.getName();
			if(ptimestamp){
				command="T "+(lfile.lastModified()/1000)+" 0";
				// The access time should be sent here,
				// but it is not accessible with JavaAPI ;-<
				command+=(" "+(lfile.lastModified()/1000)+" 0\n");
				out.write(command.getBytes()); out.flush();
				if(checkAck(in)!=0){
					//System.exit(0);
					return;
				}
			}

			// invoke "C0644 filesize filename", where filename should not include '/'
			long filesize=lfile.length();
			command="C0644 "+filesize+" ";
			if(filename.lastIndexOf('/')>0){
				command+=filename.substring(filename.lastIndexOf('/')+1);
			}
			else{
				command+=filename;
			}
			command+="\n";
			out.write(command.getBytes()); out.flush();
			if(checkAck(in)!=0){
				//System.exit(0);
				return;
			}

			// invoke a content of lfile
			fis=new FileInputStream(lfile);
			byte[] buf=new byte[1024];
			while(true){
				int len=fis.read(buf, 0, buf.length);
				if(len<=0) break;
				out.write(buf, 0, len); //out.flush();
			}
			fis.close();
			fis=null;
			// invoke '\0'
			buf[0]=0; out.write(buf, 0, 1); out.flush();
			if(checkAck(in)!=0){
				return;
			}
			out.close();

			channel.disconnect();
			session.disconnect();
		}
		catch(Exception e){
			System.out.println(e);
			try{if(fis!=null)fis.close();}catch(Exception ee){}
		}
	}

	private int checkAck(InputStream in) throws IOException{
		int b=in.read();
		// b may be 0 for success,
		//          1 for error,
		//          2 for fatal error,
		//          -1
		if(b==0) return b;
		if(b==-1) return b;

		if(b==1 || b==2){
			StringBuffer sb=new StringBuffer();
			int c;
			do {
				c=in.read();
				sb.append((char)c);
			}
			while(c!='\n');
			if(b==1){ // error
				System.out.print(sb.toString());
			}
			if(b==2){ // fatal error
				System.out.print(sb.toString());
			}
		}
		return b;
	}
}
Posted by 김민우 julingks

댓글을 달아 주세요

DDL Operation

하이브 테이블을 만들고 결과를 보여준다.

hive> CREATE TABLE pokes (foo INt, bar STRING);

두 개의 컬럼이 있는 pokes 테읍ㄹ을 생성한다. 첫 번째는 정수, 두 번째는 문자열이다.

hive> CREATE TABLE invites (foo INT, bar STRING) PARTITIONED BY (ds STRING);

두개의 컬럼을 가지고 한 개의 파티션 컬럼을 가지는 invites 테이블은 생성한다. 파티션 걸럼은 가상 컬럼이다. 이것은 데이타 자체의 일부분이 아니다. 특별한 데이터 셋이 로드되는 파티션으로 부터 유래한다.
디폴트로 테이블들은 text input format이고 구분자는 ^A(crt-a)라고 가정한다.

hive> SHOW TABLES;

테이블의 목록을 보여준다.

hive> SHOW TABLES ‘.*s’;

‘s’로 끝나는 테이블의 모든 목록을 보여준다. 이 패턴 매칭은 Java regular expressions을 따른다.

hive> DESCRIBE invites;

컬럼의 목록을 보여준다.

테이블 변경에서 테이블 이름은 변경될 수 있다. 그리고 추가 컬럼도 drop 될 수 있다.

hive> ALTER TABLE pokes ADD COLUMNS (new_col INT);
hive> ALTER TABLE invites ADD COLUMNS (new_col2 INT COMMENT ‘a comment’);
hive> ALTER TABLE events RENAME TO 3koobecaf;

테이블 drop

hive> DROP TABLE pokes;

 

Metadata Store

메타데이터는 javax.jdo.option.ConnectionURL 의 이름의 하이브 설정 변수에 의해 디스크 스토리의 위치 결정되는  임베디드 더비 데이터베이스이다.  초기 값은 ./metasore_db 이다.

메타스토어는  JPOX를 지원하는 어떤 데이타베이스에도 저장될 수 있다. 위치와 RDBMS의 타입은 javax.jdo.option.ConnectionURL 과 javax.jdo.option.ConnectionDriverName 두 변수에 의해서 조정된다.
지원하는 데이터베이스들의 좀 더 자세항 사항은 JDO(또는 JPOX) 문서를 참조해라 테이터베이스 스키마는  src/contrib/hive/metasore/src/model에 있는 JDO 메타데이터 어노테이션 파일 package.jdo 에 정의 되어 있다.

향후에는 메타스토어 자체가 stand-alone 서버가 될 것이다.

DML Operation

flat 파일의 데이타를 Hive로 로드한다.

hive> LOAD DATA LOCAL INPATH ‘./examples/files/kv1.txt’ OVERWRITE INTO TABLE pokes;

ctrl-a 로 나눠진 두 컬럼 포함하는 파일을 pkes 테이블로 로드한다.  ‘local’ 이라고 명시하는 것은 입력파일이 로컬 파일 시스템에 있다는 것이다. ‘local’을 빼면 HDFS 에 있는 파일을 찾는다. ‘overwrite’ 키워드는 테이블의 기존의 데이타는 삭제됨을 명시한다. ‘overwrite’ 키워드가 빠지면 데이터 파일은 기존 데이터 셋에 추가된다.

알아야 할 점

  • 로드 커맨드에 수행되는 스키마에 대한 데이터 검증(verification)은 없다
  • 만약 HDFS에 파일이 있다면 그것은 Hive-controlled 파일 시스템 네임스페이스로 이동한다.
    하이브 디렉토리의 루트는 hive-default.xml 파일에 hive.metastore.warehouse.dir 옵션에 의해 지정된다. 하이브에서 테이블을 생성하기 전에 이 디렉토리를 만들 것을 사용자에게 충고한다
hive> LOAD DATA LOCAL INPATH ‘./examples/files/kv2.txt’ OVERWRITE INTO TABLE invites PARTITION (ds=’2008-08-15)’;
hive> LOAD DATA LOCAL INPATH ‘./.examples/files/kv3.txt’ OVERWRiTE INTO TABLE invites PARTITION  (ds=’2008-08-08’);

위에 두 개의 LOAD 명령은 invites 테이블의 두 개의 다른 파티션으로 데이터를 로드한다. invites 테이블은 ds 키에 의해서 파티션되어 생성된다.

hive> LOAD DATA INPATH ‘/user/myname/kv2.txt’ OVERWRITE INTO TABLE invites PARTITION (ds=’2008-08-15)’;

위에 커맨드는 HDFS 파일/디렉토리로 부터 데이터를 읽어 table에 로드한다. HDFS로 부터 데이터를 로드하는 것의 결과는 파일/디렉토리로 이동되는 것이다. operation은 거의 즉시 수행된다.

SQL Operation

Example Queries

예제 쿼리는 build/dist/examples/queries에 있다. 더 많은 쿼리는 ql/src/test/queries/positive 에 있다

SELECTS and FILTERS

hive> SELECT a.foo FROM invites a WHERE a.ds=’2008-08-15’;

invites 테이블의 파티션 ds=2008-08-15의 모든 로우에서 foo 컬럼을 선택한다. 결과는 어디에도 저장되지 않는다. 그러나 콘솔 화면에 디스플레이 된다.
다음 예제들에서 INSERT(하이브 테이블이나, 로컬 디렉토리, HDFS 디렉토리)는 선택적이다.

hive> INSERT OVERWRITE DIRECTORY ‘/tm/hdfs_out’ SELECT a.* FROM invites a WHERE a.ds=’2008-08-15’;

쿼리의 결과를  HDFS 디렉토리에 저장한다. 결과 데이터는 디렉토리 파일 안에 있다. (mapper의 개수의 의존한다)
파티션된 테이블은 항상 WHERE 절에 파티션 선택해야 한다.

hive> INSERT OVERWRITE LOCAL DIRECTORY ‘/tmp/local_out’ SELECT a.* FROM pokes a;

pokes 테이블의 모든 로우를 선택하여 로컬 디렉토리로 로드한다.

hive> INSERT OVERWRITE TABLE events SELECT a.* FROM profiles a;
hive> INSERT OVERWRITE TABLE events SELECT a.* FROM profiles a WHERE a.key < 100;
hive> INSERT OVERWRITE LOCAL DIRECTORY '/tmp/reg_3' SELECT a.* FROM events a;
hive> INSERT OVERWRITE DIRECTORY '/tmp/reg_4' select a.invites, a.pokes FROM profiles a;
hive> INSERT OVERWRITE DIRECTORY '/tmp/reg_5' SELECT COUNT(*) FROM invites a WHERE a.ds='2008-08-15';
hive> INSERT OVERWRITE DIRECTORY '/tmp/reg_5' SELECT a.foo, a.bar FROM invites a;
hive> INSERT OVERWRITE LOCAL DIRECTORY '/tmp/sum' SELECT SUM(a.pc) FROM pc1 a;

컬럼의 합(SUM), 평균(arg), 최소값(min), 최대값(max)이 사용 될 수 있다.

GROUP BY

hive> FROM invites a INSERT OVERWRITE TABLE events ELECT a.bar, count**) WHERE a.foo > 0 GROUP BY a.bar
hive> INSERT OVERWRITE TABLE events SELECT a.bar, count(*) FROM invites a WHERE a.foo > 0 GROUP BY a.bar;

MULTITABLE INSERT

FROM src
INSERT OVERWRITE TABLE dest1 SELECT src.* WHERE src.key < 100
INSERT OVERWRITE TABLE dest2 SELECT src.key, src.value WHERE src.key >= 100 and src.key < 200
INSERT OVERWRITE TABLE dest3 PARTITION(ds='2008-04-08', hr='12') SELECT src.key WHERE src.key >= 200 and src.key < 300
INSERT OVERWRITE LOCAL DIRECTORY '/tmp/dest4.out' SELECT src.value WHERE src.key >= 300;

JOIN

hive> FROM pokes t1 JOIN ivites t2 ON (t1.bar=t2.bar) INSERT OVERWRITE TABLE evners SELECt t1.bar, t2.bar,t2.foo;

 

STREAMING

hive> FROM invites a INSERT OVERWRITE TABLE event SELECT TRANSFORM (a.foo, b.bar) AS (off,rb) USING ‘/bin/cat’ WHERE a.ds>’2008-0809

 

Reference

Posted by 김민우 julingks
TAG ddl, dml, Hadoop, hive, NoSQL, SQL

댓글을 달아 주세요

Hive는  하둡 위에서 돌아가는 데이터 웨어하우스 인프라 스트럭처다.   하둡에 저장된 데이터를 요약, adhoc  쿼리, 큰 데이터셋의 분석을 쉽게 만들어주는 도구를 제공한다.  Hive는 이 데이타 위의 스트럭처를 집어 넣는 매커니즘을 제공한다. 이 데이터에 쿼리 요청을 위해서 SQL과 친숙한 SQL의 기반의 Hive QL로 불리는 간단한 쿼리 언어를 제공한다. 동시에 이 언어는 내장 언어가 제공하지 않는 좀 더 정교한 분석을 하기 위해 전통적인 맵/리듀스 프로그래머가 커스텀 mapper와 reducer를 연결할 수 있게 해준다.

Installation and Configuration

Requirements

  • Java 1.6
  • Hadoop 0.17.x to 0.20.x.

Installing HIve a Stable Release

아파치 다운로드 미러에서 안정된 배포 버전을 다운 받는다.압축을 푼다.

$ tar -xzvf hive-x.y.z.tar.gz

HIVE_HOME환경변수를 설치한 디렉토리를 가리키도록 설정한다.

$ cd hive-x.y.z
$ export HIVE_HOME=`pwd`

마지막으로 $HIVE_HOME/bin 을 PATH에 추가한다

$ export PATH=$HIVE_HOME/bin:$PATH

Running Hive

하이브는 하둡을 사용한다는 것은

  • PATH에 하둡이 있어야 한다.
  • export HADOOP_HOME=<hadoop-install-dir>

추가로 /tmp 와 /user/hive/warehouse를 만들어야 한다. HDFS에서 chmod g+w 정해야 한다. 그래야 하이브가 테이블을 만들 수 있다.

$HADOOP_HOME/bin/hadoop fs -mkdir /tmp
$ $HADOOP_HOME/bin/hadoop fs -mkdir /user/hive/warehouse
$ $HADOOP_HOME/bin/hadoop fs -chmod g+w /tmp
$ $HADOOP_HOME/bin/hadoop fs -chmod g+w /user/hive/warehouse

하이브 커맨드 라인 인터페이스 사용한다

$ $HIVE_HOME/bin/hive

 

Configugration management Overview

하이브 default 설정은 <install-dir>/conf/hive-default.xml에 저장되어 있다.
설정 변수는 <install-dir>/conf/hive-site.xml 에서 정의된 것의 따라 변경될 수 있다.
하이브 설정 디렉토리의 위치는 HIVE_CONF_DIR 환경 변수 설정에 의해 변경될 수 있다.
Log4j 설정은 <install-dir>/conf/hive-log4j.properties에 저장되었다.
하이브 설정은 하둡 위에 덮어 쓴다. - 즉 하둡 설정 변수를 초기값으로 상속 받는다.
하이브 설정은 다음에 의해 조작된다.

  • hive-site.xml을 편집하기나 알맞는 변수(하둡 변수 포함)를 정의한다.
  • cli에서 set 커맨드를 사용한다.
  • 다음 syntax를 사용하여 하이브를 실행한다
    - $ bhin/hive -hiveconf x1=y1 -hiveconf x2=y2
    - HIVE_OPTS 환경변수를 위와 같이 “-hiveconf x1=y1 -hiveconf x2=y2” 로 지정한다

Runtime Configuration

  • Hive 퀄리는 map-reduce 쿼리들을 이용해서 실행된다. 따라서 그런 쿼리들은 하둡 설정 변수에 의해 control 된다.
  • cli 커맨드 ‘SET’ 은 하둡 (또는 하이브) 설정 변수를 지정하는데 사용될 수 있다. 예를 들면
hive> SET mapred.job.tracker=myhost.mycompany.com:50030;
hive> SET -v;

-v 는 현재의 모든 설정들을 보여준다. -v 옵션이 없다면 오직 기본 하둡 설정과 다른 변수만 표시된다

Hive, Map-Reduce and Local-Mode

Hive 컴파일러는 대부분의 쿼리들의 map-reduce job들을 생성한다. 이 job들은 다음 변수가 가리키는 Map-Reduce 클러스터에 제출된다.

mapred.job.tracker

보통 map-reduce 클러스터는 여러 개의 노드를 가리키지 않는 반면에, 하둡 또한 유저 워크스테이션 위에서 map-reduce job들을 로컬에서 실행하기 위한 멋진 옵션을 제공한다. 이 것은 작은 데이터 셋에 쿼리를 실행할 때 매우 유용할 수 있다. 이 경우에 로컬 모드 실행은 보통 대형 클러스터에 job을 제출하는 것 보다 상당히 빠르다. 데이터는 HDFS에서 투명하게 접근할 수 있다. 반대로 로컬 모드는 오직 하나의 reducer 만 실행하며 큰 데이터 셋 처리에서는 매우 느려진다.

0.7버전부터 하이브는 로컬 모드 실행을 완전히 지원한다. 이것을 가능하게 하기 위해 사용자는 다음 옵션을 지정해야 한다.

hive> SET mapred.job.tracker=local;

추가로, mapred.local.dir 로컬 머신 위에 유요한 경로를 가리켜야 합니다. (예를 들면 /tmp/<username>/mapred/local) (그러지 않으면, 사용자는 로컬 디스크 스페이스를 할당 받으라는 예외를 얻게 된다.)

0.7 버전부터, 하이브는 또한 자동적으로 로컬 모드에서 map-reduce job들을 실행하기 위한 모드를 제공한다. 관련된 옵션은 다음과 같습니다.

hive> SET hive.exec.mode.local.auto=false;

이 특징은 초기 값이 false임을 명심하자. 만약 ture라면, 하이브는  쿼리에서 각각 map-reduce job의 크기를 분석한다.
다음 thresholds가 만족된다면 로컬에서 실행이 될 수도 있다.

  • job의 총 입력 크기가 hive.exec.mode.local.auto.inputbytes.max  (초기값 128MB) 보다 작을 때
  • 총 map-task의 개수가 havie.exec.mode.local.auto.tasks.max( 초기값 4)
  • 총 필요한 reduce-task의 개수가 1 또는 0

따라서 작은 데이터 셋의 쿼리와 다음 job들의 입력이 실질적으로 더 작을 때 여러 개 map-reduce job들 위한 쿼리에서 job들은 로컬에서 실행될 수 있다.

하둡 서버 노드들의 runtime 환경과 하이브 클라이언트를 실행하는 머신의 차이점이 있을 수 있다는 것을 명심해라. (vm 버전이나 다른 소프트웨어 라이브러리 때문에) 이는 로컬 모드에서 실행 중 예기치 못한 행동이나 에러를 유발할 수 잇다. 또한 로컬 모든 실행은 child jvm(하이브 클라이언트의)으로 분리되어 실행된다. 사용자가 정말 원한다면, child jvm을 위한 최대 메모리 양을 hive.mapred.local.mem. 옵션을 통해서 조정할 수 있다. 초기값은 0 이다. 이 경우에 하이브는 하둡이 child jvm의 초기 메모리 한계를 정하도록 한다.

Error Logs

하이브는 로깅을 위해 log4j를 사용한다.  디폴트 로그들은 CLI에 의해 콘솔로 방출되지 않는다. 디폴트 로깅 레벨은 WARN 이다. 로그는 다음 폴더에 저장된다.

  • /tmp/{user.name}/hive.log

사용자가 원한다면 밑에 보여지는 arguments를 추가하여 콘솔로 방출하게 할 수 있다.

  • bin/hive -hiveconf hive.root.logger=INFO,console

사용자는 오직 다음을 이용해서 로깅 레벨을 변경할 수 있다.

  • bin/hive -hiveconf hive.root.logger=INFO,DRFA

set 커맨드를 이용해서 hive.root.logger를 정하는 것은 로깅의 property를 변경하지 않는다. property는 초기화 시간에 결정된다.

하둡 클러스터에서 하이브 실행 중 로깅은 하둡 설정에 의해 통제된다. 보통 하둡은 테스크가 실행되었을 때 클러스터 머신에 저장된 맵과 리듀스 테스크 당 하나의 로그파일을 생성한다, 로그 파일은 하둡의 JobTracker 웹 UI에서 Task Details 페이지를 클릭 해서 얻을 수 있다.

로컬 모드를 사용할 때, 하둡/하이브 실행 로그는 클라이언트 머신 자체에서 생성된다. 0.6 버전부터는 하이브는 디폴트로 로그가 어디에 전달될지를 결정하기 위해 hive-exec-log4j.properties 를 사용한다. 디폴트 설정은 로컬 모드에서 실행된 쿼리 당 하나의 로그 파일이 생성된다. 그리고 이것은 /tmp/{user.name} 밑에 저장된다. 설정파일을 분리하여 생성하는 의도는 관리자가 만약 바람직하다면 실행 로그 캡쳐를 집중화 할 수 있도록 하기 위함이다.  실행 로그는 runt-time 에러를 디버깅 하는 데는 가치가 없다.

Reference


Posted by 김민우 julingks

댓글을 달아 주세요

Bigtable

Hadoop 2011. 2. 14. 13:02

  빅테이블은 구글에서 개발한 분산 데이터 관리 시스템으로, 수백 내지 수천 대의 값싼 하드웨어 장비를 이용해 페타 바이트 이상의 구조화된 데잍터를 저장할 수 있다. 빅케이블은 범용성, 확장성, 고성능, 고가용성의 목표를 가지고 만들어진 시스템이다.

데이터 모델은 분산돼 있는 다차원으로 정렬된 맵이다. 모든 데이터는 row key. column key, timestamp로 정렬돼 있으며, value에는 바이트 배열을 저장할 수 있다. 데이터 모델을 구성하는 주요 엘리먼트는 row, column family, timestamp 등이 있다

하나의 테이블에 저장된 데이터는 row key로 유일하게 식별된다. 읽기/쓰기 연산은 하나의 열 단위로 원자적으로 처리된다. 빅ㅌ이블은 하나의 아주 큰 테이블을 row key의 영역을 이용해 파티셔닝하며, 파티셔닝된 단위를 테블릿tablet이라고 부른다. 하나의 테블릿은 특정 서버에 의해 서비스되며, 하나의 서버는 수 천개의 테블릿을 서비스 한다.

파티셔닝 범위, 서비스 서버 등과 같은 파티셔닝에 대한 정보는 하나의 루트 테블릿과 다수의 메타 테블릿에 저장된다. 루트 테블릿을 서비스하는 서버의 정보는 zookeeper 와 같은 역할을 하는 chuby (distributed lock service)에 저장되며, 루트 테블릿에서 하나의 열에는 하나의 메타 tablet에 대한 정보 (tablet 이름, 최대 row key, 서버 정보)를 저장한다. 메타 테블릿에서 하나의 열에는 사용자 테이블에 있는 하나의 테블릿에 대한 정보를 저장한다. 특정 row key를 서비스하는 사용자 테이블의 테블릿과 테블릿 서버를 찾기 위해 chuby -> 루트 테블릿 - > 메타 테블릿 순으로 찾는다.

하나의 빅테이블 클러스터는 하나의 마스터 서버와 다수의 테블릿 서버로 구성된다. 마스터 서버는 마타 정보 같이 클러스터 관리에 필요한 정보를 갖고 있지 않기 때문에 마스터 장애 시에도 데이터 서비스는 영향을 받지 않는다. 마서터 서버는 테블릿을 할당하거나, 테블릿 서버가 클러스터에 추가/제거되는 것을 감지하고, 테블릿 서버의 부하 분산과 구글 파일 시스템에 저장된 파일에 대한 가비지 컬렉션을 수행한다. 테블릿 서버는 테블릿을 관리하고 클라이언트로부터 데이터 읽기/쓰기 요청을 받아 처리한다. 하나의 테블릿 서버는 수천 개까지 ㅌ에블릿을 서비스하고, 하나 테블릿 크기는 100~200MB이다.

빅테이블은 구글 파일 시스템, 맵리듀스(MapReduce), 처비(chuby) 등과 같은 구글 내부의 여러 분산 플랫폼을 이용한다. 빅테이블은 구글 파일 시스템을 데이터 파일이나 커밋 로그 저장용으로 사용한다. 구글 파일 시스템에서 하나의 파일은 3개의 복제본을 갖고 있기 때문에 추가적인 백업이 필요 없으며, 수천 노드 이상으로 확장할 수 있는 확장성과 복제본 간의 정합성을 제공한다.

구글 파일 시스템은 하둡 파일 시스템의 기본 설계를 제공한 파일 시스템이다. 구글 파일 시스템 역시 파일의 랜덤쓰기 기능을 제공하지 않기 때문에 이미 저장된 파일을 수정하는 것이 불가능하다. 파일 시스템의 이런 제약 때문에 빅테이블은 메모리 기반In-Memonry, 디스크기반On-Disk 데이터 관리 시스템의 속성을 모두 갖고 있다. 빅테이블의 쓰기 연산은 데이터 파일을 직접 수정하지 않고 메모리에만 쓰기 연산의 내용을 기록한다. 메모리가 일정 크기에 도달하면 메모리의 내용을 파일 시스템으로 저장한다. 쓰기 연산을 메모리에만 저장하기 때문에 테블릿 서버에 장애가 발생한 경우 데이터 복구를 위해 모든 쓰기 연산 처리시 구글 파일 시스템에 커밋 로그를 저장한 후 메모리에 저장한다.

빅테이블에 저장된 데이터에 대해 대규모의 분석 작업이 필요한 경우 맵리듀스 플랫폼을 이용하며, 분산 처리되는 단위는 하나의 테블릿이다. 처비는 분산 락 서비스를 제공하는 시스템으로, 빅테이블에서는 루트 메타 정보를 서비스하는 테블릿 서버의 위치 정보, 테이블의 스키마 정보 등을 저장하거나 여러 마스터 서버가 동시에 실행 중일 때 유요한 마스터 서버를 선출하거나, 테블릿 서버의 장애 상황을 발생을 감지하는 등에 사용한다. 하나의 처비 셀은 5개의 노드로 구성되며, 노드 간의 모든 정보는 동기화돼 마스터 정보에 대한 SPOF(Single Point Of Failure) 지점을 없애는 역할을 수행한다. 빅테이블은 구글 파일 시스템을 이용함으로써 데이터의 정합성은 보장하지만 네트워크 단절 상황에서 가용성을 지원하지 않는다.

빅테이블은 웹 페이지 인덱싱, Google Earth, Google Finance, Google Analytics, personalized Search 등 이미 구글의 많은 서비스에 적용 중이다.

아파치 같은 오픈소스 진영이나 인터넷 서비스 업체들이 빅테이블의 개념을 도입한 시스템을 발표하고 있으며, 이들 대부분은 공개 소프트웨어다.

Source

Related Links

Posted by 김민우 julingks

댓글을 달아 주세요

MySql 쿼리 결과 출력하기

import java.sql.*;
import java.util.ArrayList;

public class JdbcTest {

	private String URL = "jdbc:mysql://localhost";
	private String USERNAME = "user";
	private String PASSWORD = "password";
	private Connection conn;
	public JdbcTest(){
		openConnection();
	}

	public void openConnection(){
		try{
			Class.forName("com.mysql.jdbc.Driver").newInstance();
			conn=DriverManager.getConnection(URL,USERNAME,PASSWORD);
			if(!conn.isClosed())
				System.out.println("Successfully connected to "+ "MySQL server using TCP/IP...");
		}catch(Exception e){
			System.err.println("Exception : "+e.getMessage());
		}
	}

	public void close(){
		try{
			if(conn!=null) conn.close();
		}catch(SQLException e){}
	}

	private void writeResultSet(ResultSet resultSet) throws SQLException {
		int columnCount = resultSet.getMetaData().getColumnCount();
		if (columnCount<1) return;
		System.out.println("--------------------------------------");
		ArrayList <String> labels = new ArrayList<String>();
		for  (int i = 1; i<= columnCount; i++){
			String label = resultSet.getMetaData().getColumnLabel(i);
			System.out.print(label+"\t");
			labels.add(label);
		}
		System.out.println("\n--------------------------------------");
		while(resultSet.next()){
			for(String label:labels)
				System.out.print(resultSet.getString(label)+"\t");
			System.out.println();
		}
		System.out.println("--------------------------------------\n");
	}

	private void executeQuery(String query){
		System.out.println("Execute Query : "+query);
		try{
			Statement st=conn.createStatement();
			ResultSet resultSet = st.executeQuery(query);
			writeResultSet(resultSet);
			resultSet.close();
			st.close();
		}catch(Exception e){
			System.out.println("Exception : "+e.getMessage());
		}
	}
	public static void main(String [] args){
		JdbcTest db = new JdbcTest();
		db.executeQuery("show databases;");
		db.executeQuery("use mysql");
		db.executeQuery("show tables;");
		db.executeQuery("select * from user;");
		db.close();
	}
}
Posted by 김민우 julingks
TAG db, java, mysql

댓글을 달아 주세요

JDBC를 이용하여 MySQL 서버에 접속하기
import java.sql.*;

public class JdbcTest {
	public static void main(String [] args){
		Connection conn= null;
		
		try{
			Class.forName("com.mysql.jdbc.Driver").newInstance();
			conn=DriverManager.getConnection("jdbc:mysql:/dbserver","user","password");
			if(!conn.isClosed())
				System.out.println("Successfully connected to "+ "MySQL server using TCP/IP...");
		}catch(Exception e){
			System.err.println("Exception : "+e.getMessage());
		} finally{
			try{
				if(conn!=null) conn.close();
			}catch(SQLException e){}
		}
	}

}
Posted by 김민우 julingks

댓글을 달아 주세요

MySQL 설치 후 root 비밀번호를 설정해야 된다.
우선 비번없이 mysql client로 접속할 수 있다.

mysql> use mysql;

mysql> update user set Password = password('비밀번호') where User = 'root';
Query OK, 3 row affected (0.00 sec)

여기서 주의해야 할 것은 컬럼의 이름이 Password, User가 소문자일 수도 있다. (또는 user 테이블이 User일 수 있다)
  • show tables; 
  • desc 테이블_이름;
위 두 명령을 이용해서 테이블 이름과 컬럼 이름을 직접 확인한 후에 root 비번 바꾸는 쿼리를 실행하도록 하자.
쿼리를 실행 후에는 mysql 을 재시작하거나 다음 명령어를 실행하여 권한을 적용한다.
mysql> flush privileges;

Posted by 김민우 julingks

댓글을 달아 주세요

vsftpd는 UNIX-like 시스템에서 GPL 라이센스 FTP 서버다. 안전하고 매우 빠르고 Stable하다.

Features
  • Virtual IP configurations
  • Virtual users
  • Standalone or inetd operation
  • Powerful per-user configurability
  • Bandwidth throttling
  • Per-source-IP configurability
  • Per-source-IP limits
  • IPv6
  • Encryption support through SSL integration
  • etc.

1. 설치
# apt-get install vsftpd

2. 재시작
# /etc/init.d/vsftpd restart

3. 설정
# vim /etc/vsftpd.conf

4. 서버 작동 확인 (21번 포트가 떠있는지 확인한다.)
# netstat -ntl
...
tcp      0      0      0.0.0.0:21      0.0.0.0:*      LISTEN
...

Posted by 김민우 julingks

댓글을 달아 주세요

 영속성 메커니즘과 상호작용할 때는 영속성(persistence)을 반영하는 객체의 상태와 생명주기에 애플리케이션이 직접 관여할 필요가 있다. 객체가 활동하는 중에 객체의 상태가 변하는 것을 영속성 생명 주기(Persistence Lifecycle)라 한다. 

 영속성 생명주기의 다양한 상태와 상태 전이를 정의하는 용어는 각 ORM 솔루션마다 다르다. 게다가 내부적으로 사용하는 객체 상태는 클라이언트 애플리케이션으로 노출하는 상태와 다를수도 있다. 하이버네이트는 단지 네 가지 상태만을 정의해서 클라이언트 코드로부터 내부 구현의 복잡성을 감춘다.
 다음은 하이버네이트(Hibernate)에서 정의한 객체 상태와 각 상태에 사이의 전이에 대한 그림이다.



비영속 객체(Transient Object)
 new 연산자를 사용해 생성한 객체는 바로 영속화 되지 않는데, 이러한 상태를 비영속(transient) 상태라 한다. 
비영속 상태는 어떤 데이터베이스 테이블 행과도 연관 관계를 맺지 않으므로 이 객체를 참조하는 다른 객체가 없다면 바로 상태를 잃게 된다. 이 객체는 실질적으로 이 시점에 수명이 다해 더는 접근할 수 없으므로 가비지 컬렉션의 대상이된다. 자바 퍼시스턴스엣서는 이 상태를 표현하는 용어가 없으며 단지 새로 생상한 엔티티 객체일 뿐이다. 
 비영속 상태에서 영속성 컨텍스트가 관리하는 영속 상태로 바뀌려면 영속성 관리자를 호출하거나 이미 영속 상태인 인스턴스에서 참조를 생성해야 한다.

영속 객체(Persistent Object)
 영속 인스턴스는 데이터베이스 동일성을 지닌 엔티티 인스턴스를 말한다. 이는 영속 인스턴스와 관리형 인스턴스는 데이터 베이스 식별자로 설정된 주키 값을 가지고 있음을 의미한다.
 영속 인스턴스는 애플리케이션에 객체를 생성한 후 영속성 관리자의 메서드 호출을 통해 영속화했을 수도 있고, 또는 이미 관리하고 있는 또 다른 영속 객체에서 자신에 대한 참조를 생성했을 때 영속화됐을 수도 있다. 아니면 쿼리를 실행하거나, 식별자를 기준으로 룩업하거나, 또는 또 다른 영속 인스턴스에서 시작한 객체 그래프를 탐색해서 가져온 인스턴스일지도 모른다.
 영속 인스턴스는 항상 영속성 컨텍스트와 연관 관계를 맺는다. 하이버네이트는 영속 인스턴스를 캐시에 저장하고, 애플리케이션 영속 인스턴스를 수정했는지 알아낼 수 있다.
 
삭제 객체(Removed Object)
 엔티티 인스턴스를 삭제하는 방법은 여러가지다. 예를 들어 영속성 관리자의 명시적인 연산으로 인스턴스를 삭제할 수 있다. 또한 인스턴스에 대한 모든 참조가 제거되면 삭제될 수 있는데, 이 기능은 하이버네이트와 하이버네이트 확장을 설정한 자바 퍼시스턴스에서만 이용할 수 있다.
 작업 단위가 종료되는 시점에 객체가 삭제될 예정이라면 객체는 삭제 상태이긴 하지만 작업 단위가 완료될 때까지는 계속해서 영속성 컨텍스트에 의해 관리된다. 삭제 상태의 객체는 작업 단위가 완료되자마자 데이터베이스에서 삭제되므로 재사용해서는 안 된다.

준영속 객체(Detached Object)
 애플리케이션에서 바로 생성한 객체는 비영속 상태가 된다. 이제 영속성 관리자의 연산을 호출해서 인스턴스를 영속화한다. 이 모든 행동은 단일 작업 단위에서 일어나며, 해당 작업 단위의 영속성 컨텍스트를 특정 시점에 데이터베이스와 동기화 한다. 
 작업 단위를 완료했고 영속성 컨텍스트가 닫혔다. 그러나 애플리케이션은 여전히 저장한 인스턴스를 참조한다. 이 같은 상태에 있는 객체를 준영속 상태에 있다고 하며 더는 데이터베이스와의 동기화를 보장하지 않기 때문에 이제 이 인스턴스는 영속석 컨텍스트에 포함되지 않는다. 준영속 객체는 영속화 시점의 데이터를 유지한다. 준영속 객체를 계속 사용하고 수정할 수 있지만, 언젠가는 변경한 내용을 영속화하고 싶을 것이다. 하이버네이트는 이 같은 상황을 다루기 위해 재진입(reattachment)과 병합(merging)이라는 두가지 연산을 제공한다. 
 

Source :
  • Hibernate 완벽 가이드 (Java persistence with Hibernate) 크리스찬 바우어, 개빈 킹 지음 / 박찬욱, 백기선, 이대엽 옮김
Posted by 김민우 julingks

댓글을 달아 주세요

DevMode를 실행할 때 옵션들

Google Web Toolkit 2.0.3
DevMode [-noserver] [-port port-number | "auto"] [-whitelist whitelist-string] [
-blacklist blacklist-string] [-logdir directory] [-logLevel level] [-gen dir] [-
bindAddress host-name-or-address] [-codeServerPort port-number | "auto"] [-serve
r servletContainerLauncher[:args]] [-startupUrl url] [-war dir] [-extra dir] [-w
orkDir dir] module[s]

where
  -noserver        Prevents the embedded web server from running
  -port            Specifies the TCP port for the embedded web server (defaults
to 8888)
  -whitelist       Allows the user to browse URLs that match the specified regex
es (comma or space separated)
  -blacklist       Prevents the user browsing URLs that match the specified rege
xes (comma or space separated)
  -logdir          Logs to a file in the given directory, as well as graphically

  -logLevel        The level of logging detail: ERROR, WARN, INFO, TRACE, DEBUG,
 SPAM, or ALL
  -gen             Debugging: causes normally-transient generated types to be sa
ved in the specified directory
  -bindAddress     Specifies the bind address for the code server and web server
 (defaults to 127.0.0.1)
  -codeServerPort  Specifies the TCP port for the code server (defaults to 9997)

  -server          Specify a different embedded web server to run (must implemen
t ServletContainerLauncher)
  -startupUrl      Automatically launches the specified URL
  -war             The directory into which deployable output files will be writ
ten (defaults to 'war')
  -extra           The directory into which extra files, not intended for deploy
ment, will be written
  -workDir         The compiler's working directory for internal use (must be wr
iteable; defaults to a system temp dir)
and
  module[s]        Specifies the name(s) of the module(s) to host


Posted by 김민우 julingks

댓글을 달아 주세요

간단한 자바 컴파일 빌드 스크립트이다.

build.xml

  
  

  
    
  

  
    
    
      
    
    
      
    
   


Posted by 김민우 julingks

댓글을 달아 주세요

데이브 후버의 Apprenticeship Patterns의 번역서가 나왔다. '프로그래머의 길 멘토에게 묻다' (강중빈 옮김)

제 2장, 잔을 비워라. 요약
  • Your First Language
  • The White Belt 
  • Unleash Your Enthusiasm 
  • Expose Your Ignorance
  • Confront Your Ignorance
  • The Deep End
  • Retreat into Competence
Your First Language (첫번째 언어)
상황
 당신은 완전히 새내기이며 한두 가지 프로그래밍 언어를 대략만 아는 정도다.

문제
 어떤 특정한 프로그래밍 언어를 써서 팀의 다른 동료들과 동일한 품질 수준으로 결과물을 낼 수 있느냐에 내 일자리가 달려 있는 것 같다. 또는, 첫 일 자리를 얻을 수 있는지 여부가 특정한 프로그래밍 언어에 얼마나 능숙하느냐에 달려 있는 상황이다.

해결책
 언어를 하나 선택하고, 그 언어에 능숙해져라. 이 언어가 앞오로 몇 년 동안 당신이 문제를 해결할 때 쓸 주력 언어이며 실제로 쓰면서 연마하는 기본 기술이 될 것이다. 이 선택이 쉽지만은 않으며 여러 가지를 주의 깊게 고려해야한다. 이제 이 언어를 기초로 삼아 당신의 초반 경력이 쌓일 것이기 때문이다. 

실천 방안
 자신이 선택한 언어의 명세를 찾아서 읽어 보라. 
 자신이 쓰는 언어의 표준 라이브러리가 오픈 소스 형태라면, 소스를 이용 하라 패턴에 설명된 기법을 써서 그 소스를 통독해 보라. 소스를 보다가 버그를 발견했다면, 수정 패치를 만들어서 제작자에게 보내 보라.
 언어에 대한 지식을 쌓을 수 있는 또 다른 방법은, 같이 일하는 사람들에게 첫 번째 언어를 어떻게 선택했는지 묻는 것이다. 민츠 기법, 슈바를츠 변환, 더프 디바이스 같은 관용적인 기법에 대해서 좀 더 알아보아도 좋을 것이다.

The White Belt (흰 띠를 매라)
상황
당신은 첫 번째 언어를 심도 있게 이해하게 되었고 어느 정도의 수준에 오른 것 같아 다소 느긋해졌다. 동료들은 당신의 능력을 인정하고 있으며, 당신의 전문 분야쪽 문제가 생기면 도와달라고 요청하는 정도가 되었다. 당신은 자기 역량에 대해 자부심을 가지고 있다.

문제
 새로운 것을 배우려고 애를 써보지만, 이전에 비해서 새 기술을 익히는 일이 왠지 더 힘들어진 것 같다. 최선을 다해 노력해 보아도 자기 학습의 속도는 점점 더뎌지는 듯하다. 당신은 자기 개발이 교착 상태에 빠진 것이 아닌가 두려워 진다.

해결책
 새로운 상황에 들어설 때는, 학습을 통해 얻은 자신감은 그대로 두면서 이전에 얻은 지식은 한편으로 밀어두어라. 흰 띠를 맨다는 것은, 검은 띠라면 방법을 알지만 흰 띠는 배우는 것 말고 다른 선택이 없다는 깨달음에 근거를 둔 행위다. 
 새로운 기술 분야를 배울 때 '알지 못함'의 태도를 취하는 접근법을 취하면 학습 과정이 엄청나게 가속화 된다. 습관적인 프로그래밍 방식을 잠시 보류하도록 자신을 훈련 시키면 새로운 가능성이 발견된다. 하지만 높은 수준의 전문 역량을 성취하여 마침내 자부심을 느끼는 한 사람의 프로그래머로서, 알지못하는 영역으로 발을 들여놓고 스스로 어리석게 보여야 한다는 것은 고통스러운 일이 될 수 있다. 
 하지만, 새로운 지식을 받아들이려면 우리는 우선 과거의 경험과 선입견을 한 켠으로 밀어 둘 수 있어야 한다.
 새로운 언어나 도구, 비즈니스 분야를 배울때 관ㄴ용적인 표현을 배우는 데 열려 있으면 기존 커뮤니티와 소통하기 수월해진다. 새로운 지식과 오래된 지식을 잘 융화시켰을 때, 양쪽 분야 모두로부터 생상적인 통찰력을 이끌어 낼 수 있을 것이다.

실천 방안
 배운 것을 잊어버릴 수 이는 기회를 찾아보라. 특정 패러다임으로 작성했던 프로그램을 하나 골라서 다른 패러다임에 속한 언어로 다시 구현해 보라.

Unleash Your Enthusiasm (열정을 드러내라)
상황
당신은 소프트웨어 개발이라는 기예에 대한 만족을 모르는 열정과 호기심을 지녔다.

문제
당신이 동료들에 비해서 얼마나 더 큰 열정을 지녔는지 의식하면서, 스스로 열정을 숨기고 지내게 되었다.

해결책
 미숙함에도 불구하고 당신은 '전염되는 의욕'같은 독특한 속성을 가지고 온다. 기예를 향한 당신의 열정을 어느 누구도 꺽지 못하게 하라. 그것은 소중한 것이며 당신의 학습을 촉진시킬 것이다. 
 조직이란 그 구성이 어떠하든 간에 평균 수준으로 회귀하려는 경향이 있다. 보통 팀은 진행 중인 프로젝트를 완료해서 납품하거나 개발 주기에서 그들을 괴롭히는 면을 개선하는 일에 관심이 집중되어 있다. 
학습 능력보다 실무적인 역량을 더 중요시하는 사람들이라면 당신은 별로 좋은 인사을 주지 못한다. 만약 팀이 당신의 열정을 받아주지 못한다고 생각되면, 당신의 열정을 키워 나갈 방법을 달리 찾을 필요가 있다.
 하지만 견습생들의 열의와 기여에 대해 열림 팀에서라면, 자유로운 상상력이나 열정 같은, 경험이 더 많은 개발자들이 개대하는 독특한 기질을 팀에 가져올 수 있다. 
 연구자들은 경험 수준이 다양한 사람들이 구성된 팀이 실제로 더 건강하다는 사실을 발견했다. 궁극적으로, 열정을 드러내는 것이야말로 견습생이 맡아야 하는 몇 안되는 책무 중 하나다. 심도 있는 지식이나 엄청난 생산성으로 기여하지 못하더라도, 팀에 열정을 붙어 넣는 것이야 말로 당신의 본문이라 할 수 있다.

실천 방안
 어떤 아이디어가 있었지만 실제로 제안하지는 않았던 가장 최근의 기억을 떠올려 보라. 제안할 대상으로 생각한 사람을 찾아가서 그 아이디어를 설명하라. 그 사람이 미흡한 점을 지적한다면, 그런 점을 개선할 수 있게 도와달라고 설득해 보라.

Concrete Skills (구체적인 기술)
상황
 당신은 현재보다 더 나은 학습 기회를 얻고자 재능 있는 장인들이 모인 팀에 들어가서 할 수 있는 역할을 찾고 있다.

문제
 유감스럽게도 그 팀은 업무에 직접적인 도움이 되지 않을 사람을 고용하는 위험을 감수하고 싶어 하지 않는다. 더구나 그 팀에서는 단순 작업을 자동화 했다든지 해서, 당신이 간접적으로 기여하는 것마저도 여의치 않을 수 있다.

해결책
 구체적인 기술을 습득해서 유지하라. 견습생은 그 열정만으로도 팀 내에 빨리 배우는 능력을 가져다 주겠지만, 만약 당신이 특정한 도구와 기술 분야에 대해 뚜렷하고 입증할만한 역량을 지녔다면, 일정 수준으로 성장할 때까지 팀에 간접 적으로 기여할 수 있으리라는 신뢰를 얻기가 더 쉬울 것이다.
 구체적인 기술의 예를 들자면, 하이버네이트, 스트럿츠 같이 잘 알려진 오픈소스 프레임워크에 대한 지식, 기초적인 웹 디자인, 자바스크립트, 당신이 선택한 언어의 표준 라이브러리 등이 있을 것이다.

실천방안
 당신이 우러러보는 역량을 가진 사람들의 이력서를 모아 보라. 각 사람들의 이력서에서 다섯가지 정도 대표적인 력량을 뽑아 보고, 그중에서 어떤 역량이 지금 들어가고 싶어 하는 팀과 유사한 환경에서 바로 쓸모가 있을지 판단해 보라. 그런 기술을 습득했음을 보일 수 있는 토이 프로젝트에 대한 계획을 세우라. 그리고 그 계획을 실행하라.

Expose Your Ignorance (무지를 드러내라)
상황
 당신을 소프트웨어 개발자로 채용한 사람들은, 자신이 하는 일이 무엇인지 잘 알고 있을 거라고 믿고 있다.

문제
 관리자나 팀의 사람들은 당신이 잘 해 낼 거라는 확신을 갖기 원하지만, 실제로 당신은 몇몇 필수적인 기술에 대해 그다지 익숙하지 않다. 이런 일은 컨설턴트 뿐 아니라 누구에게 일어날 수 있다. 당신이 팀에 합류하게 된 이유는 아마도 해당 비지니스 영역이나 팀이 사용하는 기술 분야를 깊이 이해하고 있었기 때문일 것이다. 아니면 그 일을 할 사람이 당신밖에 없었을 지도 모른다.

해결책
 당신을 믿고 있는 사람들에게 학습 과정도 소프트웨어 납품의 일부분임을 보여주어라. 그들에게 당신이 성장하는 모습을 보여주어라. 
 개발자라면 유능할거라는 기대도 점점 커져 간다. 그렇지만 당신은 아직 미숙하기 때문에 모르는 영역이 상당히 많다. 
 소프트웨어 장인은 고객이나 동료와 맺은 튼튼한 관계를 통해서 자기 평판을 쌓아 간다. 사람들에게 진실을 말하라. 그들이 무엇을 원하는지 이제 당신이 이해하기 시작했고, 그것을 해낼 방법을 배워가는 중이라고 알려주어라. 아는 척 하기보다는 당신이 얼마나 잘 배울 수 있는지를 가지고 안심시켜라. 이렇게 해서 당신의 평판은 어떤 지식을 알고 있느냐가 아니라 학습하는 능력이 얼마나 좋은지를 기반으로 쌓여갈 것이다.

실천 방안
 업무에 관해서 정말로 이해되지 않는 것 다섯 가지를 적어 보라. 그 목록을 다른 사람들이 볼 수 있는 곳에다 붙여 두어라. 그리고 당신의 업무가 바뀔 때마다 그 목록을 갱신하는 습관을 들여라.

Confront Your Ignorance (무지에 맞서라)
상황
 당신이 보유한 기술 목록에 일상 업무와 관련이 있는 기술이 빠져 있음을 알게 되었다.

문제
 숙달해야 할 도구나 기법들이 있기는 하지만 어떻게 시작해야 좋을지 모르겠다. 그 중 몇 가지는 이미 모두가 다 알고 있는 듯 하고, 다른 살마은 당신도 당연히 알거라 기대하는 것 같다.

해결책
 도구나 기법을 하고 고른 다음에 그것과 관련된 지식의 빈틈을 능동적으로 메워라. 
 이렇게 할 때는 자신에게 자장 효과적인 방법을 택해라. 
 어떤 방법이 당신에게 맞든 간에, 주위의 마음 맞는 사람들과 멘토들에게 혹시 그 기술을 이미 보유하고 있는지, 또 배운 것을 공유해 줄 수 있는지 물어보기를 잊지 마라. 때로는 다른 사람들 역시 그 기술을 익히려 할 것이며, 그들과 같이 작업하면서 더 나은 진전을 볼 수 있을 것이다. 
 
실천 방안
 무지를 드러내라 패턴의 실천 방안에서 언급된 항목들 각각에 대해서 학습 하도록 노력하고, 학습이 진행된 다음에는 목록에서 하나씩 지워 나가라. 이렇게 해서 얻은 새 지식은 그 전까지 알아채지 못했던 빈틈을 드러낼 수도 있다. 그 빈틈을 자신의 목록에 잊지말고 추가하여라.

The Deep End (깊은 쪽)
상황
 당신은 조금씩 안전하게 걸음을 옮겨 가는 것이 불만족스럽다. 당신은 안정된 상태가 아니라 판에 박힌 관습에 빠져 있을지 모른다는 두려움이 들기 시작한다. 안정된 상태라면 더 높은 단계로 오르기 위해서 부지런히 연습하며 역량을 강화하고자 할 것이다. 하지만 판에 박힌 듯한 상태에서는 무난한 수준의 능력일지라도 결국은 평범함으로 퇴보하게 된다.

문제
 당신은 기술과 자신감, 성공적인 업무 포트폴리오를 키워가야 한다. 또한 더 큰 일을 통해 스스로 도전할 필요가 있다고 느낀다. 그것은 더 큰 규모의 프로젝트나 더 큰 팀, 더 복잡한 과제, 새로운 사업 분야, 또는 새로운 장소와 관련될 수도 있다.

해결책
 깊은 쪽으로 뛰어들어라. 다 준비될 때까지 기다리다가는 아무 일도 못 할 수가 있다. 그러무로 당신에게 두드러지는 역할이나 어려운 문제가 주어진다면, 그 기회를 놓치지 말고 두 손으로 꽉 잡아라. 두렵게 생각되는 일을 맡고, 능력을 넘어서는 듯한 일을 실제로 함으로써만 당신은 성잘할 수가 있다. 

실천 방안 
 당신이 참여했던 프로젝트 중에서 코드의 라인 수나 개발자의 수로 봐서 가장 큐모가 크고 성공적이었던 프로젝트는 무엇인가? 당신이 단독으로 작업한 것 중 가장 규모가 큰 코드는 무엇인가? 이 질문들에 대한 답을 적고, 프로젝트의 복잡도를 나타내는 다른 척도가 있을지, 프로젝트를 평가 할 수 있는 도다른 방법이 있을지 한번 찾아보라. 그 첫도로 당신이 이때껏 참여 했던 모든 프로젝트를 평가해 보라. 이제 다음 프로젝트가 시작되면, 당신은 모든 프로젝트를 망라한 차트를 그려 놓고 새 프로젝트가 어디쯤에 위치하는지 점찍어 볼 수 있다. 얼마 후에는 이 차트에서 당신의 경력이 어떠너 방향으로 가고 있는지 알 수 있을 것이고, 여기에 기초해서 선택을 하기 시작할 수도 있을 것이다.

Retreat into Competence (한발 물러서라)
상황
 당신은 자신이 얼마나 보잘것 없는 지식을 가졌는지 깨닫기 시작한다. 또는 새로운 도전을 시작했지만 별로 잘 되어가지 않는다. 또는 둘 다 해당된다.

문제
 너무나 광대한 자신의 무지에 직면하면서 당신은 압도되어 버린다.

해결책
 한반 물러섰다가 투석기로 쏜 돌처럼 앞으로 나아가라.
 평정을 뒤찾기 위해 자신이 지닌 익숙한 능력 속으로 잠시 후퇴하라. 어떻게 만들어야 하는지 잘 아는 무언가를 만들어 보는 시간을 가져라. 그리고 그 경험을 바탕으로 당신이 얼마만큼의 길을 왔고 지금 현재 역량은 어느 정도인지 깨달아라.
 이 패턴은 자신의 능력을 벗어난 데까지 뻗어 보려고 한 사람들과 관련이 깊다. 만일 책임과 기술적 복잡도를 점진적으로 늘려가면서 적당한 보폭으로 견습과정을 걸어가고 있다면, 이 패턴으로 피난처를 구할 일은 없을 것이다. 하지만 당신이 정말로 고군분투하고 있거나, 깊은 쪽에서 간신히 물 위로 머리만 내밀고 있는 경우라면, 잠시 뒤로 물러설 기회를 찾아라. 때로는 두 걸음 전진하기 위해서 한 걸음 후퇴할 필요도 있는 법이다. 그럴 때는 가능한 빨리 그 뒷걸음질을 전진하기 위한 동력으로 바꾸는 것이 중요하다. 그 동력은 당신에게 이전보다 더 많은 지식과 더 훌륭한 기술이라는 자산으로 드러날 것이다.




덧.
 견습 프로그래머에게 강력히 추천하고 싶은 책이다. 상황, 문제는 책 내용을 그대로 옴겼고, 해결책에서 구체적인 방법들은 생략 하였다. 책의 내용은 모두 공개되어 있으나 영어가 부담스럽다면 책을 사서 읽는 것을 추천한다. 책의 가격과 독서 시간의 비용을 상쇄하고도 더 큰 피드백을 얻을 수 있을 것이다.


Reference : 
Related Links :
Posted by 김민우 julingks

댓글을 달아 주세요

비고
2009년 3월 software_craftsmanship 메일링 리스트 내의 오랜 논의 끝에 아래와 같은 선언문 초안이 도출되었다.

뜻을 품은 소프트웨어 장인으로서, 우리는 소프트웨어 개발을 수련하고 다른 이들의 학습을 돕는 것으로 전문적인 소프트웨어 개발의 기대치를 높이고 있다. 이와 같은 작업을 통해 우리는 아래와 같은 결론에 이르렀다.

 우리는 동작하는 것을 넘어서 잘 짜인 소프트웨어에,
 변화에 대응할 뿐 아니라 지속적으로 가치를 더하는 일에,
 개인들 그리고 그 사이의 상호작용에 더해서 전문가들의 공동체에,
 고객과의 공동 작업 뿐 아니라 생산적인 파트너십에 가치를 둔다.
 즉, 우리는 왼편의 항목을 추구함에 있어서 오른편의 내용이 필수불가결함을 알게 되었다.




Posted by 김민우 julingks

댓글을 달아 주세요

Google Guice

Mics 2010. 8. 22. 16:56
구글 쥬스(Google guice)는 Dependency Injection 프레임웍이다.

자바 코드의 new 키워드의 사용을 팩토리 패턴으로 대체한다.
new 키워드 대신에 @Inject 어노테이션을 사용한다.

우리의 코드가 변경하기 쉽고, 유닛테스트 및 재사용이 쉽게 하기 위해서는 코드가 서로 직접적인 의존관계여서는 안된다. 이런 경우에 팩토리 패턴이 필요하다.

Guice는 특히, 제너릭이나 어노테이션 같은 자바 5에서 소개된 특징들에 관해서 자바의 Type-safety 을 받아들인다.
Guice가 자바가 놓친 특징을 채워 줄 것으로 생각해도 된다. 이상적으로는 언어 자체가 이같은 특징을 제공하는게 좋다. 그러나 그런 언어가 나오기 전까지는 우리에게 Guice가 있다.

Guice의 목적은 개발과 디버깅을 쉽고 빠르게 만드는데 있다. 어렵고 느리게 만드는 것이 아니다. 
도구가 우리의 일을 쉽게 만들수 있다 할지라도, 우리는 도구에 관계없이  코드를 이해할 수 있어야 한다.
에러가 발생 했을 때, 쥬는는 유용한 메세지를 생산하기 위해서 특별한 노력을 한다.

다음은 new 키워드와 팩토리 패턴의 비교와 Guice에 대한 소개에 관한 Bob Lee의 비디오 프리젠테이션이다.


Source : 
Posted by 김민우 julingks

댓글을 달아 주세요

지식 포트폴리오를 관리하는 것은 금융 관련 포트폴리오를 관리하는 것과 매우 유사하다
  • 진지한 투자자들은 주기적으로 투자하는 습관이 있다
  • 장기간 성공의 열쇠는 다각화다
  • 똑똑한 투자자들은 자신의 포트폴리오를 보수적인 투자, 위험성이 큰 투자, 보상이 높은 투자 사이에서 균형을 잘 맞춘다
  • 최대 수익을 위해 투자자들은 싸게 사서 비싸게 팔려고 한다
  • 포트폴리오는 주기적으로 재검토하고 재조정해야 한다.
경력을 성공적으로 쌓기 위해서는 이와 동일한 지침을 사용해서 지식 포트폴리오를 관리해야 한다.

포트폴리오 가이드라인

주기적인 투자
 금융 투자에서와 마찬가지로 자신의 지식 포트폴리오에 주기적으로 투자해야 한다. 비록 소량일지라도 습관 자체가 금액의 합계 만큼이나 중요하다.

다각화
 여러가지를 알면 알수록 자신의 가치는 더욱 높아진다. 기본적으로 현재 작업에 사용하는 특정 기술의 등장과 퇴장을 알아야 한다. 컴퓨터 분야의 지형은 빨리 변한다. 오늘날 인기 있는 기술이 내일이면 거의 쓸모없어지기도 한다. 더 많은 기술에 익숙하다면, 변화에 더 잘 적응할 수 있을 것이다.

리스크 관리
 위험하지만 잠재적으로 보상이 높은 것에서 리스크가 낮고 보상도 낮은 것에 이르기까지 기술은 다양한 스펙트럼 위에 존재한다. 어느 날 갑자기 무너질지 모를 위험한 주식에 돈을 모두 투자하는 것은 좋은 생각이 아니며, 그렇다고 모든 돌을 보수적으로 투자하고 호기를 놓쳐 버리는 것도 좋지 않다. 여러분의 기술 달걀을 한 바구니에 모두 담지 마라.

싸게 사서 비싸게 팔기
 새롭게 떠오르는 기술이 인기를 끌기 전에 미리 알고 학습하는 것은 저평가된 주식을 찾아내는 것만큼이나 어려울 수 있지만, 이익 또한 그만큼 클 수 있다. 자바가 처음 나왔을 때 학습하는 것은 나름대로 리스크가 있었겠지만 얼리어뎁터들은 여기에서 큰 이득을 얻어 이제는 그 분야의 꼭대기에 있다.

검토 및 재 조정
 이 산업은 매우 동적이다. 지난날 탐구하기 시작한 인기 있는 기수링 지금에 와선 완전히 식어버릴지도 모른다. 한동안 사용하지 않았던 데이터베이스 기술을 복습해야 할 필요가 생길지도 모른다. 혹은 다른 언어를 시도해 봤다면 새로운 일자리를 얻는 데에 더 유리 할 수도 있다.


이제 포트폴리오에 무엇을 언제 추가할지 가이드라인을 갖게 되었다. 
자신의 포트폴리오에 자금을 제공할 지식 자산을 얻는 최선의 길은 무엇일까? 
몇가지를 살펴보자.

매년 새로운 언어를 최소 하나는 배워라.
 다른 언어는 동일한 무제를 다르게 푼다. 몇 개의 서로 다른 접근법을 알면 사고를 확장하고 판에 박힌 사고에 갇히는 걸 예방하는 데에 도움이된다.

기술 서적을 분기마다 한 권씩 읽어라.
 현재 사용하는 기술을 일단 완전히 익혔다면, 가지치기를 해서 지금하느 프로젝트와 관련 없는 분야까지 공부 범위를 넓혀라

비 기술 서적도 읽어라.
 컴퓨터를 사용하는 것은 사람이라는 점을 기억하는게 중요하다. 우리는 바로 이 사람들을 만족시키려고 노력하고 있다.

수업을 들어라.
 대학 또는 컨퍼런스에서 열리는 흥미로운 강좌를 찾아라

지역 사용자 모임에 참여하라.
 가만히 듣고 오지말고 적극 참여하라. 고립은 경력에 치명적일 수 있다. 타 회사 사람들이 어떤 일을 하는지 알아보라.

다른 환경에서 실험해보라. 
 윈도우에서만 일으 해 왔다면, 집에서는 유닉스를 갖고 놀아라. 
만약 makefile과 vim 만 쓰고 있다면, IDE를 시도해 보라.

트랜드를 놓치지 마라
업계의 잡지와 저널을 구독해라

 어떤 답을 알 수 없는 문제에 직면했을 때에 거기서 멈추지 마라. 스스로 답을 찾지 못하면, 답을 찾아줄 수 있는 사람을 찾아야 한다. 다른 사람들과 이야기함으로써 개인 네트워크를 구축하는 데 도움이 되기도 하고, 답을 찾는 도중에 별로 관련 없어 보이는 문제들에 대한 해답을 찾아서 놀라는 일도 생긴다. 그러는 사이 포트폴리오는 계속 커져 간다.
 이 모든 독서와 연구는 시간이 거릴고, 시간은 늘 부족한 자원이다. 그래서 미리 계획해야 할 필요가 있다.

마지막으로 중요한 점은 '비판적으로 생각'하는 것이다. 벤더나 매체의 과대광고에 흔들림이 없도록 자신의 포트폴리오에 있는 지식이 정확하고 확실해야 할 필요가 있다. 자신의 도그마가 유일한 답이라고 주장하는 열광자들을 주의해야 한다.

Source : 

Posted by 김민우 julingks

댓글을 달아 주세요