Skip to main content

用于编写 Oracle Tuxedo 客户端和服务器的 Python3 绑定

项目描述

https://github.com/aivarsk/tuxedo-python/workflows/CI/badge.svg

用于编写 Oracle Tuxedo 客户端和服务器的 Python3 绑定。支持 Python2。通过一本书使用 Python 实现 Oracle Tuxedo 应用程序现代化:

https://ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&MarketPlace=US&ASIN=180107058X&ServiceVersion=20070822&ID=AsinImage&WS=1&Format=_SL160_&tag=aivarsk-20

为什么?

我非常喜欢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模块目前仅支持STRINGCARRAYFML32缓冲区类型。

CARRAY映射到/来自 Python字节类型。

STRING映射到/来自 Python str类型。

FML32映射到 Python dict 类型/从 Python dict类型映射,其中字段名称 ( str ) 作为键,不同类型 ( intstrfloatdict ) 的列表 ( list ) 作为值。dictFML32的转换还将类型intstrfloatdict视为具有单个元素的列表:

{'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()来宣传服务器提供的服务。必须存在同名的方法。tpsvrdonetpsvrthrinittpsvrthrdone会在 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 标识符

Fname32Fldid32可用于查找从字段标识符到名称或其他方式的映射。

从标识符确定字段编号和类型的函数:

assert t.Fldtype32(t.Fmkfldid32(t.FLD_STRING, 10)) == t.FLD_STRING
assert t.Fldno32(t.Fmkfldid32(t.FLD_STRING, 10)) == 10

例外

发生错误时,模块会引发XatmiExceptionFml32Exception 。异常包含包含 Tuxedo 错误代码的附加属性代码,您可以将其与定义的错误(如TPENOENTTPESYSTEM )进行比较。

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

项目详情


下载文件

下载适用于您平台的文件。如果您不确定要选择哪个,请了解有关安装包的更多信息。

源分布

tuxedo-1.0.8.tar.gz (22.5 kB 查看哈希

已上传 source