关于EasyCICS的服务程序开发


在EasyCICS的服务程序使用多线程的注意事项
因为CICS服务程序的要求,只允许在主线程调用CICS API,所以EasyCICS有关CICS API的函数只允许在主线程被调用。


我有一条SQL语句调用不成功,为什么交易还是提交了?
一定要在执行每一条SQL语句后检查SQLCODE,CICS执行同步操作(包含在CicsCommit,CicsRollBack和ExitEasyCics内)时,将提交或回滚所有SQLCODE=0的语句。当然如果这些SQL语句无法最终提交,则会全部回滚。


请描述TXseries服务程序互相调用时的事务边界情况
1、如果是两个不同的CICS域的CICS服务程序相互调用,比如PrgA调用PrgB:

A. 如果在两阶段提交的情况下,也就是说采用两阶段提交的XA Switch Load File;采用支持两阶段提交的连接方式,比如PPC TCP和PPC Gateway;且采用无SYNCONRETURN的调用,比如CallProgramExt,则完全由PrgA决定提交和回滚,PrgB的提交和回滚操作将无效。
B. 如果在一阶段提交的情况下,也就是说采用一阶段提交的XA Switch Load File;采用只支持一阶段提交的连接方式,比如CICS TCP和Local SNA;或采用有SYNCONRETURN的调用,比如CallProgram,PrgA和PrgB自行决定提交和回滚。
2. 如果是在同一个CICS域的CICS服务程序相互调用,比如PrgA调用PrgB,且没有使用SysID,则当前的同步操作将完成从上一个同步操作到目前的所有支持事务的语句。


使用SetCurrentCA的注意事项
因为CA是作为公用内存而全程使用的,所以应该把CA声明成全局或静态变量,大小为BUF_SIZE。如果把CA声明成局部变量,必须在退出函数前使用SetCurrentCA(0)恢复原始的CA。另外必须对CA进行初试化。初试化的方法是在SetCurrentCA后使用BeginWrite函数,或者直接对CA使用memset,把它全部填充为二进制'\0'。


在数据量很大的系统,如何避免SFS撑满的现象?
首先要了解为什么会发生SFS撑满的现象。在EasyCICS创建结果集的时候,如果该结果集大于32K,则部分数据会暂时保存到SFS中。但如果读结果集的一方(比如CICS客户机)没有读完结果集就退出程序,在SFS中的数据不会马上被删除,因为服务程序不知道客户机会不会取这些数据。实际的情况是必须等待超时,超时的长度由RD的属性TSQAgeLimit决定,缺省是20(天),最少是5(天)。
为了避免SFS撑满,可以综合采用以下办法:

  1. 设置RD的属性TSQAgeLimit为5。
  2. 使用RsOpen打开结果集后,如果不想读完最后一行,应使用RsClose函数,则必定清除残余的结果集。
  3. 尽量使用最新的EasyCICS版本,因为最新的版本对结果集的处理更加周到。
  4. SFS的逻辑卷不能太小,就象UNIX的/tmp文件系统不能太小一样。对比较大的系统,应该给SFS的数据逻辑卷1-2G的空间,给SFS的日志逻辑卷的空间大概是数据逻辑卷的三分之一到二分之一。
  5. 推荐使用ECTSQ实用程序
注1:如果冷启动CICS域,则SFS中的TSQ自动被清除。
注2:有些人说ECTSQ在某些平台的CICSv5.1下编译出错,我已经更新的make文件。


使用StartTran调用的程序为什么不能再使用CallProgram?
问题描述: 两个PD分别名为EC01和EC02,TD TR02指向EC02。在EC01中以StartTran("TR02",...)的方式调用EC02,若EC02中没有执行CallPragram,则一切正常。若执行了CallProgram,则报错如下:
ERZ015028W/0154 07/28/02 11:12:33 RESC: Exception in user application code - exception string is: 'exc_e_illaddr'

解答:
注意被StartTran调用的服务程序没有公共通讯区,所以不能使用CallProgram。要使用CallProgram,必须为它建立一个公共通讯区,然后再恢复其本来的通讯区。EC02代码示意如下:

char ca[BUF_SIZE];
int r;


if( InitEasyCics() ) ExitEasyCics();

SetCurrentCA(ca);
......

r= CallProgram("...");
......

SetCurrentCA(0);

ExitEasyCics();


如何向被StartTran调用的程序传递Key值?
调用程序代码示意如下:

#include "easycics.h"

extern char *pComm;

void main (void){
        char ss[20] = "Hello world !";

        if( InitEasyCics() ) ExitEasyCics();

        BeginWrite();
        SetValue( "A", "VA" );
        SetValue( "B", "VB" );
        StartTran("TR33", pComm, BUF_SIZE);

        PrintStatus( "EC32 End" );

        ExitEasyCics();
}


被调用程序代码示意如下:

#include "easycics.h"


void main (void){
        char ca[BUF_SIZE], s[100], s1[10], s2[10];
        short n;
        int r;

        if( InitEasyCics() ) ExitEasyCics();
        n=BUF_SIZE;
        r= RetrievePara( ca, &n );

        SetCurrentCA(ca);

        sprintf( s, "Retrieve Retcode=%d, Length=%d", r, n );
        PrintStatus(s);
        GetValue1( "A", s1, sizeof(s1) );
        GetValue1( "B", s2, sizeof(s2) );
        sprintf( s, "A=%s, B=%s", s1, s2 );
        PrintStatus(s);

        SetCurrentCA(0);

        ExitEasyCics();
}


如何调试EasyCICS程序?
可以采用通用的CICS调试方法,比如CEDF和针对ASRA dump文件和traceback的分析。还可以分析和跟踪公共通信区的内容。观察公共通信区内容的方法如下:

如果是服务器的程序,可以用PrintStatus函数把需要调试的内容显示在控制台(使用cicstail观察)。


服务程序调用服务程序要注意什么
服务程序调用服务程序要使用CallProgram,没有任何问题。注意事项如下:
1.如果有必要,请使用SetCurrentCA来切换通信区。
2.任何结果集的操作不能交叉进行,必要时请使用数组变量。
3.服务程序调用服务程序,客户方的结果集长度不得超过32K,服务器方可以传递超过32K的数据。且不支持上载和下载数据块。必要时可以利用TSQ等传递数据。


不使用XAD时,我的服务程序建立了一个连接,得到一个句柄,我希望下一次调用该服务程序时不必再重新建立连接,直接使用该句柄,怎么办?
1. 把服务程序设置成驻留内存,注意修改CICS域设置:
PD:Resident=yes
RD:ProgramCacheSize=xx (建议大一些,比如50)
2. 修改程序:把该句柄设置成全局或静态变量,并赋初值0,例如:
static int g_Handle=0;
注意:第二次调用该程序时,系统不会再赋给它这个初值。所以可以在程序的开始,调用以下函数来得到句柄:

int getHandle(){
 if(g_Handle==0){
        g_Handle= connectXXX();
} return g_Handle;
}


出口程序和Abend handler
CICS服务器和客户机都有出口程序的概念。CICS服务器的出口程序不能调用CICS API,但是可以修改通信区的数据。示例程序参见cicsuxit.c。一般我们只关心其中的DPL user exit,即函数UE015050。如果设置了DPL user exit,在所有的DPL调用时都会调用该出口程序。
还有一种方式叫做“Abend Handler”。使用“EXEC CICS HANDLE ABEND”调用,可以指定这个服务程序发生Abend后去执行哪个程序。例如:

#include "easycics.h"

void main( void ){
        if( InitEasyCics() ) ExitEasyCics();
        EXEC CICS ADDRESS EIB( dfheiptr );
        EXEC CICS HANDLE ABEND PROGRAM("EC003";);
        ... ...
        ExitEasyCics();
}

这样,该服务程序在发生Abend时,会调用PROGRAM("EC003")。
PROGRAM("EC003")的举例写法如下:

void PrintStatus(char *statusbuf){
        EXEC CICS WRITEQ TD QUEUE("CSMT") FROM(statusbuf) LENGTH( strlen(statusbuf) );
}

void main( void ){
        EXEC CICS ADDRESS EIB( dfheiptr );

        PrintStatus("In Abend handler";);
        EXEC CICS ABEND;
}
注意:
如果Abend Handler程序采用EXEC CICS RETURN结束,则不会发生Abend。
通过EXEC ASSIGN ABCODE,可以得到实际的ABCODE。


系统运行频繁出现临时存储队列(TS)读取失败,产生A23G错误,同时产生dump文件
可能的原因如下:

  1. 如果结果集已经读完了,还继续读(RsFetchRow),也可能产生这个错误。所以要检查RsFetchRow的次数不可以超过结果集总行数!
  2. 如果你使用了ECTSQ或ECSFS这种维护工具,加入设置的时间间隔为1小时(3600秒)。但是有一个应用程序,生成了结果集后,超过1小时后,还在读结果集的数据,就有可能出线这个错误信息。
注意:
1. 设置ECTSQ或ECSFS的间隔时间超过结果集最大的有效时间。
1) 如果使用了ECTSQ,修改environment文件中的环境变量ECTSQ_TIMEOUT。比如“ECTSQ_TIMEOUT=7200”指定时间间隔是7200秒。
2) 如果使用了ECSFS,修改命令行参数,比如“ecsfs /.:/cics/sfs/MYSRV CICS01 7200”指定时间间隔是7200秒。
2. 使用EasyCICS的大型系统的SFS的数据卷至少要1G大。如果要扩大逻辑卷,需要停止CICS域和SFS,扩大逻辑卷,冷启动SFS,再冷启动CICS域。


服务程序都采用驻留内存方式,希望在同一个cicsas运行的服务程序,不要两次执行初始化
使用putenv和getenv通信即可。


如何设计CICS域的启动程序(RD:StartupProgList)
把用于初始化的业务程序直接设置为CICS域的启动程序,有以下问题:
1. 在启动程序(在RD:StartupProgList定义)正常退出以后,CICS域才能启动。如果发生失败(Abend),则CICS域不能启动。
2. RD:StartupProgList只能定义十个启动程序,如果启动的初始化程序过多,则不够定义。
3. 如果启动程序长时间运行,则CICS域长时间不能正常启动。
所以一般来说,启动程序使用CicsStart(CICS START调用)来启动真正的初始化程序。
#例如:启动初始化交易TR12

#include "easycics.h"

void main (void){
        int rc;
        char ss[20] = "Hello world !";

        if( InitEasyCics() ) ExitEasyCics();

        rc= StartTran( "TR12", ss, 20 );
 if( rc==0 ){
        /* 启动成功 */
 }

        ...
        ExitEasyCics();
}


#例如:如果希望过10秒再启动该初始化交易TR12,必须直接使用CICS API调用

#include "easycics.h"

void main (void){
        int rc, delay;
        char ss[20] = "Hello world !";
        short sn;

        EXEC CICS ADDRESS EIB (dfheiptr);
        if( InitEasyCics() ) ExitEasyCics();

        delay=10; /* 10 seconds */
        sn= sizeof(ss);
        EXEC CICS START AFTER SECONDS(delay) TRANSID("TR12") FROM(ss) LENGTH(sn) RESP(rc);
 if( rc== DFHRESP(NORMAL) ){
        /* 启动成功 */
 }

        ...
        ExitEasyCics();
}
无论是CICS域的启动程序,还是被CicsStart(CICS START调用)启动的程序,都没有公共通信区。建议下载最新的easycics v2.26a来编译之。
否则,如果使用easycics v2.25c以前的版本,必须避免一切针对通信区的easycics函数。


对于传输大数据量的程序,如何提高效率
这个问题主要在于提高SFS的效率。
修改/var/cics_servers/SSD/SSD.stanza文件中的SSD定义,提高BufferPoolSize,提高OpThreadPoolSize和ResThreadPoolSize,其中OpThreadPoolSize与RD:MaxServer相当。提高Checkpoint。例如:

/.;/cics/sfs/LR2PC:
ResourceDescription="SFS Server Definition"
AmendCounter=1
Permanent=no
StartType=auto
ProtectionLevel=none
MRAArchivingEnabled=no
BufferPoolSize=30000
IdleTimeout=300
Checkpoint=50000
OpThreadPoolSize=60
ResThreadPoolSize=15
CollatingLanguage="C"
ShortName="SLR2PC"
UserID="%S"
DataVolume="sfs_%S"
LogVolume="log_%S"
LogFile="logfile"
NameService=NONE

另外可以增加以下的系统环境变量:
ENCINA_TPOOL_SIZE=40
注意修改后要冷启动SFS才能生效。

从配置上讲,对于生产系统,一般需要创建1-2G的SFS数据卷,而SFS日志卷512M即可。
如果把SFS数据卷建立到RAM DISK上,对性能来说就再好不过了,当然建立到磁盘阵列上也不错。


EasyCICS v2.28使用AIX的RAMDisk作为缓存的设置步骤示例

1.  创建供EasyCICS使用的RAMDisk
mkramdisk 1500M
ls -l /dev | grep ram
mkfs -V jfs /dev/ramdisk0
mkdir /var/cicstmp
mount -V jfs -o nointegrity /dev/ramdisk0 /var/cicstmp

chown -R cics:cics /var/cicstmp


#注:如果要删除该RAMDisk
rmramdisk ramdisk0


2.  在Region的environment文件中加入:(如果不加入此项,则还是使用TSQ)
ECB_DIR=/var/cicstmp


3.  使用自动保护/清理程序:参见ecbuf的readme文件
1)  编译:
cc -o ecbuf    ecbuf-unix.c

2)  在后台执行之:
nohup ./ecbuf /var/cicstmp 1800 &


4. 如果使用了多Region的ISC:(单Region不需要此步骤)

1) 按照EasyCICS的全局配置规则配置CD等

2) 定义ECB程序
make -f ecb.unix.mk
cicsadd -c pd -r ... ECB PathName="..." RSLKey=public



返回“EasyCICS主页”