服务端编程的任务
在三层结构中,把业务逻辑都放到中间层上,采用tuxedo做中间件时,也就是放到TUXEDO的服务端上,在服务端把业务逻辑划分成一个个独立的服务(SERVICE),把多个服务按一定的规则绑定到一个SERVER中.客户端调用这些服务来实现相应的操作.所以服务端编程的主要任务就是编写一个个的服务(SERVICE)。
SERVER端的生命周期如下:
1. 在SERVER启动时,它将自动调用初始化函数:tpsvrinit(int argc, char *argv[])
如果该SERVER所在的GROUP配置了连接数据库的接口,那么在默认情况下,该SERVICE将自动调用tpopen(),与数据库建立连接。
2. 该SERVER将把它所包含的SERVICE在系统的BOLLITIN BOARD中进行登记。使客户端可以调用这些SERVICE
3. 该SERVER进入循环,从它所对应的消息队列中取CLIENT发送的请求,调用相应的SERVICE进行处理,并调用tpreturn()把结果返回给CLIENT端或调用tpforward()传送给别的SERVICE处理。
4. 当系统SHUTDOWN 或用tmshutdown –s 把该SERVER SHUTDOWN 时,将自动调用SERVICE:tpdone()它将做一些清除工作,如断开与数据库的连接,从系统的BULLITON BOARD中清除与该SERVER对应的登记项。
SERVICE的划分原则:
• 信息隐藏原则:定义服务以及从客户到服务的数据流尽可能独立于服务的实现。服务程序中不要嵌入客户机有关数据采集技术等信息。反之,客户机也不应该意识到服务器程序的实现细节,如数据库结构以及记录格式。
• 分层服务原则:尽可能每个服务只完成一项���务,而不是多项任务。不要在一个服务函数中包含过多或过少的功能。努力定义一组简单服务来实现业务的基本功能,通过这组服务的组合,可以实现复杂的应用服务功能。
• 业务对象原则:围绕业务对象设计并使用服务功能。以一组相关的对象或公共对象将服务组织成为服务进程(SERVER)。
合理处理SERVICE与SERVER的关系
如果从管理维护方面看,一个SERVICE对应一个SERVER是最简单的方式。但这会增加SERVER的数量,使TUXEDO系统对系统的IPC资源要求增大(使系统的性能降低),或超过(使TUXEDO系统无法启动成功)。所以需要把多个SERVICE放到一个SERVER中。以降低TUXEDO对系统IPC资源的需求。下面是一些把SERVICE放在一起的原则:
1. 有互相调用的SERVICE不要放到同一个SERVER中,以免引起死锁现象。
2. 执行时间相近的SERVICE可放到同一个SERVER中。
3. 同一个SERVER中的SERVICE最好有相同的服务优先级,如果不同,最低的那个的请求可能要很长时间才得到处理。
4. 一个SERVER中不要有太多的SERVICE。
5. 把多资源要求相近的SERVICE放到同一个SERVER中。
6. 可根据业务规则把SERVICE放到同一个SERVER中。
7. 对一些使用率较高的(如:银行的取款对应的SERVICE)应单独放在一个SERVER中,并采用MSSQ方式。不要把它们与其他的SERVICE放在同一个SERVER中。
SERVER端编程
SERVER端编程主要用C(或COBAL)语言编写一个个的SERVICE,如果需要进行数据库调用,则用数据库提供的嵌入SQL语言的编程接口,如ORACLE的PROC,INFORMIX的ESQL等编写。一个SERVICE其实就时一个C函数,但它的参数只能是一个TPSVCINFO结构体指针。该结构体在atmi.h中的定义:
struct tpsvcinfo {
#define XATMI_SERVICE_NAME_LENGTH 32
char name[XATMI_SERVICE_NAME_LENGTH];/* service name invoked */
long flags; /* describes service attributes */
char *data; /* pointer to data */
long len; /* request data length */
int cd; /* reserved for future use */
long appkey; /* application authentication client key */
CLIENTID cltid; /* client identifier for originating client */
};
typedef struct tpsvcinfo TPSVCINFO;
TPSVRCINFO说明:
char name[XATMI_SERVICE_NAME_LENGTH]; 该SERVICE的名字
long flags; CLIENT端在TPCALL,TPACALL等的FLAGS中设置的值
char *data; 指向CLIENT发送过来的缓冲区的首地址
long len; data缓冲区的的长度
int cd; 当采用CONVERSATION通讯方式时,对应的CD值
long appkey; 当采用安全认证时,该CLIENT所对应的KEY
CLIENTID cltid; 用于识别该CLIENT的ID
在SERVER端的程序中可以调用TUXEDO提供的ATMI编程接口。这些函数主要在atmi.h中定义。
一个SERVICE的处理结果可以用tpreturn()返回给调用它的CLIENT端或用tpforward()转发给另一个SERVICE。
Tpreturn()函数:
Void tpreturn(int rval, int rcode, char *data,long len, long flags)
参数:
rval:定义该SERVICE的返回值,可以是:
TPSUCCESS: CLIENT端的tpgetrply() 或 tpcall()会返回一个非0值。如果是用会话方式进
行通讯,则会产生TPEV_SVCSUCC事件。
TPFAIL: CLIENT端的tpgetrply() 或 tpcall()会返回-1。如果是用CONVERSATION进行
通讯,则会产生TPEV_SVCFAIL事件。
TPEXIT: 和TPFAIL一样,并且该SERVICE所在的SERVER进程会自动退出。
Rcode:可以给它赋值,CLIENT端可以用tpurcode()取到该值。
Data: 返回给CLIENT端的缓冲区
Len: Data缓冲区的长度,只有该缓冲区类型为CARRAY时,才需要指定。
Flags:现在没用,设为0
TUXEDO中,服务可被客户端调用,也可被另一个服务调用,同时TUXEDO提供另一种调用方式--管道方式:服务进程在处理客户的请求时,不把结果返回给客户进程,而是把处理过的结果进一步转发给后续的其他服务进程,由其他服务进程接着处理,自己继续完成另外的服务请求,被转发的服务请求的结果由后续服务进程直接返回给客户进程,从而为编程和应用提供一种更加灵活的机制。如图所示,这种调用方式是通过Tpforward()函数来完成的。
Tpforward()函数
void tpforward(char *service, char *data, long len, long flags)
参数:
service:要调用的SERVICE的名字
data:传送给该SERVICE的输入缓冲区
len:data缓冲区的长度
flags:现在没用,设为0
Tpsvrinit()函数
Int tpsvrinit(int argc, char **argv)
描述:在SERVER启动时,它将自动调用函数:tpsvrinit(int argc, char *argv[])
如果该SERVER所在的GROUP配置了连接数据库的接口,那么在默认情况下,该SERVICE将自动调用TPOPEN(),建立与数据库的连接。
参数:int argc, char **argv 参数的含义与main()函数的参数含义一样。
返回值:执行成功返回0,失败返回-1
tpsvrdone()函数
void tpsvrdone()
描述:tpdone()做一些清除工作,如断开与数据库的连接,从系统的BULLITON BOARD中清除与该SERVER对应的登记项。
参数:无
返回值:无
SERVER端程序的编译:
用buildserver编译SERVER端程序,buildserver将调用C编译器对SERVER端程序进行编译。其格式如下:
buildserver [-o executable]...
[-v] \
[-s service2, service3:func] \
[-f source/object]...\
[-l object] ... \
[-r resource manager]
说明:
-v – 输出详细的编译信息
-o executable – SERVER的名字
-l executable – 在连接TUXEDO的库之后,还要连接的库文件
-s service – 指定将要发布的SERVICE的名称
-f source/object – 一个 .c 或 * .o 文件,它可以包含在该SERVER中的SERVICE调用到的函数
-r resource manager –RM的名称
例子:
buildserver -r ORACLE_XA -s DEMO -o TEST –f init.o –l mylib.so