上篇简单介绍了 tuxedo的应用配置,这篇就通过实例来介绍tuxedo与oracle数据库的连接,以及简单的
基于同步通信方式的tuxedo服务端和客户端程序。篇幅有点长,组织有点乱,还请多包涵。
2.2 Tuxedo 与数据库互联
2.2.1 概述
在两层的 C/S结构中,客户端直接访问数据库,当采用TUXEDO中间件后,形成三层结构。这时,客户端
不直接访问数据库,而是改为调用中间件TUXEDO服务端上的服务,由TUXEDO服务端访问数据库,并把结果返回
给客户端。TUXEDO服务端可以和ORACLE在同一台服务器上,也可以在不同的机器上。如果在不同的机器上,在
TUXEDO的服务端所在的机器上要安装一个ORACLE的客户端。
TUXEDO服务端与ORACLE数据库连接有两种方式:
1. 不通过 XA 接口直接互连。适用于整个系统只有一个数据库的情况。
2. 通过 XA 接口互连,对整个系统有一个或多个数据库都适用,建议采用。
2.2.1 系统说明
操作系统:WINDOW XP + SP2
TUXEDO 版本:9.0 安装目录 c:\bea\tuxedo9.0
ORACLE 版本:10g 安装目录 c:\oracle
开发工具:VS 2005, VC 6.0
所有配置文件、源代码文件和执行程序所在目录:d:\zwtest
我根据自己的测试设置的环境变量,仅供在编译程序和启动tuxedo服务出错时比较
1.用户环境变量
APPDIR,值为 :d:\zwtest
TUXCONFIG,值为:d:\zwtest\tuxconfig
INCLUDE,值为:
C:\oracle\product\10.2.0\db_1\OCI\include;
C:\Program Files\Microsoft Visual Studio 8\VC\include;
2.系统环境变量
INCLUDE,值为:
C:\Program Files\Microsoft Visual Studio 8\VC\include;
C:\Program Files\Microsoft Visual Studio 8\VC\atlmfc\include;
C:\Program Files\Microsoft Visual Studio\VC98\MFC\Include;
C:\Program Files\Microsoft Visual Studio\VC98\Include;
C:\Program Files\Microsoft Visual Studio 8\VC\include;
LD_LIBRARY_PATH,值为:
$LD_LIBRARY_PATH:$TUXDIR/lib
LIB,值为:
C:\Program Files\Microsoft Visual Studio 8\VC\lib;
C:\tools\xerces-c_2_3_0-win32\xerces-c_2_3_0-win32\lib;
C:\Program Files\Microsoft Visual Studio\VC98\Lib;
path,值为:
%TUXDIR%\bin;
C:\Program Files\Microsoft Visual Studio 8\Common7\IDE;
C:\oracle\product\10.2.0\db_1\BIN;
C:\Program Files\Microsoft Visual Studio 8\VC\bin;
TUXDIR,值为:C:\bea\tuxedo9.0
2.2.2 配置的步骤
2.2.2.1 ORACLE的配置
1.以 dba 的权限进入sqlplus(默认口令是oracle)
C:\>sqlplus / as sysdba
2.搜索一下 xaview.sql 所在的位置(本例是在 C:\oracle\product\10.2.0\db_1\RDBMS\ADMIN\ 下)
运行:
SQL>@C:\oracle\product\10.2.0\db_1\RDBMS\ADMIN\xaview.sql;
如果一切正长则会提示创建了两个视图
3.授权
SQL>grant select on v$xatrans$ to public with grant option;
SQL>grant select on v$pending_xatrans$ to public with grant option;
4. 用 system 用户(缺省口令是manager,请根据实际情况修改)连接并授权
SQL>connect system/manager;
SQL>grant select any table to public;
5. 以system的身份建表,并插入2条测试记录
SQL>create table zwtest (name varchar2(8), age number);
SQL>insert into zwtest values('zhang', 30);
SQL>insert into zwtest values('liu', 27);
SQL>commit;
2.2.2.2 TUXEDO的配置
1. 修改 tuxedo 安装路径下的udataobj目录下的RM文件,在以Oracle_XA:xaosw:开头的那行开头加上#注释掉
如果是windows平台,就添加以下行:
Oracle_XA;xaosw;C:\oracle\product\10.2.0\db_1\RDBMS\XA\oraxa10.lib C:\oracle\product
\10.2.0\db_1\precomp\LIB\orasql10.lib
如果是unix平台,就添加:
Oracle_XA:xaosw:-L${ORACLE_HOME}/lib -lclntsh
注意:
上述库文件的位置可能随版本不同而不同,修改时以在本地机器上的实际位置为准
2. 在TUXEDO 用户下创建TMS文件:TMS_ORA10g, TUXEDO通过 TMS_ORA10g 与 ORACLE数据库采用 XA 协议进行通讯
buildtms -o c:\bea\tuxedo9.0\bin\TMS_ORA10g -r Oracle_XA
注意:
1.要是在windows平台下就在命令行窗口中运行,并确保c:\bea\tuxedo9.0\bin已经添加到系统的path环境变量中(
可用echo %path% 查看)。
这个过程中有可能会提示找不到一些头文件或库文件,一般都是所缺文件所在路径没有包含在path或INCLUDE或
LIB的环境变量中引起的,
可以搜索所缺文件的目录,把这个目录添加到对应的环境变量中。添加完后要打开一个新的命令行窗口才会生效
。
2.如果TUXEDO服务端与ORACLE数据库不在同一台服务器上,可能会提示找不到库文件 oraxaX.lib和 orasqlX.lib(
文件名中的大写X表示对应
的oracle版本号),可到ORACLE数据库的服务端对应目录下把这两个文件拷到本地机器ORACLE的客户端下的对应目
录下。
3. 配置 ubbconfig
(1) 在 *MACHINES 中增加:
TLOADDEVICE = "/home/oracle/temp/simpdb/TLOG"
TLOGNAME=TLOG
TLOGSIZE=200
(2) 改 *GROUPS 的配置为:
*GROUPS
GROUP1 LMID=simple GRPNO=1
OPENINFO="Oracle_XA:Oracle_XA+Acc=P/scott/tiger+SesTm=600+MaxCur=5+LogDir=."
TMSNAME="TMS_ORA10g" TMSCOUNT=2
注意:scott/tiger 为《tuxedo 简易培训教程》上数据库所采用的用户及口令,在我的测试平台上导致tuxedo服务启动
时 TMS_ORA10g无法启动,
所及建议用 system用户和对应密码 替换 OPENINFO中的 scott/tiger。
如果要试一下 scott 用户, 一般应先以system或sysdba用户登录sqlplus,然后对scott用户进行解锁:
alter user scott account unlock;
然后才能以scott用户登录,再根据提示修改默认密码。
修改后的配置文件 ubbconfig 内容如下:
*RESOURCES
IPCKEY 123456
DOMAINID simpapp
MASTER simple
MAXACCESSERS 100
MAXSERVERS 50
MAXSERVICES 100
MODEL SHM
LDBAL N
*MACHINES
servername LMID=simple
APPDIR="D:\simapp"
TUXCONFIG="D:\simapp\tuxconfig"
TUXDIR="C:\bea\tuxedo9.0"
TLOGDEVICE="D:\simapp\TLOG"
TLOGNAME=TLOG
TLOGSIZE=100
*GROUPS
GROUP1 LMID=simple GRPNO=1
OPENINFO="Oracle_XA:Oracle_XA+Acc=P/scott/tiger+SesTm=600+MaxCur=5+LogDir=."
TMSNAME="TMS_ORA10g" TMSCOUNT=2
*SERVERS
DEFAULT:
CLOPT="-A"
exefilename SRVGRP=GROUP1 SRVID=1
*SERVICES
注:
1) *MACHINES下的 servername 是指服务器所在的完整的计算机名
2) exefilename 是服务端可执行程序名,不加后缀,如果写错了在启动tuxedo服务时会出现以下报错:
CMDTUX_CAT:816: ERROR: Cannot exec, executable file not found
3) *GROUPS 的 OPENINFO中,如果用scott用户登录,则启动tuxedo服务时会出现以下报错:
exec TMS_ORA10g -A :
Failed.
用system用户登录则正常,可能和oracle相关的配置有关。但是以 system 用户登录后,在随后
执行客户端示例程序时也会报错:
tpcall failed, tperrno=11, tperrtext=TPESVCFAIL - application level service failure
查看ULOG文件,提示 select from EMP failure, sqlcode=-942, sqlerr=ORA-00942: 表或视图不存在
然后用system用户登录sqlplus,查询EMP表:
select * from EMP;
果然提示: ORA-00942: 表或视图不存在
再用scott连接:
conn scott/aiuuai;
查询EMP表返回正确结果。
总结: 用示例程序测试时,要么正确配置 Oracle 使TMS可以通过scott用户连接到oracle,要么以system
用户建新表作为被测的数据表,后者可能更容易些。
4) 在应用配置里提到,*SERVICES 提供了应用的特殊交易的信息。包括负载平衡(LOAD)和数据缓冲类型检查
(BUFTYPE)。
如果全部都是缺省值则本节可以省略。所以,*SERVICES下没有内容
(3) 最后生成 tuxconfig:
tmloadcf -y ubbconfig
系统自动生成一个 tuxconfig 文件, -y 的意思是无条件地覆盖已经存在的 tuxconfig 文件。
以下是我定义的 ubbconfig 内容:
*RESOURCES
IPCKEY 123456 #<Replace with a valid IPC Key>
DOMAINID zwtest
MASTER simple
MAXACCESSERS 10
MAXSERVERS 5
MAXSERVICES 10
MODEL SHM
LDBAL N
*MACHINES
APPDIR= "D:\zwtest" #"<Replace with the current directory pathname>"
TUXCONFIG= "D:\zwtest\tuxconfig" #<Replace with your TUXCONFIG Pathname>"
TUXDIR= "C:\bea\tuxedo9.0" #"<Directory where TUXEDO is installed>"
"HP12193906110" LMID=simple
TLOGDEVICE="D:\zwtest\TLOG"
TLOGNAME=TLOG
TLOGSIZE=100
*GROUPS
GROUP1 LMID=simple GRPNO=1 OPENINFO=NONE
OPENINFO="Oracle_XA:Oracle_XA+Acc=P/system/aiuuai+SesTm=600+MaxCur=5+LogDir=."
TMSNAME="TMS_ORA10g" TMSCOUNT=2
*SERVERS
DEFAULT:
CLOPT="-A"
testsvr SRVGRP=GROUP1 SRVID=1
*SERVICES
4.重命名下列文件,因为下列文件名与ORACLE带的文件名有冲突,所以要更改
(1) tuxedo安装路径 include目录下的:
sqlca.h 改为 sqlca.h.bbb
sqlcode.h 改为 sqlcode.h.bbb
sqlda.h 改为 vsqlda.h.bbb
(2) tuxedo安装路径 lib目录下的:
libsql.lib 改名为 libsql.lib.bbb
注意:
在oracle10g下,只发现sqlca.h, sqlda.h和tuxedo有重名文件,实际修改时最好先
在oracle安装目录中查找 sqlca.h , sqlcode.h , sqlda.h , libsql.lib 这4个文件,
如果和tuxedo安装目录下有重名文件,则将tuxedo目录下的重名文件作以上的更名修改
5.用TMADMIN 创建 TLOG文件,TUXEDO用一个文件TLOG记录对数据库操作的日志。用于协调分布式数据库的提交与回滚。
D:\>tmadmin
>crdl -b 500 -z d:\zwtest\TLOG
>crlog -m simple
>q
2.2.2.3 服务端的程序:
(1) testsvr.pc ,功能:根据客户端传入的EMPNO到表EMP中取ENAME的值,并把它返回给客户端。
但实际运行时会有错误返回,查ULOG发现是将从客户端收到的字符串转换为long型时有误,
因而导致错误的empno,所以查不到对应的ename,oracle查询返回"未找到数据"的错误,进而
使服务端返回交易失败的错误信息
/* testsvr.pc */
#include <stdio.h>
#include <atmi.h>
#include <userlog.h>
EXEC SQL INCLUDE sqlca;
EXEC SQL BEGIN DECLARE SECTION;
long al_empno=0;
char ac_ename[11]="";
EXEC SQL VAR ac_ename IS STRING(11);
EXEC SQL END DECLARE SECTION;
TEST(TPSVCINFO *rqst)
{
/* recv data from client */
al_empno = (FBFR32 *)rqst->data;
EXEC SQL select ename into :ac_ename from EMP where empno=:al_empno;
if (sqlca.sqlcode != 0)
{
userlog ("select from EMP failure, sqlcode=%ld, sqlerr=%s\n",
sqlca.sqlcode, (char*)sqlca.sqlerrm.sqlerrmc);
strcpy (rqst->data, sqlca.sqlerrm.sqlerrmc);
tpreturn (TPFAIL, 0, rqst->data, 0, 0);
}
/* return the record set to client */
strcpy (rqst->data, ac_ename);
tpreturn (TPSUCCESS, 0, rqst->data, 0, 0);
}
***************************************************************************************
(2) 修改后的 testsvr.c, 经测试可以正常运行,返回结果
并增加了一个将收到的字符串转换为大写并返回的服务(交易) UPPER
/* testsvr.pc */
#include <stdio.h>
#include <atmi.h>
#include <userlog.h>
#include <stdlib.h>
EXEC SQL INCLUDE sqlca;
EXEC SQL BEGIN DECLARE SECTION;
int al_age=0;
char ac_name[11]="";
EXEC SQL VAR ac_name IS STRING(11);
EXEC SQL END DECLARE SECTION;
GETNAME(TPSVCINFO *rqst)
{
/* recv data from client */
al_age = atoi (rqst->data);
EXEC SQL select name into :ac_name from zwtest where age=:al_age;
if (sqlca.sqlcode != 0)
{
userlog ("select from help failure, sqlcode=%ld, sqlerr=%s, age=%d\n",
sqlca.sqlcode, (char*)sqlca.sqlerrm.sqlerrmc, al_age);
strcpy (rqst->data, sqlca.sqlerrm.sqlerrmc);
tpreturn (TPFAIL, 0, rqst->data, 0, 0);
}
/* return the record set to client */
strcpy (rqst->data, ac_name);
tpreturn (TPSUCCESS, 0, rqst->data, 0, 0);
}
UPPER(TPSVCINFO *rqst)
{
int i;
userlog("request string = %s\n", rqst->data);
for (i=0; i<rqst->len-1; i++)
rqst->data[i] = toupper(rqst->data[i]);
tpreturn (TPSUCCESS, 0, rqst->data, 0L, 0);
}
2.2.2.4 编译服务端程序
1.用ORACLE的 proc 把 testsvr.pc 文件预编译成 testsvr.c 文件
d:\zwtest\proc testsvr.pc include=%TUXDIR%\include
2.用buildserver把第一步生成的 testsvr.c 编译成可执行文件,注意 -r 后带的 Oracle_XA 要与 RM 文件中的一致。
d:\zwtest\buildserver -o testsvr -f testsvr.c -r Oracle_XA -s GETNAME, UPPER
2.2.2.5 编写客户端程序:
(1) testcli.c, 发送一个字符串给服务端,接收服务端传回的转换为大写的字串
/* testcli.c */
#include <stdio.h>
#include <string.h>
#include "atmi.h"
int main (int argc, char *argv[])
{
long reqlen = 1024;
char *reqbuf;
if (argc != 2)
{
printf ("usage: testcli string\n");
exit (0);
}
/* connect to tuxedo server */
if (tpinit ((TPINIT *) NULL) == -1)
{
(void) fprintf (stderr, "Tpinit failed\n");
exit (1);
}
/* allocate send buffer */
reqbuf = (char *) tpalloc ("STRING", NULL, reqlen);
if (reqbuf == (char *)NULL)
{
printf("tpalloc failed\n");
tpterm();
}
strcpy (reqbuf, argv[1]);
printf("reqbuf = %s\n", reqbuf);
/* call server "UPPER" of tuxedo */
if (tpcall ("UPPER", (char *)reqbuf, 0L, (char**)&reqbuf,
(long *)&reqlen, 0 ) < 0)
{
printf ("tpcall failed, tperrno=%ld, tperrtext=%s\n",
tperrno, tpstrerror(tperrno));
tpfree (reqbuf);
tpterm ();
exit (1);
}
printf ("return = %s\n", reqbuf);
tpfree (reqbuf);
tpterm ();
return (0);
}
****************************************************************************
(2) testdbcli.c,传递一个年龄值给服务端,服务端查oracle数据库后返回对应的姓名
/* testdbcli.c */
#include <stdio.h>
#include <string.h>
#include "atmi.h"
int main (int argc, char *argv[])
{
char *reqbuf;
int reqlen = 11;
if (argc != 2)
{
printf ("usage: testdbcli age_number\n");
exit (0);
}
/* connect to tuxedo server */
if (tpinit ((TPINIT *) NULL) == -1)
{
(void) fprintf (stderr, "Tpinit failed\n");
exit (1);
}
/* allocate send buffer */
reqbuf = (char *) tpalloc ("STRING", NULL, reqlen);
if (reqbuf == (char *)NULL)
{
printf("tpalloc failed\n");
tpterm();
}
memset(reqbuf, 0, reqlen);
strcpy(reqbuf, argv[1]);
printf("reqbuf = %s\n", reqbuf);
/* call server "GETNAME" of tuxedo */
if (tpcall ("GETNAME", (char *)reqbuf, 0L, (char**)&reqbuf,
(long *)&reqlen, 0 ) < 0)
{
printf ("tpcall failed, tperrno=%ld, tperrtext=%s\n",
tperrno, tpstrerror(tperrno));
tpfree (reqbuf);
tpterm ();
exit (1);
}
printf ("name = %s\n", reqbuf);
tpfree (reqbuf);
tpterm ();
return (0);
}
2.2.2.6 编译客户端程序
d:\zwtest\buildclient -o testcli -f testcli.c
d:\zwtest\buildclient -o testdbcli -f testdbcli.c
2.2.2.7 启动 tuxedo
d:\zwtest\tmboot -y
如果显示提示信息:
Booting all admin and server processes in D:\zwtest\tuxconfig
INFO: BEA Tuxedo, Version 9.0, 32-bit, Patch Level (none)
INFO: Serial #: 650522264138-1796918373125, Expiration 2006-01-15, Maxusers 100
INFO: Licensed to: BEA Evaluation Customer
Booting admin processes ...
exec BBL -A :
process id=3960 ... Started.
Booting server processes ...
exec TMS_ORA10g -A :
process id=2692 ... Started.
exec TMS_ORA10g -A :
process id=2588 ... Started.
exec testsvr -A :
process id=2872 ... Started.
4 processes started.
就说明服务正确启动了, 这时就可以在客户端所在目录下运行客户端程序:
D:\zwtest>testcli.exe hello
reqbuf = hello
return = HELLO
D:\zwtest>testdbcli.exe 30
reqbuf = 30
name = zhang
如果提示:
Booting all admin and server processes in D:\zwtest\tuxconfig
INFO: BEA Tuxedo, Version 9.0, 32-bit, Patch Level (none)
INFO: Serial #: 650522264138-1796918373125, Expiration 2006-01-15, Maxusers 100
INFO: Licensed to: BEA Evaluation Customer
Booting admin processes ...
exec BBL -A :
process id=3920 ... Started.
Booting server processes ...
exec TMS_ORA10g -A :
Failed.
exec TMS_ORA10g -A :
并且程序停止不动,也不退出,好像死了一样
这就是TMS_ORA10g 在启动时发生了错误,本例中是由于前面提到的OPENINFO中使用scott用户引起的,
这样就会在当前目录生成一个 *.trc 文件,记录失败的原因。
同时 tuxedo 的 ULOG.MMDDYY 文件中也会记录一些错误信息,以供进行错误分析。
假死的程序可以用 Ctrl+C 来终止,然后用 tmshutdown -y 来关闭已经启动的 BBL 和其他服务。
最后可以用 tmshutdown -y来检测所有服务是否已关闭,提示:
Shutting down all admin and server processes in D:\zwtest\tuxconfig
tmshutdown: internal error: CMDTUX_CAT:764: ERROR: can't attach to BB
就表示本机的服务已经被关闭,这时就可以去修改ubbconfig,重新生成tuxconfig,然后再启动服务。
需要对服务端程序进行修改时,也必须先tmshutdown -y,才能修改服务端程序并重新生成可执行程序。
--转自