??xml version="1.0" encoding="utf-8" standalone="yes"?>粉嫩精品导航导航,无码日韩精品一区二区免费,综合精品久久http://www.aygfsteel.com/adamduan/category/50479.htmlzh-cnWed, 28 Dec 2011 09:17:50 GMTWed, 28 Dec 2011 09:17:50 GMT60[IBM]构徏轻量U?Batch 框架处理 DB2 Content Manager 8.3 大量数据导入http://www.aygfsteel.com/adamduan/archive/2011/12/28/367430.htmlD|D|Wed, 28 Dec 2011 09:16:00 GMThttp://www.aygfsteel.com/adamduan/archive/2011/12/28/367430.htmlhttp://www.aygfsteel.com/adamduan/comments/367430.htmlhttp://www.aygfsteel.com/adamduan/archive/2011/12/28/367430.html#Feedback0http://www.aygfsteel.com/adamduan/comments/commentRss/367430.htmlhttp://www.aygfsteel.com/adamduan/services/trackbacks/367430.htmlhttp://www.ibm.com/developerworks/cn/data/library/techarticles/dm-0908luyx/

本文介绍了如何用多U程来构量 Batch 框架Q将大量的数据迁Ud IBM DB2 Content Manager 8.3 中。通过本文的学习,读者可以了解如何通过使用多线E调?IBM DB2 Content Manager API 构徏的框架来启动Q暂停,恢复Q停止,攄{操作?/p>

在用 API 导入大量数据的过E中Q如果没有框架很难有效的Ҏ个过E控Ӟ仅仅通过日志来分析解决问题L很浪Ҏ_q且效率不太理想?/p>

本文的内Ҏ在了如何使用多线E和配置文g来构?Batch 框架来处理大数量导入的问题?/p>

随着 IBM DB2 Content ManagerQ简U?IBM CMQ品的不断成熟Q越来越多的内容理pȝ需要迁Ud IBM CM 中来Q这些需要迁Uȝ数据通常首先把结构化的内容导到文本文件中Q与之相对应的图像和 pdf 文g通常攑֜对应的文件夹中,囑փ?pdf 对应的文件夹路径也通常存放在文本文件中Q然后迁Uȝ序遍历文本文Ӟ把对应的 Item q移?IBM CM 中。这些需要迁Uȝ数据通常都有几百 GQ如何有效的控制q移q程是一个很大的挑战Q因此我们必LZ个轻量?batch 处理框架来控制整个数据的q移周期Q记录处理过E中的错误,保证数据的一致性?/p>

同时Q在?API 导入数据的过E中Q被导入数据L千边万化Q无效的映射导入数据?DB2 Content Manager 的项Q导致工作变得复杂,同时使的设计和代码冗余,q且佉K用,l护和扩展步ؓ艰难?/p>

Z克服所提到的挑战,q个 batch 框架必须要有以下功能Q?

  • 用户Z不媄响生产环境性能的考虑Q可以暂时停止数据的q移Q或者减~迁Ud理的频率Q即框架必须h suspend ?slowdown 功能?/li>
  • 用户可以让暂停处理的pȝl箋处理Q即框架必须h resume 功能?/li>
  • 用户可以让系l停止处理,修改某些配置Q然后l处理,x架必L re-start 功能?/li>
  • 处理q程中发生的错误Q警告系l必记录下来,用户可以Ҏq些记录来修正数据?/li>
  • 通过配置文g建立规则来解x据千边万化的问题?/li>

构徏框架

构徏交互?/span>

要框架有交互性,我们必须有三个个U程Q客LU程Q服务端U程Q工作线E。客LU程负责发出工作指oQ服务端U程接受q些指oq调用工作线E来做实际的工作。对于客L和服务器交互Q在没有 web 服务器支持的情况下,我们可以采用一U古老但是很有效的做法:socket ~程?Java socket 对象?accept Ҏ会一直阻塞直到客L有程序输入,当客L有新的命令输入的时候,服务器端?socket 中读出命令,然后执行命o。下面是CZE序QClient.java 代表客户端程序,Server.java 代表服务器端E序QWorker.java 代表工作E序 ,Config.java 代表pȝ中一些参数配|?/p>
清单 1. 客户端程?/strong>
package com.ibm.batch.sample;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import org.apache.log4j.Logger;

public class Client {
	private Config config = null;
	public void setConfig(Config config) {
		this.config = config;
	}
	private Logger logger = Logger.getLogger(Client.class);
	public void sendCommand(String command) {
		Socket socket = null;
		OutputStream out = null;
		BufferedWriter writer = null;
		try {
			// establish the connection with server.
			socket = new Socket(config.getHost(), config.getSocketPort());
			out = socket.getOutputStream();
			writer = new BufferedWriter(new OutputStreamWriter(out));
			// send the command to server
			writer.write(command);
			writer.flush();
		} catch (IOException e) {
			logger.error(e.getMessage(), e);
			throw new RuntimeException(e);
		}
	}
}


清单 2. 服务器端E序
package com.ibm.batch.sample;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import com.ibm.batch.sample.util.ResourceUtils;

public class Server {
	private Config config = null;
	private boolean processing = true;	
	private Worker worker = null;
	public void setConfig(Config config) {
		this.config = config;
	}
	public static void main(String[] args) {
		Server server = new Server();
		// create the work thread
		Worker worker = server.createWorker(args);
		worker.start();		
		server.receiveAndExecuteCommand();
	}
	private Worker createWorker(String[] args) {
		Worker worker = new Worker();
		this.worker = worker;
		return worker;
	}
	/**
	 * receive the command from client and execute the command. the method is
	 * keeping running until client send the 'stop' command.
	 * 
	 * @throws Exception
	 */
	public void receiveAndExecuteCommand() {
		ServerSocket serverSocket = buildSocketConnection();
		// loop until client send 'stop' command
		while (processing) {
			Socket socket = null;
			try {
			socket = serverSocket.accept();
			String commandLine = readCommandFromSocket(socket);
                executeCommand(commandLine);
			} catch (Exception e) {
				throw new RuntimeException(e);
			} finally {
				ResourceUtils.closeSocket(socket);
			}
		}
	}
	private void executeCommand(String commandLine) {
		// TODO Auto-generated method stub
	}

	/**
	 * read the command from the socket
	 * 
	 * @param socket
	 * @return
	 */
	private String readCommandFromSocket(Socket socket) {
		InputStream in = null;
		BufferedReader bufferedReader = null;
		String commandLine = null;
		try {
			in = socket.getInputStream();
			bufferedReader = new BufferedReader(new InputStreamReader(in));
			commandLine = bufferedReader.readLine();
		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			ResourceUtils.closeInputStream(in);
			ResourceUtils.closeReader(bufferedReader);
		}
		return commandLine;
	}
	/**
	 * build the socket.
	 * 
	 * @return
	 */
	private ServerSocket buildSocketConnection() {
		// prepare the socket for client to connect.
		ServerSocket serverSocket;
		try {
			serverSocket = new ServerSocket(config.getSocketPort());
		} catch (java.net.BindException e1) {
			throw new RuntimeException("Socket port already in use.", e1);
		} catch (IOException ioe) {
			throw new RuntimeException(ioe);
		}
		return serverSocket;
	}
}


清单 3. 工作E序
package com.ibm.batch.sample;

import org.apache.log4j.Logger;

public class Worker extends Thread {
	Logger logger = Logger.getLogger(Worker.class);
	/**
	 * the main method for create item function.
	 */
	public void run() {
		createItem();
	}
	/**
	 * do the real job
	 */
	private void createItem() {	 		
	}
}

d suspend ?slowdown 处理命o

大数量的数据q移一般是在周末或者晚上进行,但是如果客户的历史数据太大,在周末或者晚上数据可能处理不完,Z不媄响生产环境的性能Q我们必能够在客户的工作时间暂~处理或者降低处理的频率Q把 cpu {资源让l客L序,也就是说处理U程 worker 的工作可?suspend 或?slowdow 。ؓ了让 worker U程知道需?suspend 当前处理Q我们可以在 worker 内部讄一个布变?isSuspendQ当E序在@环创?CM item 的时候,我们每次都判断一下这个布变?isSuspendQ当其ؓ ture 的时候,E序p用线E的 wait Ҏ中断当前U程的处理,wait Ҏq可以接受一个以微秒为单位的旉参数Q当旉到达 wait 指定的时间的时候,E序l箋创徏 CM Item 。ؓ了多U程之间的变量可见性,我们必须?worker ?isSuspend 变量?suspendTime 讄?volatile 。同理我们设|一个布变?isSlowdown 以及 slowdowTime 。示例程序如下:


清单 4. 工作E序
package com.ibm.batch.sample;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import org.apache.log4j.Logger;
import com.ibm.batch.sample.util.ResourceUtils;

public class Worker extends Thread {
	Logger logger = Logger.getLogger(Worker.class);	
	private volatile boolean isSlowdown = false;
	private volatile Double slowdownTime;
	private volatile boolean isSuspend;
	private volatile Double suspendTime;
	public void setSlowdown(boolean isSlowdown) {
		this.isSlowdown = isSlowdown;
	}
	public void setSlowdownTime(Double slowdownTime) {
		this.slowdownTime = slowdownTime;
	}
	public void setSuspend(boolean isSuspend) {
		this.isSuspend = isSuspend;
	}
	public void setSuspendTime(Double suspendTime) {
		this.suspendTime = suspendTime;
	}
	public boolean isSlowdown() {
		return isSlowdown;
	}
	public Double getSlowdownTime() {
		return slowdownTime;
	}
	public boolean isSuspend() {
		return isSuspend;
	}
	public Double getSuspendTime() {
		return suspendTime;
	}
	protected Object semaphore = new Object();
	private Config config;
	public void setConfig(Config config) {
		this.config = config;
	}


清单 5. L?/strong>
/**
	 * the main method for create item function.
	 */
	public void run() {
		BufferedReader reader = null;
		try {
			reader = getFileReader();
			String oneLine = null;
			while ((oneLine = reader.readLine()) != null) {
				if (isSlowdown()) {
					sleep4GivenTime();
				}
				if (isSuspend()) {
					suspend4SomeTime();
				}
				createItem(oneLine);
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		} finally {
			ResourceUtils.closeReader(reader);
		}
	}

	/**
	 * current thread sleep for some time,the unit is minute.
	 */
	protected void sleep4GivenTime() {
		try {
			Thread.sleep((long) (slowdownTime.doubleValue() * 1000));
		} catch (InterruptedException e) {
			// do nothing
		}
	}


清单 6.Suspend Ҏ
			
/**
 * suspend working for given time.
 */
protected void suspend4SomeTime() {
	synchronized (semaphore) {
	try {
	Double suspendTime = getSuspendTime();
	if (suspendTime != null) {
		double suspendTimeDouble = suspendTime.doubleValue() * 60 * 1000;
		semaphore.wait((long) suspendTimeDouble);
	} else {
	                     semaphore.wait();
	}
	} catch (InterruptedException e) {
	// tell user that the processing started
	logger.info("suspend is over,system is continue processing .");
	}
	}
	}
	/**
	 * do the real job
	 * 
	 * @throws Exception
	 */
	private void createItem(String oneLine) throws Exception {
	}
	private BufferedReader getFileReader() throws FileNotFoundException {
		String fileName = config.getFileName();
		File processingFile = new File(fileName);
		BufferedReader reader = new BufferedReader(new FileReader(
				processingFile));
		return reader;
	}
}

d resume 功能

在程序暂停处理以后,我们可以提前l止 suspendQ让框架l箋处理Q也是框架必须?resume 功能。我们调?Worker.java 对象上的 notify Ҏ来实现这个功能,CZ如下Q?/p>
清单 7.Resume
public class Worker extends Thread {
	/**
	 * resume the working.
	 */
	public void continueWorking() {
		cleanSuspend();
		synchronized (semaphore) {
			semaphore.notify();
		}
	}
}

 

d stop ?re-start 功能

有时候用户因Z些原因(例如修改配置文gQ想停止E序的执行,所以框架必L stop 的功能,但是 stop 的时候我们必L意记录程序处理到的行敎ͼq样客户再开始执行的时候能够从上次执行的断点l执行,也就是框架具备了 re-start 功能Q这?batch E序必须具备的一U很重要的功能,re-start 功能有多U实现方法,我们q里采取一U简单的ҎQ在 stop 的时候,把当前处理的记录C个文本文件中去,下次启动的时候从上次最后处理的对象开始进行处理。所以我们在 Worker.java 中增加一?keepProcessing 布尔变量Q在循环创徏 CM Item 的时?, 我们每次都判断一下这个值是否ؓ trueQ如果ؓ false 的话Q我们就停止循环处理Q在 Worker.java 中还要增加一?moveReaderToLastProcess ҎQ把 reader 重新定向Cơ处理点?/p>
清单 8. 停止和重?/strong>
public class Worker extends Thread {
	private volatile boolean keepProcessing;
	public boolean isKeepProcessing() {
		return keepProcessing;
	}
	public void setKeepProcessing(boolean keepProcessing) {
		this.keepProcessing = keepProcessing;
	}
	/**
	 * the main method for create item function.
	 */
	public void run() {
		BufferedReader reader = null;
		try {
			long lastProcessedRow = config.getLastProcessedRow();	
			reader = moveReaderToLastProcess(lastProcessedRow);
			String oneLine = null;
			connectToCM();
			while (((oneLine = reader.readLine()) != null)
					&& isKeepProcessing()) {
				if (isSlowdown()) {
					sleep4GivenTime();
				}
				if (isSuspend()) {
					suspend4SomeTime();
				}
				createItem(oneLine);
				lastProcessedRow++;
			}
			logCurrentProcessingLine(lastProcessedRow);
		} catch (Exception e) {
			throw new RuntimeException(e);
		} finally {
			ResourceUtils.closeReader(reader);
		}
	}	
	private void logCurrentProcessingLine(long lastProcessedRow) {
		config.setLastProcessedRow(lastProcessedRow);
	}	
	/**
	 * move current reader position to last process postion
	 * @return
	 * @throws IOException
	 */
private BufferedReader moveReaderToLastProcess(long lastProcessedRow) 
         throws IOException {
		// get the file reader
		BufferedReader reader = getFileReader();	
			
		// move the reader to the start row -1.
		int count = 0;
		while (count < lastProcessedRow-1) {
			reader.readLine();			
			count++;
		}
		return reader;
	}
}

d错误处理功能

刚才我们调用?createItem Ҏ是直接抛出异常的Q但是这L处理实际上是错误的,因ؓ?batch 处理q程中,我们不希望在处理某一?item 出错D剩余?item 不再处理Q所以我们在 catch 里面对异常进行分cd理,我们 catch 住非查异常(runtime exceptionQ,通常非检查异常是不可以恢复的Q所以我们直接抛出,让程序结束处理。对于其余的异常Q我们只是在日志中记录下来,q不抛出。在全部处理l束以后Q用户可以检查日志来q行相应的处理。示例代码如下:


清单 9. 错误处理
public class Worker extends Thread {
	/**
	 * do the real job
	 * 
	 * @throws Exception
	 */
	private void createItem(String oneLine) throws Exception {
		try {
			//create the item from one line
		}catch (RuntimeException e) {
			throw e;
		}catch (Exception e) {
			logger.error(e.getMessage(),e);
		}
	}
}

d创徏 CM item 功能

下面的内Ҏ在了如何使用配置文g来处理导入的问题?/p>

通过调用和运?API 来处理数据的导入Q我们首先定义一个基本信息的配置文gQ用来制定连接的信息Q其他配|文件的目录Q工作的目录{有兛_入需要的参数。然后定义导入数据和 DB2 Content Manager 的项的映配|文件。配|文件定义结束后Q我们就可以调用QPQ来启动相应的导入流E,在程序运行过E中Q可以动态的更改配置Q从而有效的处理导入的Q务?/p>

在开发过E中Q您可以灉|地定义各U配|文件以便实现多U导入规则,同时在程序运行中q行数据校验Q以防止冗余和非法数据被错误导入?/p>

下面的一些配|和代码CZQ以此介l了如何定义配置文gQ然后管?API 来完成导入的d?/p>

定义基本信息配置文gQ在该文件中Q须先设?IBM DB2 Content Manager 的一些连接参敎ͼ 如:

contentManagerDatabase=iCMnlsdb // 定义调用的数据库名字
 contentManagerUsername=iCMadmin // 定义用户?
 contentManagerPassword= password // 定义q接密码
 contentManagerSchema=ICMADMIN // 定义具体?schema

您可以在代码中用以上参数来实现对 IBM DB2 Content Manager 的连接,代码CZQ?/p>
DKDatastoreICM dsICM = new DKDatastoreICM(); 
// 创徏q接 dsICM.connect("iCMnlsdb", "iCMadmin", "password", "SCHEMA=ICMADMIN");

q需指定哪个文g夹存放映文Ӟ以及需导入的数据文Ӟ如:

mappingFilePath=config/rapid/mapping // 映射文g路径
 dataFileFolder=config/rapid/data // 数据文g路径

也可定义一些参数来增强该导入的程控制Q如Q?/p>
runPhase=2 
// 指定是第二阶D导入,在导入时需更新已有的数?/pre>

定义映射文gQ该配置文g主要用于用h要导入的数据映射?IBM DB2 Content Manager ?Item Type 中,您可自由定制该文Ӟ使用户遵循您定义的规范顺利完成数据迁UR如Q?/p>
C001.del=c01 
 C002.del=c01

该定义中 C001.del ?C002.del 是需要导入的数据文gQc01 是对应的 Item Type 名字。这U定义方法可实现多个数据文件导入同一?Item Type 中?/p>

具体的对应关pd下:

position=1|name=COMPANYNAME 
 position=2|name=COMPANYID 
 position=3|name=INPUTVALUE 
 position=-1|name=SPECIALVALUE|value=C1

q个映射关系反映了数据文件中列数?Item Type ?attribute 的关p,如第一列在 Item Type 中代表了名字?COMPANYNAME ?attribute 。您也可定义一些特D规则,如将 position 设ؓ负数Q以便反映该列是一个特D的 attribute, 它的值是固定的?比如?position 设ؓ -1 Ӟ名ؓ SPECIALVALUE ?attribute 的值L?C1 ?/p>

若您惛_现将一个数据文件导入多?Item Type 中,可在数据文g中加入一个特D列Q在映射文g中指定该列的列数Q以及当该列的值和多种 Item Type 的映关pR如Q?/p>
C003.del(position:3)

q样QC003.del ׃是单一的对应一?Item TypeQ而是先去取第三列 INPUTVALUE 的|再去对应表中查找到关联的 Item Type 。该对应表可设成Q?/p>
Value1=c01 
 Value2=c02

若第三列 INPUTDOCID 的gؓ Value1 Ӟ其对应的 Item Type ?c01Q同L当gؓ Value2 Ӟ会将该行数据导入?c02 ?Item Type 中?/p>

调用 API 完成操作的代码示例:在编写代码过E中Q需要调?DB2 Content Manager ?API 来完?Item Type 以及它包含的 attribute 的创建。上文已l出了通过参数来连?Content Manager 的方法,下面的示例代码用得到?DKDatastoreICM 来实现具体的操作Q?/p>
清单 10. API 调用
// Create an item / DDO / Root Component
DKDDO ddo = dsICM.createDDO("S_withChild", itemPropertyOrSemanticType); 
//createDDO(<Overall Item Type>, <Item Property / Semantic Type>);

// Adding Multivalue Attributes to DDOs, multiple type can be used, 
//here just give some example
 ddo.setData(ddo.dataId(DKConstant.DK_CM_NAMESPACE_ATTR,"S_varchar"), 
"this is a string value");  
  //string

ddo.setData(ddo.dataId(DKConstant.DK_CM_NAMESPACE_ATTR,"S_date"), 
java.sql.Date.valueOf("2001-08-12"));
 //date

ddo.setData(ddo.dataId(DKConstant.DK_CM_NAMESPACE_ATTR,"S_double"), 
new Double("123")); 
//double

l束?/font>

通过本文的介l,怿您对多线E构建的 Batch 框架实现大量数据q移的过E,和通过配置文g的管理的 API 实现数据导入的过E也有了一定的了解和学习。您可灵zd实现一对一Q一对多Q多对多{各U映关p,您也可以利用多线E来实现其他的功能的开发,~写出更加富有创造性的软g?/p>

参考资?

学习

获得产品和技?/strong>

  • 使用可直接从 developerWorks 下蝲?IBM 试用软g 构徏您的下一?Linux 开发项目?br />


D| 2011-12-28 17:16 发表评论
]]>
վ֩ģ壺 | Ϊ| | Ͷ| | | | ǿ| | ͼƬ| İ| Ͽ| ɽ| | | | | ¹Ȫ| | | Ͻ| | | | | | ƺ| | ɳ| ȫ| | ҵ| | ¤| | ʯׯ| | Ǧɽ| | | ̩˳|