用于编写 Oracle Tuxedo 客户端和服务器的 Python3 绑定
项目描述
用于编写 Oracle Tuxedo 客户端和服务器的 Python3 绑定。支持 Python2。通过一本书使用 Python 实现 Oracle Tuxedo 应用程序现代化:
为什么?
我非常喜欢tuxmodule使您能够与 Oracle Tuxedo 交互的方式。不幸的是,它已经过时并且在某种程度上受到限制。所以我克隆了 tuxmodule 并开始清理编译器警告并处理我想到的一些功能:
多线程服务器
支持嵌套的 FML32 缓冲区和更多类型
支持最新的 Oracle Tuxedo 功能,例如tpadvertisex()和tpappthrinit()
即使服务返回 TPFAIL(而不是异常)也能收到响应
但我意识到这对我来说太多了,所以我决定用 C++ 和pybind11为 Oracle Tuxedo 编写我自己的 Python 模块,首先关注我认为最重要的部分。
支持的平台
该代码使用 GCC 8 编译器在最新版本的 Ubuntu 和 Oracle Linux 上进行了测试。它应该可以在其他 Linux 发行版和其他 UNIX 上运行,而无需进行调整或进行少量调整。
Windows 运行时要求(实验性)
在 Windows 上,Visual C++ 可再发行包是此项目的运行时要求。可以在这里找到。
我已经使用 Python 3.7.7 成功构建了模块。
Python 3.8.3 无法导入类似的模块,但我还没有解决方案。
ImportError: DLL load failed while importing tuxedo: The specified module could not be found.
Oracle Tuxedo 的替代品
Tuxedo-Python 也可以与称为 Fuxedo 的 Oracle Tuxedo 的开源替代品一起使用。只需导出指向安装 Fuxedo 的文件夹的TUXDIR就可以了。
该模块提供的所有演示代码都适用于 Oracle Tuxedo 和 Fuxedo,您可以通过使用 Python 和 Tuxedo-Python 模块来避免供应商锁定。
一般的
tuxedo模块目前仅支持STRING、CARRAY和FML32缓冲区类型。
CARRAY映射到/来自 Python字节类型。
STRING映射到/来自 Python str类型。
FML32映射到 Python dict 类型/从 Python dict类型映射,其中字段名称 ( str ) 作为键,不同类型 ( int、str、float或dict ) 的列表 ( list ) 作为值。dict到FML32的转换还将类型int、str、float或dict视为具有单个元素的列表:
{'TA_CLASS': 'Single value'}
转换为FML32然后返回dict变为
{'TA_CLASS': ['Single value']}
在 C 中采用缓冲区和长度参数的所有 XATMI 函数在 Python 中只采用缓冲区参数。
调用服务
tuxedo.tpcall()和tuxedo.tpgetrply()函数返回一个包含 3 个元素的元组,或者在没有接收到数据时抛出异常。这是我认为tuxmodule出错的部分:服务可能会在成功(TPSUCCESS)和失败(TPFAIL)时返回响应,并且通常失败响应包含一些重要信息。
0 或TPESVCFAIL
tpurcode ( tpreturn的第二个参数)
数据缓冲区
rval, rcode, data = t.tpcall('.TMIB', {'TA_CLASS': 'T_SVCGRP', 'TA_OPERATION': 'GET'})
if rval == 0:
# Service returned TPSUCCESS
else:
# rval == tuxedo.TPESVCFAIL
# Service returned TPFAIL
编写服务器
Tuxedo 服务器被编写为 Python 类。Tuxedo 调用tpsvrinit(3c)函数时将调用对象的tpsvrinit方法,成功时必须返回 0,错误时必须返回 -1。tpsvrinit的一项常见任务是通过使用服务名称调用tuxedo.tpadvertise()来宣传服务器提供的服务。必须存在同名的方法。tpsvrdone、tpsvrthrinit和tpsvrthrdone会在 Tuxedo 调用相应函数时被调用。所有这 4 种方法都是可选的,并且tuxedo模块总是在调用用户提供的方法之前调用tpopen()和tpclose()函数。
每个服务方法都接收一个带有传入缓冲区的参数,并且服务必须以调用tuxedo.tpreturn()或tuxedo.tpforward() 结束。与 C 不同,tuxedo.tpreturn()和tuxedo.tpforward()不执行longjmp,而是在服务方法返回时为这些调用设置参数。你可以有一个在 Python 的tpreturn之后执行的代码,它与上下文管理器配合得很好。以下两个代码片段是等效的,但我相信第一个更不容易出错。
def ECHO(self, args):
return t.tpreturn(t.TPSUCCESS, 0, args)
def ECHO(self, args):
t.tpreturn(t.TPSUCCESS, 0, args)
之后,必须使用类的实例和命令行参数调用tuxedo.run()以启动 Tuxedo 服务器的主循环。
#!/usr/bin/env python3
import sys
import tuxedo as t
class Server:
def tpsvrinit(self, args):
t.tpadvertise('ECHO')
return 0
def tpsvrthrinit(self, args):
return 0
def tpsvrthrdone(self):
pass
def tpsvrdone(self):
pass
def ECHO(self, args):
return t.tpreturn(t.TPSUCCESS, 0, args)
if __name__ == '__main__':
t.run(Server(), sys.argv)
UBBCONFIG
要将 Python 代码用作 Tuxedo 服务器,文件本身必须是可执行的 ( chmod +x *.py ) 并且它必须包含带有 Python 的 shebang 行:
#!/usr/bin/env python3
之后,您可以使用*.py文件作为UBBCONFIG中的服务器可执行文件:
"api.py" SRVGRP=GROUP1 SRVID=20 RQADDR="api" MIN=1 SECONDARYRQ=Y REPLYQ=Y
写客户
实现 Tuxedo 客户端不需要什么特别的东西,只需导入模块并开始调用 XATMI 函数。
#!/usr/bin/env python3
import sys
import tuxedo as t
rval, rcode, data = t.tpcall('.TMIB', {'TA_CLASS': 'T_SVCGRP', 'TA_OPERATION': 'GET'})
使用 Oracle 数据库
只需按照cx_Oracle的文档,您就可以使用cx_Oracle库和本地事务访问 Oracle 数据库。
如果您希望用 Python 编写的服务器参与全局事务,请首先指定要使用的资源管理器名称(类似于buidserver)。tuxedo模块目前支持:
NONE 默认“null”资源管理器
Oracle_XA 用于 Oracle 数据库
t.run(Server(), sys.argv, 'Oracle_XA')
之后,您应该使用tuxedo.xaoSvcCtx()函数在tpsvrinit中创建数据库连接:
def tpsvrinit(self, args):
self.db = cx_Oracle.connect(handle=t.xaoSvcCtx())
这是与标准cx_Oracle用例的唯一区别。这是一个单线程服务器的完整示例:
#!/usr/bin/env python3
import sys
import tuxedo as t
import cx_Oracle
class Server:
def tpsvrinit(self, args):
t.userlog('Server startup')
self.db = cx_Oracle.connect(handle=t.xaoSvcCtx())
t.tpadvertise('DB')
return 0
def DB(self, args):
dbc = self.db.cursor()
dbc.execute('insert into pymsg(msg) values (:1)', ['Hello from python'])
return t.tpreturn(t.TPSUCCESS, 0, args)
if __name__ == '__main__':
t.run(Server(), sys.argv, 'Oracle_XA')
对于多线程服务器,必须在tpsvrthrinit()(而不是tpsvrinit())中为每个线程创建新连接,并将其存储在threading.local()的线程本地存储中。
服务器必须属于以Oracle_XA作为资源管理器的组,在UBBCONFIG中类似这样
*GROUPS
GROUP2 LMID=tuxapp GRPNO=2 TMSNAME=ORACLETMS OPENINFO="Oracle_XA:Oracle_XA+Objects=true+Acc=P/scott/tiger+SqlNet=ORCL+SesTm=60+LogDir=/tmp+Threads=true"
*SERVERS
"db.py" SRVGRP=GROUP2 SRVID=2 CLOPT="-A"
tpadmcall
即使在应用程序关闭时, tpadmcall也可用于应用程序管理。与调用.TMIB服务相比,它也没有服务调用开销。Python 函数的外观和行为类似于tpcall ,除了rcode(结果元组中的第二个元素)始终为常量 0。
#!/usr/bin/env python3
import tuxedo as t
rval, _, data = t.tpadmcall({'TA_CLASS': 'T_DOMAIN', 'TA_OPERATION': 'GET'})
全球交易
可以使用tuxedo.tpbegin()、tuxedo.tpcommit()、tuxedo.tpabort()启动和提交或中止事务。这些函数采用与其对应的 C 函数相同的参数。
缓冲区导出和导入
FML32 标识符
Fname32和Fldid32可用于查找从字段标识符到名称或其他方式的映射。
从标识符确定字段编号和类型的函数:
assert t.Fldtype32(t.Fmkfldid32(t.FLD_STRING, 10)) == t.FLD_STRING
assert t.Fldno32(t.Fmkfldid32(t.FLD_STRING, 10)) == 10
例外
发生错误时,模块会引发XatmiException或Fml32Exception 。异常包含包含 Tuxedo 错误代码的附加属性代码,您可以将其与定义的错误(如TPENOENT或TPESYSTEM )进行比较。
try:
t.tpcall("whatever", {})
except t.XatmiException as e:
if e.code == t.TPENOENT:
print("Service does not exist")
演示
demo/文件夹有一些概念验证代码:
client.py Oracle Tuxedo 客户端
在 Oracle Tuxedo 服务器中运行的api.py HTTP+JSON 服务器
在 Oracle Tuxedo 服务器中运行的ecb.py HTTP+XML 客户端
mem.py多线程内存缓存
db.py在全局事务中使用 cx_Oracle 模块访问 Oracle 数据库
buf.py tpimport/tpexport 和 FML32 标识符的演示
去做
实现一些更有用的 API