上一文章 《__Win7 配置OGG(Oracle GoldenGate).docx》定下了 兩個目標:
- 目標1:
給安裝的Oracle_11g 創建 兩個用戶 admin 和 root 。
admin 對應了 ADMIN 結構,創建了一個 TB_ TEST表。
root 對應了 ROOT 結構,也創建一個 TB_ TEST (表結構一摸一樣)。
當 admin.TBTEST 中的表數據 變化時,root.TB TEST 的表數據 自動同步(備份)
PS. 實際的 容災備份,肯定是 兩個 Oracle 服務器,通過網絡傳輸 備份數據 —— 各位可以想象 admin 和 root 在兩台不同服務器 上。
- 目標2:
當 admin.TB_ TEST 表數據發生變化時,把這種變化 傳遞給 Java、C# 程序。
當 Java、C# 程序得到 數據變化消息時,把 數據的變化 記錄到日志中(或者其他操作)。
其中,目標1 已經完成,我們開始嘗試 目標2

下載 OGG Adapter Java:
http://www.oracle.com/technetwork/cn/middleware/goldengate/downloads/index.html








開始配置 Java運行環境:
我開始后悔了,我的系統是 64位Win7。
我后悔自己安裝的是 32位的 Oracle。
我后悔自己安裝了兩套 JDK: C:\Program Files\Java\jdk1.8.0121 和 C:\Program Files (x86)\Java\jdk1.8.0121。
我后悔自己的OGG目錄 取了這么長的 一個名字: D:\oracle\product\11.1.0\x86_ogg4oracle\
接下來的文章:這些坑將導致各種詭異的問題 —— 填坑指南,作者帶着你 一起踩坑一起填,在失敗中積累厚重經驗、在填坑中收獲絕對穩定。
配置環境變量:
我的電腦 64位Win7,安裝了 64位、32位 位兩種 JDK。Oracle_11g 32位,OGG 32位。
進行如下操作:
- 找到 64位 JDK目錄 —— 右鍵,“360強力刪除” —— 沒錯,強力粉碎,這源自我踩坑踩出的仇恨,粉碎,粉碎。 桌面>計算機>右鍵>屬性>高級系統設置>“高級”選項卡>環境變量。

CLASSPATH : .;%JAVAHOME%\lib\tools.jar;%JAVAHOME%\lib\dt.jar;%JAVA_HOME%\bin;
JAVAHOME : C:\Program Files (x86)\Java\jdk1.8.0121
JREHOME : C:\Program Files (x86)\Java\jre1.8.0121
Path : %JAVA_HOME%\bin;
驗證環境變量:
打開cmd (不詳述了,cmd 都打不開,這篇文章你也不用看了)

建議重啟電腦,好像有時候,JAVA的環境變量 要重啟后 才生效 —— 重啟保平安。
開始考驗 JAVA基礎的時候了:
打開 eclipse,新建一個 hello world 項目。導出 jar 包。




再次驗證 java 運行環境:

你以為 這個 hello.jar 有啥用? —— 沒用!
玄學懂不懂?
安裝SQL Server 你不去上個廁所,你還想安裝成功?【上廁所,減少人為磁盤操作,SQLServer安裝程序更穩定】
你不寫個 hello word,你還想把OGG Adapter Java 配成?【就是為了驗證 java 運行環境,配置者的java基礎】
開始配置 OGG Adapter Java 【正式代碼在下面,我們要先驗證 OGG-Java 的配置環境】
回顧一下:針對 ADMIN.TB_TEST 表 —— 當數據變化時,我們的Java插件要能夠 捕獲到數據變化。

生成表結構 定義文件。【目標:得到一個 .def 后綴的文件(過程中的其他文件都可以刪除)】
在 D:\Temp\ 文件夾創建一個 文本文件 source.prm 內容如下:

運行 cmd,通過 source.prm 生成 source.def 文件。



找到 D:\oracle\product\11.1.0\x86ogg4oracletarget\ 【目標端】雙擊運行 ggsci.exe 程序。 添加 javaue

配置 javaue
javaue.prm如下:
Extract JAVAUE
SourceDefs dirprm/source.def
getEnv (JAVA_HOME)
-- getEnv (LDLIBRARYPATH)
getEnv (PATH)
CUserExit ggjava_ue.dll CUSEREXIT PassThru IncludeUpdateBefores
GetUpdateBefores
Table ADMIN.*;
javaue.properties 如下:
gg.handlerlist=sample.SampleHandler
java.naming.provider.url=tcp://localhost:61616
java.naming.factory.initial=org.apache.activemq.jndi.ActiveMQInitialContextFactory
gg.handler.sample.type=sample.SampleHandler
goldengate.userexit.timestamp=utc
goldengate.userexit.nochkpt=true

嘗試啟動 javaue


PS.
如果出現 “不出錯”的錯誤(rpt 文件沒有任何異常信息),但 start javaue 窗口一閃而過,死活無法啟動。
記住如下方式:用命令行 參數模式 啟動 javaue,可以顯示 “不出錯”錯誤。
extract paramfile D:\oracle\product\11.1.0\x86_ogg4oracle_target\dirprm\javaue.prm

正式編寫 OGG Adapter Java 插件代碼
新建項目 custom,添加 jar包 引用。



創建 Java文件 SampleHandler,源碼如下:
注意:OGG 12 和 OGG 11 的代碼是完全不同的。
//---------------------------------------------------------------------------------------------------
package sample; //包名稱 sample,類名稱 SampleHandler —— 有沒有讓你想到什么? javaue.properties
import java.io.*;
//這是 OGG 11 的 import
import com.goldengate.atg.datasource.AbstractHandler;
import com.goldengate.atg.datasource.DsConfiguration;
import com.goldengate.atg.datasource.DsEvent;
import com.goldengate.atg.datasource.GGDataSource.Status;
import com.goldengate.atg.datasource.meta.DsMetaData;
import com.goldengate.atg.datasource.test.DsTestUtils.Logger;
import com.goldengate.atg.datasource.meta.*;
import com.goldengate.atg.datasource.*;
//這是 OGG 12 的 import
//import org.slf4j.Logger;
//import org.slf4j.LoggerFactory;
//import oracle.goldengate.datasource.AbstractHandler;
//import oracle.goldengate.datasource.DsConfiguration;
//import oracle.goldengate.datasource.DsEvent;
//import oracle.goldengate.datasource.DsTransaction;
//import oracle.goldengate.datasource.conf.DsHandler.Handler.Status;
//import oracle.goldengate.datasource.meta.DsMetaData;
//import oracle.goldengate.datasource.GGDataSource;
public class SampleHandler extends AbstractHandler {
//OGG 11 和 OGG 12 的 日志好像不一樣, OGG 12 似乎要寫 LoggerFactory.getLogger(SampleHandler.class);
private final Logger logger
= com.goldengate.atg.datasource.test.DsTestUtils.Log4jLogger.getLogger(SampleHandler.class);
@Override
public void init(DsConfiguration conf, DsMetaData metaData) {
super.init(conf, metaData);
logger.info("init!");
WriteStringToFile("D:\\SampleHandler。log", "SampleHandler.init(*)");
}
@Override
public Status transactionCommit(DsEvent e, DsTransaction tx) {
logger.info("transactionCommit!");
WriteStringToFile("D:\\SampleHandler。log", "SampleHandler.transactionCommit(*)");
return Status.OK;
}
@Override
public void destroy() {
super.destroy();
WriteStringToFile("D:\\SampleHandler。log", "SampleHandler.destroy(*)");
logger.info("destroy!");
}
@Override
public String reportStatus() {
WriteStringToFile("D:\\SampleHandler。log", "SampleHandler.reportStatus(*)");
return "reportStatus";
}
public void WriteStringToFile(String filePath, String text) {
try {
System.out.println(text);
File file = new File(filePath);
PrintStream ps = new PrintStream(new FileOutputStream(file));
//ps.println(text);// 往文件里寫入字符串
ps.append(text);// 在已有的基礎上添加字符串
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
//---------------------------------------------------------------------------------------------------
編譯通過,導出 jar包:




再次啟動 javaue:
窗口再次一閃而過,啟動失敗,繼續查錯。
參考:https://blog.csdn.net/catoop/article/details/49300853


重裝 Java環境,重新修改 4個環境變量,驗證 java 安裝環境。
java -jar D:\hello.jar重啟電腦(或者關掉所有 cmd, ggsci 窗口),重新打開 \x86ogg4oralcetarget\ggsci.exe 輸入命令: start mgr start javaue


現代詩:《OGG的迷惘》
那一刻,
晶瑩的淚水滑過臉龐,
我似乎,
看到了成功的希望。
蹉跎與掙扎,
糾結與迷惘。
在折騰中,我終究能走向想要的遠方。
—— 再給我一次機會,讓我再配置一次OGG。
—— 不!我選擇 死亡(罵娘)~
打油詩:《神坑OGG》
神坑最多OGG,
坑得勞資沒脾氣。
坑得勞資沒脾氣~
神坑最多OGG~
開始測試 javaue 插件是否成功:
程序這次崩潰了,對比一下 兩種配置:

在下面的配置(崩潰)中,我們看到了一個路徑 dirlib/custom.jar。
我們嘗試將 eclipse 導出的 jar 文件 D:\Temp\custom.jar,復制到 \x86ogg4oracletarget\dirlib\ 文件夾(dirlib 要手動創建)
再次運行,還是崩潰,還是相同的錯誤信息:


雖然不報錯了,但為什么 我在 insert 時,插件代碼沒有執行呢???

我們刪除 javaue,然后重建一下:

再次啟動 start javaue —— 記住我的ID,再不成功,我直播倒立寫代碼 ~

結果我又失望了:插件似乎沒有執行 —— 插件的代碼會生成一個 D:\ SampleHandler.log 的日志文件的,但結果沒有。
我似乎又打臉了 —— 啪啪的疼~
我整個人都不好了,日子本已如此艱難,真心快要過不下去了。
就在我一籌莫展時,我有事沒事的 翻看 \x86ogg4oracletarget\ 的幾個 6個字母的 文件夾。
意外發現了一個文件,我好奇的將文件打開。

我看到了 一個路徑:沒錯啊!就是我配置的 數據文件夾 路徑。
確實沒錯啊!難道非要驗證一下 路徑么?我雖然吃路徑的虧 上了 5次當了。
—— 但這個路徑確實沒錯啊,還不信,我復制給你看看。

整個人都不好了,文本配置最大的弊端就是:你敲幾百幾千個字母,一點手誤都不能有(尼瑪路徑錯了還沒提示)
再次重建 javaue,插入數據 —— 我這次也沒抱啥希望了,被折磨哭了都,不想被打臉了。

我看到了 一個 路徑: \dirdat\r1000000 —— 感覺很奇怪:
源端 ext1 >>> 目標端 rep1,這種數據文件 都已經到了 r1000005 了 —— 為什么 javaue 還在找 r1000000
—— 要不,在源端 重建一個 ext2,使用 r2,讓 javaue 從 r2 找數據?
打開源端 OGG 目錄:新建 ext2

源端 添加、啟動 ext2,啟動成功(我都已經駕輕就熟了) :

打開目標端,重建 javaue,使用 r2:

這次,我們看到了一段之前沒有出現過的代碼:

打開 dirdat 目錄:

再次執行 insert 腳本,插入數據的那一刻,javaue 崩潰了:

一臉懵逼,整個人都不好了。 就在我手足無措時,我意外看到 D:\ SampleHandler.log 文件被創建了 —— 雖然程序崩潰了,但是Java代碼執行了。


FlagFile 意外配置成功了(本文不多說了,就截了個圖)
心累了,又折騰了一天時間。直接說結果吧:
OGG Adapter Java,只和 /dirdat/r2000000 這種數據有關【和 版本無關、和 x86 x64 無關,和 源端、目標端 無關。】
整個OGG 所有涉及到的所有路徑,都必須使用 反斜杠 /。【如果寫成 D:\AAA\x86\ 程序可能會識別為亂碼而詭異崩潰。】
尤其注意:添加 javaue 時,路徑一定要用 反斜杠 /

其實直到現在,
OGG Adapter Java 11 x86 版本 每次都會崩潰。就是前兩個步驟的BUG截圖:數據變化捕獲到了,Java代碼執行了,但是程序崩潰了。
OGG Adapter Java 12 x64 版本,我把 /dirprm/javaue.prm 和 /dirprm/javaue.properties 和 /dirprm/source.def 三個文件 從 11 拷貝到 12, 執行 add extract javaue —— 程序卻能正常運行,捕獲到數據。
OGG 源端 必須和 Oracle 版本、32\64 位保持一致。
但是 OGG Adapter 沒那么多限制。OGG Adapter 只針對 /dirdat/r1 或 /dirdat/r2 這類 Trail 文件。
最終方案如下:

仔細看 右下角: 我增加了一個 OGG 12 x64 —— 由這個 OGG 啟動 javaue。
而 javaue 只只要對接 磁盤文件 dirdat\r2000001
雖然 有兩個 OGG 11 x86,但是 好在 磁盤 Trail文件 是互通的。
放一個成功運行的截圖:
我寫了一個 C#程序,每隔一秒 就向 ADMIN.TB_TEST 寫入一行記錄。
理論上 ROOT.TB_TEST 會同步這些數據。
理論上 javaue 能捕獲到 數據變化。

至此,第二目標完成。
最后放出 最終成功的配置:
或者參見 下一篇文章 《OGG Adapter Java一次性成功》
dirprm 文件夾:

javaue.prm 配置:

javaue.properties 配置:

Source.def 是生成的(生成方式看文章前面),手寫無效:

custom.jar 是編譯后 導出的 jar包:
//---------------------------------------------------------------------------------------
package sample;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
//這是 OGG 11 的 import
//import com.goldengate.atg.datasource.AbstractHandler;
//import com.goldengate.atg.datasource.DsConfiguration;
//import com.goldengate.atg.datasource.DsEvent;
//import com.goldengate.atg.datasource.GGDataSource.Status;
//import com.goldengate.atg.datasource.handler.*;
//import com.goldengate.atg.datasource.meta.DsMetaData;
//import com.goldengate.atg.datasource.test.DsTestUtils.Logger;
//import com.goldengate.atg.datasource.meta.*;
//import com.goldengate.atg.datasource.*;
//這是 OGG 12 的 import
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import oracle.goldengate.datasource.AbstractHandler;
import oracle.goldengate.datasource.DsColumn;
import oracle.goldengate.datasource.DsConfiguration;
import oracle.goldengate.datasource.DsEvent;
import oracle.goldengate.datasource.DsOperation;
import oracle.goldengate.datasource.DsOperation.OpType;
import oracle.goldengate.datasource.DsTransaction;
import oracle.goldengate.datasource.GGTranID;
import oracle.goldengate.datasource.meta.ColumnMetaData;
import oracle.goldengate.datasource.meta.DsMetaData;
import oracle.goldengate.datasource.meta.TableMetaData;
import oracle.goldengate.datasource.meta.TableName;
import oracle.goldengate.datasource.GGDataSource.Status;
public class SampleHandler extends AbstractHandler {
//OGG 11 和 OGG 12 的 日志好像不一樣, OGG 12 似乎要寫 LoggerFactory.getLogger(SampleHandler.class);
//private final Logger logger = Log4jLogger.getLogger(SampleHandler.class);
private final Logger logger = LoggerFactory.getLogger(SampleHandler.class);
@Override
public void init(DsConfiguration conf, DsMetaData metaData) {
super.init(conf, metaData);
logger.info("init!");
WriteStringToFile("D:\\SampleHandler.log", "SampleHandler.init(*)");
}
@Override
public Status transactionCommit(DsEvent e, DsTransaction tx) {
// DsMetaData meta = e.getMetaData();
// //System.out.println(meta);
//
// Set<TableName> tableNames = meta.getTableNames();
// for(TableName tableName : tableNames){
// String tableStr = "";
// tableStr = tableStr + tableName+" : ";
//
// TableMetaData metaData = meta.getTableMetaData(tableName);
// ArrayList<ColumnMetaData> columns = metaData.getColumnMetaData();
// for(ColumnMetaData column : columns){
// tableStr = tableStr + "\r\n " + column.getColumnName() + " | "+column.getDataType().toString();
// }
//
// System.out.println(tableStr+"\r\n");
// //System.out.println(metaData);
// }
//
// //GGTranID tranId = e.getTargetCheckpointInfo();
// //System.out.println(tranId);
//
// System.out.println(tx.getSize());
// System.out.println(tx.getTotalOps());
// System.out.println(tx.getReadTime());
// System.out.println(tx.getTransactionBeginTime());
//
//
// Object eventSource = e.getEventSource();
// System.out.println(eventSource);
//
// DsOperation lastOp = tx.getLastOperation();
// System.out.println(lastOp);
//
//
// List<DsOperation> listOp = tx.getOperations();
// System.out.println(listOp.size());
// //System.out.println(listOp.get(0).getColumn(0).getBeforeValue());
// for(DsOperation op : listOp){
// System.out.println(op.getTableName());
// }
Status superResult = super.transactionCommit(e, tx);
logger.info("transactionCommit!");
WriteStringToFile("D:\\SampleHandler.log", "SampleHandler.transactionCommit(*) => "+superResult);
return superResult;
}
@Override
public Status operationAdded(DsEvent e, DsTransaction tx, DsOperation dsOperation) {
DsMetaData meta = e.getMetaData();
//System.out.println(meta);
// Set<TableName> tableNames = meta.getTableNames();
// for(TableName tableName : tableNames){
// String tableStr = "";
// tableStr = tableStr + tableName+" : ";
//
// TableMetaData metaData = meta.getTableMetaData(tableName);
// ArrayList<ColumnMetaData> columns = metaData.getColumnMetaData();
// for(ColumnMetaData column : columns){
// tableStr = tableStr + "\r\n " + column.getColumnName() + " | "+column.getDataType().toString();
// }
//
// System.out.println(tableStr+"\r\n");
// //System.out.println(metaData);
// }
System.out.println("---------------------------------------");
OpType opType = dsOperation.getOperationType();
System.out.println(opType);
TableName tableName = dsOperation.getTableName();
System.out.println(tableName.getFullName());
TableMetaData metaData = meta.getTableMetaData(tableName);
ArrayList<ColumnMetaData> columnMetas = metaData.getColumnMetaData();
List<DsColumn> columns = dsOperation.getColumns();
for(int i=0; i<columnMetas.size(); i++){
ColumnMetaData columnMeta = columnMetas.get(i);
DsColumn column = columns.get(i);
System.out.println(" " + columnMeta.getColumnName() + "\t | \t"+ column.getAfterValue() + (column.isChanged()? "\t >>> \t" +column.getAfterValue():""));
}
System.out.println("---------------------------------------");
Status superResult = super.operationAdded(e, tx, dsOperation);
logger.info("operationAdded!");
WriteStringToFile("D:\\SampleHandler.log", "SampleHandler.operationAdded(*) => "+superResult);
return superResult;
}
//@Override
//public DataSourceListener.State getState() {
// return super.getState();
//}
@Override
public void destroy() {
super.destroy();
WriteStringToFile("D:\\SampleHandler.log", "SampleHandler.destroy(*)");
logger.info("destroy!");
}
@Override
public String reportStatus() {
String superResult = "OK"; //super.reportStatus(); 調用父類函數,程序就會崩潰。
WriteStringToFile("D:\\SampleHandler.log", "SampleHandler.reportStatus(*) => "+superResult);
return superResult;
//return "status report...===";
}
public static void WriteStringToFile(String filePath, String text) {
try {
System.out.println("AAAAAAAAAAAAAAAAA :" + text);
FileWriter writer = new FileWriter(filePath, true);
writer.write("\r\n"+text);
writer.close();
} catch (IOException e) {
//e.printStackTrace();
System.out.println(e.getMessage());
}
}
}
//---------------------------------------------------------------------------------------
至此,OGG 兩大目標 全部完成。幾乎把能踩的坑都踩了一遍。
雖然,我會再寫一篇文章 《OGG Adapter Java一次性成功》
—— 但是,不要想太多:你還是得乖乖的回到 這篇文章,學習如何從坑中逃出來。
—— 此篇OGG踩坑文章,踩的各種坑 不下十幾種,從配置到代碼、從編碼格式到反斜杠,從版本到位數,從環境變量到筆誤。
—— 作者着實不容易。
最后還是以一首打油詩結尾:
現代詩:《OGG的迷惘》
那一刻,
晶瑩的淚水滑過臉龐,
我似乎,
看到了成功的希望。
蹉跎與掙扎,
糾結與迷惘。
在折騰中,我終究能走向想要的遠方。
—— 再給我一次機會,讓我再配置一次OGG。
—— 不!我選擇 死亡(罵娘)~
打油詩:《神坑OGG》
神坑最多OGG,
坑得勞資沒脾氣。
坑得勞資沒脾氣~
神坑最多OGG~
兩首詩把 OGG 批了一頓,還能怎樣呢?當然是選擇 原諒他啊。
舒小龍 2018-05-31 20:02

