简介:面向Linux平台使用Python调用SAP RFC接口的开发者,这份资源提供了SAP官方NW RFC SDK的750版本压缩包,内含完整运行与编译环境。借助pyrfc绑定库,可在Python中直接与SAP NetWeaver系统通信,适合负责ERP集成、数据同步或自动化运维的工程技术人员。包体共27个文件,整体约18.05MB,以头文件h、C/C++源码、动态库so为主,同时包含sapnwrfc.ini配置样例、rfcexec与startrfc命令行工具以及demo示例,可支撑从环境配置到函数调用的全流程。已有1401人学习下载,是快速搭建本地开发环境的常用参考。通过该SDK,读者能省去自行搜索编译依赖的麻烦,直接获得头文件、库文件及配套说明,配合SAP函数测试用例STFC_CONNECTION验证连接,并进一步掌握表数据读取、业务函数调用等实操技能,对理解pyrfc与NW RFC SDK的协作机制也有直接帮助。压缩包目录结构保留官方发布布局,方便快速部署。
1. 在 Linux 上让 Python 通过 pyrfc 连接 SAP,绕不开的是 nwrfcsdk 这层底库
很多人在 Linux 上让 Python 通过 pyrfc 连接 SAP 时,第一反应都是直接 pip install pyrfc,真正卡住他们的往往是 nwrfcsdk 这个底层开发包没有装对。pyrfc 只是 Python 侧的一层绑定,RFC 协议的具体实现在 SAP 提供的 nwrfcsdk 里,装不上、导入报错、调用超时,十有八九是 SDK 路径、动态库依赖或账号权限出了问题。这篇笔记按实际落地顺序,把 Linux 下从 nwrfcsdk 落位到跑通第一个 RFC 函数的完整链路拆开,并把现场最常遇到的 5 类报错按现象、原因、解决写清楚。适合做 SAP 数据接口、批处理脚本或运维自动化的读者照着复现。
2. 先把 nwrfcsdk 放对位置:Linux 下的目录布局、环境变量与自检命令
安装 pyrfc 之前,先确认 nwrfcsdk 已经落位。常见做法是到 SAP 官方支持门户下载 Linux x86_64 版本的 NW RFC SDK,传到服务器后解压到固定目录,再设置环境变量。很多人跳过这一步直接装 Python 包,结果后面每一步都在为这个顺序错误付代价。
2.1 为什么不能绕过 nwrfcsdk:pyrfc 的本质是一个 C 扩展包装
pyrfc 不是纯 Python 实现的库,它通过 C 扩展去调 SAP RFC 的底层接口。RFC 协议里的握手、参数序列化、连接管理都在 SAP 提供的动态库里完成,Linux 下对应的核心文件是libsapnwrfc.so。pip 安装 pyrfc 时不会自动下载这个库,因为 SAP 的闭源代码不能扔到 PyPI 上分发,所以你必须提前在系统里准备好 nwrfcsdk。
选型上,pyrfc 是目前最常用的 Python RFC 绑定方案。SAP 侧现在也有 OData、OpenAPI 之类的接口路线,但如果你要调 ABAP 函数模块或 BAPI,RFC 这层仍然是稳定且完整的通道。pyrfc 适合那种已经确定要走 RFC 的项目,它不是银弹,但比直接手写 RFC 协议可靠得多。
2.2 SDK 解压后的目录结构:必须被 ldd 找到的是哪几个
拿到 Linux 版本的 SDK 压缩包后,我一般统一放到/opt/sap/nwrfcsdk,这样后续的脚本、systemd 服务和 cron 任务引用同一套路径,不用到处改。
mkdir -p /opt/sap/nwrfcsdk tar -xzf nwrfcsdk_linux_x86_64.tar.gz -C /opt/sap/nwrfcsdk --strip-components=1 find /opt/sap/nwrfcsdk -maxdepth 2 \( -name '*.so' -o -name '*.ini' \) -type f第一行创建目录,第二行解压时用--strip-components=1去掉压缩包里的顶层目录,让lib/libsapnwrfc.so直接出现在/opt/sap/nwrfcsdk/lib/下,路径固定且短。第三条 find 命令列出所有.so和.ini文件,用来确认核心动态库和配置文件确实解压出来了。
这里要重点确认libsapnwrfc.so存在。它是 pyrfc 运行时一定会加载的动态库,找不到它的话,后续 import 报错是必然的。另外不建议把 SDK 放到带空格或中文的路径里,动态链接和 Python 扩展加载在这种路径上容易出一些难以解释的边界问题。
2.3 环境变量三件套:SAPNWRFC_HOME、LD_LIBRARY_PATH 与 PATH
解压完成之后,环境变量必须跟上。常用的做法是定义一个标准配置,写进 profile 文件,让登录 shell 自动加载。
export SAPNWRFC_HOME=/opt/sap/nwrfcsdk export LD_LIBRARY_PATH=$SAPNWRFC_HOME/lib:$LD_LIBRARY_PATH export PATH=$SAPNWRFC_HOME/bin:$PATHSAPNWRFC_HOME告诉 pyrfc 的安装流程和 SDK 自身去哪里找资源文件,LD_LIBRARY_PATH是运行时动态链接器寻找libsapnwrfc.so的路径,PATH则是为了能直接调用 SDK 自带的辅助命令。三个变量里前两个最关键,PATH 属于顺手写上,不设置也能跑。建议把这三行写进/etc/profile.d/sapnwrfc.sh,这样新开的登录 shell 都自动带上。
这里有个很容易翻车的点:systemd 服务和 cron 任务不会加载/etc/profile.d,它们的环境是干净的。如果生产环境是用 systemd 托管 Python 服务,必须在 service 文件里单独写Environment=LD_LIBRARY_PATH=/opt/sap/nwrfcsdk/lib,cron 则在 crontab 里显式声明变量,否则服务重启后照样报找不到动态库。
2.4 用 ldd 和 ctypes 验证 SDK 是否真的可用
环境变量设完,先别急着装 pyrfc,先验证 SDK 本身能不能被系统加载。两个命令足够:
ldd /opt/sap/nwrfcsdk/lib/libsapnwrfc.so python3 -c "from ctypes import CDLL; CDLL('/opt/sap/nwrfcsdk/lib/libsapnwrfc.so'); print('sdk ok')"第一条ldd列出这个动态库依赖了哪些系统库,输出里如果出现not found,说明当前 Linux 缺少底层依赖,比如 OpenSSL 或 libstdc++ 的兼容版本,这种情况要先把系统依赖补齐再往下走。第二条用 Python 的 ctypes 直接加载动态库,只要能打印sdk ok,说明动态库本身可以加载,SDK 这层就通了。
SDK 解压包里一般会带一个sapnwrfc.ini配置示例,它控制 trace、日志目录这些运行时行为。我的习惯是解压后复制一份到工作目录,平时不动,等出现网络异常或编码异常时再把 trace 打开看日志。这样 SDK 就不是一个黑匣子,报错时能拿到比 Python 异常更底层的线索。
3. 安装 pyrfc 并跑通第一个 RFC:从 pip 到 RFC_PING 的最小闭环
SDK 就位后,下一步是安装 pyrfc 并写下第一段可运行的连接代码。这一章的目标不是调通某个业务 BAPI,而是用最保守的方式验证整条链路能通,后面再换真实业务函数。
3.1 安装前的环境检查:Python、编译器和 OpenSSL
先花 30 秒检查系统环境,比装失败后再排查快得多。
python3 --version gcc --version | head -1 openssl versionpython3 版本决定有没有匹配的预编译轮子,gcc 是源码编译失败时唯一能自救的工具链,openssl 则关系到 SDK 依赖的libssl和libcrypto是否兼容。pyrfc 的 C 扩展在导入阶段就会加载 OpenSSL 相关的动态库,系统里如果同时存在多个版本或者头文件与运行库不一致,后面很容易出现段错误。
如果你的发行版是 Debian/Ubuntu 这一类,Python 开发头文件通常叫python3-dev;RPM 系一般是python3-devel。装不装得看实际安装方式:官方轮子不需要,源码编译就必须有。一个稳妥的做法是直接把编译工具链和开发头文件装上,代价很小,省得在安装半路被卡住。
3.2 pip install pyrfc 的两种结果:预编译轮子与源码编译
安装命令本身不复杂,复杂的是失败后的判断。
pip install pyrfc pip uninstall pyrfc -y # 如果之前有过失败的安装,先卸干净再重装如果 PyPI 上有与当前 Python 版本匹配的 manylinux 轮子,安装过程几十秒就结束。如果没有匹配的轮子,pip 会转而执行源码编译,这时候需要 Cython、gcc 和 Python 头文件。源码编译路径下如果提示找不到 SDK,通常是因为SAPNWRFC_HOME没有在安装进程里生效,重新 export 后重装即可。
判断安装是否成功不要只看 pip 的输出,要进入 Python 里真正执行一次import pyrfc才算数。很多场景下 pip 已经装完,但运行时动态库没加载,报错被误判成安装失败。记住这个顺序:先 SDK,再环境变量,最后 pip 安装,倒过来做每一步都会踩到同样的坑。
3.3 最小连接代码:Connection 参数逐个拆解
连接 SAP 应用服务器最直接的方式是ashost直连,也就是指定一台应用服务器的主机名或 IP。排障阶段建议先用这种方式,因为它绕开了消息服务器和负载分担逻辑,问题定位更快。
from pyrfc import Connection conn = Connection( ashost='10.20.30.40', # SAP 应用服务器地址 sysnr='00', # 实例号,两位字符串 client='100', # 客户端,三位数字 user='RFC_USER', # 有 RFC 授权的账号 passwd='YourPassword', # 密码 lang='EN', # 登录语言,中文系统可写 ZH ) print(conn.get_connection_info()) conn.close()这段代码里唯一算得上玄学的参数是sysnr。它必须是两位字符串,比如'00'、'01',写成数字0在某些场景下会拼出错误的服务名。ashost决定了目标是哪台服务器,client是 SAP 的三位客户端编号,user和passwd是通信账号,lang影响返回文本的语言,中文环境建议直接设ZH。
连接参数可以用下面的表对照检查:
| 参数 | 取值示例 | 说明 |
|---|---|---|
| ashost | 10.20.30.40 | 应用服务器 IP 或主机名 |
| sysnr | 00 | 实例号,两位字符串,端口由 3300 加实例号得到 |
| client | 100 | 三位客户端编号 |
| user / passwd | RFC_USER / *** | 需要有 RFC 授权的账号 |
| lang | EN / ZH | 登录语言,影响文本字段 |
调用完记得conn.close(),更省事的写法是用上下文管理器,with Connection(...) as conn:内部会自动关闭连接。涉及多个业务函数时,上下文管理器还能避免忘记关连接导致的句柄堆积。
3.4 用 RFC_PING 验证链路:为什么先挑它而不是直接调 BAPI
链路验证不要一上来就调 BAPI,先用RFC_PING这个无参函数做第一声招呼。
from pyrfc import Connection with Connection( ashost='10.20.30.40', sysnr='00', client='100', user='RFC_USER', passwd='YourPassword', lang='EN', ) as conn: result = conn.call('RFC_PING') print(result) # 通常是一个空 dict,只要不抛异常,链路就是通的调用成功后返回值通常是个空 dict,不要觉得空结果没意义。这一步能验证的事情很多:网络通不通、端口对不对、网关服务是否正常、SDK 加载是否成功、账号的基础认证能不能过。链路通之后并不代表账号能调业务 BAPI,那还取决于 S_RFC 授权,所以请把「先链路、再权限、最后业务参数」当成固定顺序。
4. 参数和返回值别想当然:pyrfc 的数据映射与常见误用
连通之后,真正的开发工作才开始。RFC 函数的参数体系比普通 HTTP 接口复杂,因为函数模块本身有导入、导出、变更和表参数四类,而 pyrfc 把它们全部折叠在一个返回 dict 里。不理解这层映射,排查起来会很痛苦。
4.1 RFC 调用返回的大 dict:IMPORT、EXPORT、CHANGING、TABLES 都合在同一层
先看一个典型的带输出结构的调用,以 BAPI_USER_GET_DETAIL 为例:
from pyrfc import Connection with Connection( ashost='10.20.30.40', sysnr='00', client='100', user='RFC_USER', passwd='YourPassword', lang='ZH', ) as conn: result = conn.call('BAPI_USER_GET_DETAIL', USERNAME='TECHUSER') print(result.keys()) # 输出可能包含 ADDRESS、LOGONDATA、DEFAULTS、RETURN 等结构pyrfc 把函数的所有输出统一放在一个 dict 里:结构体参数在 dict 里仍然是 dict,表参数是 list of dict,RETURN 结构照常是一个 dict。调用前一定要先在 SAP 侧的函数文档里查清楚参数名和类型,不要凭记忆猜字段。
参数名建议统一大写。SAP 函数文档里写的字段名本来就是大写,虽然 pyrfc 有时候能容忍小写,但混写键盘容易出错。这里踩的坑通常不是连不上,而是result里取值的键名拼写错误,返回一个 None 你还以为业务数据是空的。
4.2 表参数的正确读法:COMPANYCODE_LIST 这类列表的处理
RFC 函数里最常遇到的是表参数,读取方式是一个固定的套路,以读取公司代码列表为例:
with Connection( ashost='10.20.30.40', sysnr='00', client='100', user='RFC_USER', passwd='YourPassword', lang='ZH', ) as conn: result = conn.call('BAPI_COMPANYCODE_GETLIST') for row in result['COMPANYCODE_LIST']: print(row.get('COMP_CODE'), row.get('COMP_NAME'))返回的表参数在 Python 侧是 list,每一行是 dict,行内用字段名做键取值。如果函数本身要求传入表参数,传法就是构造一个 list,里面每个元素是对应表结构的 dict。多级结构是更麻烦的场景,比如一些 BAPI 需要同时维护两个字段互相对应的表,漏掉一个配套表的字段,SAP 侧就会报结构错误。
经验是先把一个最小行的结构试通,再逐步加字段。不要一次把整张业务表的几十个字段全部组装出来,那样报错时根本分不清是哪一层的问题。每次调用后都打印一遍返回 dict 的 keys,核对实际情况和函数文档是否一致。
4.3 字符串、日期和数字字段的 Python 形态:别拿字符串去比较 date
pyrfc 会根据 SAP 的数据类型做转换,返回值并不全是字符串,这个特点经常让新人翻车。
| SAP 类型 | Python 类型 | 空值表现 | 注意 |
|---|---|---|---|
| CHAR / STRING | str | None | 判断空值用is None |
| INT1 / INT2 / INT4 | int | None | 不存在的数值可能返回 None 而不是 0 |
| FLTP / DEC | float / Decimal | None | 精度敏感场景先在 SAP 侧做处理 |
| DATS | datetime.date | None | 不是字符串,不能直接拼 SQL 比较 |
| TIMS | datetime.time | None | 与 date 一样,不是字符串 |
| STRUCTURE | dict | None | 空结构可能整体是 None |
| TABLE | list | [] | 遍历用 for,别用 len 判断后继续 |
比较常见的错误是把 DATS 类型的日期字段当成字符串去拼接查询语句,正确做法是主动str(row['DATE'])做一次转换,或者直接把 date 对象交给 ORM 处理。空值判断也要注意,SAP 的空字段返回到 Python 侧可能是 None,如果你用if field:判断,0 和空字符串会被一起吞掉,而用is None才能准确区分「没有值」和「值为空串」。
5. 避坑记录:pyrfc 连 SAP 最常遇到的 5 类现场问题
前面几章是正常路径,这一章是现场的报错清单。以下问题都是实际集成过程中反复出现的,按现象、原因、解决整理,每条后面都给了可执行的检查动作。
5.1 现象:导入 pyrfc 时报 libsapnwrfc.so 找不到
现象是ImportError: libsapnwrfc.so: cannot open shared object file,常见发生在刚部署到一台新服务器时。原因是运行 Python 进程的环境里没有LD_LIBRARY_PATH指向 SDK 的 lib 目录。这里有个很多人不知道的细节:在 Python 代码里临时设置os.environ['LD_LIBRARY_PATH']是无效的,因为动态链接器早在解释器启动时就已经读取了这个变量。
解决方式是在 shell 层 export,或者用 env 命令启动脚本:
export LD_LIBRARY_PATH=/opt/sap/nwrfcsdk/lib python3 -c "from pyrfc import Connection; print('import ok')"如果这条命令能打印import ok,说明 import 阶段的问题解决了。再往下要记住 systemd、cron、supervisor 这些进程管理方式都不会自动读取 profile 文件,必须把环境变量写到各自的环境配置里,否则服务一重启,这个问题又会回来。
5.2 现象:RFC 调用一直超时,报无法连接主机
现象是连接阶段就抛 RFCError,消息里写着连接超时或无法连接目标主机。原因排第一位的是端口没放通。RFC 连接走的是 SAP 应用服务器的 33NN 端口,NN 是实例号,实例号 00 对应 3300,实例号 01 对应 3301。先确认这个端口在网络上可用:
nc -vz 10.20.30.40 3300没有 nc 的环境可以用 telnet 或者 bash 的/dev/tcp替代。命令不通,说明问题在网络层,方向是排查防火墙、安全组和路由;命令通了,说明 SAP 网关服务和账号认证方向才更可疑。如果还拿不定,把 SDK 的 trace 打开,sapnwrfc.ini里的日志会明确写到 TCP 层还是协议层失败。这个步骤能避免把网络问题误判成代码问题。
注意生产环境要按安全规范放行端口,RFC 通道建议走内网或受控网络,不要把 SAP 端口直接暴露到公网上。
5.3 现象:链路通了,但调 BAPI 报 user is not authorized
现象是RFC_PING能过,但一调业务函数就返回授权错误。原因是这个账号只有基础认证能力,没有分配 S_RFC 授权对象,或者授权范围不含目标函数所在的函数组。解决方案是让 SAP 侧为项目建一个专门的 RFC 通信账号,分配包含 S_RFC 的角色,然后按需开放函数组权限。
排查顺序上,仍然坚持先RFC_PING再调业务 BAPI 的逻辑。链路通只代表网络和协议层正常,权限问题是另一层。有些 BAPI 除了 RFC 授权还有业务数据权限,报错信息会更靠后,这时候要看返回的 RETURN 结构里的具体消息文本,而不是盯着底层异常看。
5.4 现象:import pyrfc 后直接段错误,或报 libssl/libcrypto 找不到
现象是 Python 直接 Segmentation fault,或者提示找不到libssl.so.1.1/libcrypto.so。这种问题多半是 OpenSSL 版本环境混乱造成的,比如系统升级了 OpenSSL 大版本,或者编译 pyrfc 时的头文件和运行库不一致。
解决的通用思路是先确认 pyrfc 的扩展文件到底链接了哪个库,然后看它缺什么:
python3 -c "import pyrfc, os; print(os.path.dirname(pyrfc.__file__))" find /path/to/site-packages -name 'pyrfc*.so' ldd /path/to/site-packages/pyrfc*.so | grep -E 'ssl|crypto'如果 ldd 结果里出现 not found,说明系统确实缺对应版本的 OpenSSL 库。这种问题最难缠的地方在于系统里可能同时存在多套 openssl,改环境变量容易引发其他程序故障。我见过的干净解法是用容器固定基础镜像版本,把 SDK 和匹配的 OpenSSL 一起打进镜像,绕开宿主机的环境漂移。
5.5 现象:返回的中文变问号或乱码
现象是连接正常,但字段里的中文变成问号、乱码或者缺字。原因通常不是 Python 编码问题,而是 SAP 系统的字符集设置。如果 SAP 系统本身不是 Unicode 字符集,或者登录语言lang与数据存储语言不一致,返回的文本就会在边界上出问题。
处理步骤是先确认 SAP 系统的 Unicode 属性。兼容 Unicode 的系统一般把lang从EN换成ZH就能缓解文本类字段的乱码;非 Unicode 系统则需要依赖 RFC 函数内部的编码转换字段,或者把文本先在 SAP 侧格式化一次,再返回给 Python。排查乱码时不要一上来就在 Python 里反复 encode/decode,先抓一条真实数据对照,确定是链路问题还是字符集问题,往往一次就能定位。
6. 生产习惯:部署前跑一遍自检脚本,连接参数全部走环境变量
6.1 一个 30 秒自检脚本
每次部署新环境,我不会直接跑业务脚本,而是先跑一个自检脚本。它只有一件事:验证环境变量、SDK 加载、网络连接和基础调用能力。
import os from pyrfc import Connection, RFCError host = os.getenv('SAP_ASHOST', '10.20.30.40') sysnr = os.getenv('SAP_SYSNR', '00') client = os.getenv('SAP_CLIENT', '100') lang = os.getenv('SAP_LANG', 'ZH') try: with Connection(ashost=host, sysnr=sysnr, client=client, user=os.getenv('SAP_USER'), passwd=os.getenv('SAP_PASSWD'), lang=lang) as conn: conn.call('RFC_PING') print('RFC_PING ok') except RFCError as e: print('RFCError:', e) raise SystemExit(1)脚本故意把连接参数全部从环境变量读取,密码不落在代码里。RFCError捕获的是 RFC 层的调用错误,能保证失败时退出码非零,方便接入到 CI 或部署流程中。30 秒跑完,能过就说明这条链路是可信的。
6.2 连接参数从代码里消失
连接 SAP 的信息属于敏感配置,不建议写死在 Python 文件里。开发、测试、生产环境各有一套连接目标,用环境变量区分是最直接的方式。.env文件配好后只存在于部署环境,不进版本库,密码就不会随着代码流转。
6.3 一条铁律:先链路、再权限、最后才看业务数据
最后说一条我自己的排障习惯。刚开始做 SAP 接口时,我直接跳过RFC_PING去调 BAPI,结果遇到授权错误,误以为是网络问题,来回查端口和防火墙,浪费了整整一个下午。后来我把顺序固定死:先RFC_PING验证链路,再确认账号权限,最后才是业务参数。这个习惯在部署和上线阶段帮了大忙,也建议你从第一个脚本开始就遵守。希望帮到你。
本文还有配套的精品资源,点击获取