ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

ABAP on BTP云环境:扩展设计、Sizing与性能反模式治理

ABAP on BTP云环境:扩展设计、Sizing与性能反模式治理 最近在帮客户把一个本地开发的扩展服务迁到 ABAP environmentSAP BTP 上的 ABAP 云运行时我发现团队脑子里还是十几年前的经典 ABAP 习惯代码里到处是 SELECT SINGLE、循环里调 RFC、性能问题等到压测才想起来查。这篇文章想把三件事放一起讲清楚ABAP environment 的架构到底怎么影响你的扩展设计、Sizing 该怎么估算才算不拍脑袋、上线之后性能反模式怎么一条条治理掉。适合正要上云、或者已经在云里被性能问题追着跑的 ABAP 开发者和架构师。1. ABAP environment 的架构全景先搞清楚我们在哪1.1 从经典 ABAP 到云 ABAP底层模型的关键差异很多老 ABAP 开发者第一次打开 ABAP environment 的 ADT 工程时第一反应是“我原来的 TCode 怎么都没了”。这不是功能阉割而是运行时模型变了。在经典 ABAP 应用服务器里你面对的是一组 DIA、UPD、BTC 工作进程数据库表直接可以读RFC 可以直接建dynpro 可以随便写。到了 ABAP environment底层改成了基于 Cloud Foundry 的云运行时应用进程由平台调度你不再有自由选择工作进程类型和数量的权力连一张表的物理存储都不归你直接管理。这个变化带来的核心约束是代码不再运行在“你自己的应用服务器”上而是运行在一个被平台托管的 ABAP 会话里数据库用的是独立的 HANA Cloud 实例。你通过 CDS entity、ABAP SQL、OData 服务、HTTP/RFC outbound 与外界打交道。开发模型全面转向 ABAP RESTful Application Programming ModelRAP和 CDS 语义建模传统 dynpro 和大部分经典事务码已经被云开发模型明确排除。理解这一点非常重要。因为很多“为什么云上性能差”的争议根源不是 SAP 做了手脚而是我们把经典环境的优化思路带了进来经典环境里一个 RFC 调用走的是本机或近距离的 RFC 通道开销低云环境里同样的调用可能要经历不同层的网络跳转、服务绑定、OAuth 令牌刷新成本完全不是一个量级。架构上的差异直接决定了后面设计、Sizing、性能优化的一切前提。1.2 在 BTP 中的定位应用层、数据层与集成层的协同ABAP environment 不是孤立的它只是 BTP 里的一层。你在 BTP 子账户里创建 ABAP environment 实例时同时会在背后绑定云 Foundry 环境、HANA Cloud 数据库并通过 destination、communication arrangement 打通外部系统。换句话说你写 ABAP 代码但 ABAP 只是整个分布式系统的一小段外面还挂着身份认证、API 网关、消息队列、对象存储。我建议团队把它理解成“一个封装好的业务领域服务层”。对外它暴露 OData 或 HTTP 服务对内它访问 HANA Cloud横向它通过 communication system 与 S/4HANA、其他 BTP 服务或本地系统集成。你不再需要关心工作进程怎么分配但必须关心你的应用到底需要多少并发、多少内存、多少数据库吞吐而这些正是 Sizing 要回答的问题。有一个点容易被忽略ABAP environment 不支持经典 RFC 服务器模式也就是说外部系统不能直接调你的自定义 RFC 函数模块。外部统一的入口是 OData 或 HTTP 服务。这个限制看起来很“不方便”但反过来看是一件好事——它强迫你把业务能力显式建模成 API而不是把 RFC 函数当内部后门到处乱接。我见过太多经典系统里几百个 RFC 函数彼此调用依赖关系根本理不清。云环境给了你一次重新划边界的理由。1.3 扩展模式的取舍In-App 与 Side-by-Side 到底选哪个架构上你第一个要做的决定是新功能到底放在 S/4HANA 里面做扩展还是在 ABAP environment 里做 Side-by-Side 扩展。很多团队默认“既然我熟悉 ABAP那就直接在 S/4 里加表加程序”但这里有个隐性成本S/4HANA 的核心租约contractual)限制和升级兼容性要求越来越严格往核心系统里塞大量自定义代码每次升级都可能炸出一堆兼容性问题。在 ABAP environment 里做扩展好处是环境独立升级由 SAP 托管自定义代码造成的升级风险基本隔离。代价是你必须接受它那套约束不能用经典的隐式增强、不能直接改核心表、所有数据模型要通过 CDS 自定义发布。我的建议很简单如果你的功能是 S/4HANA 主流程的微小增强且数据主体就在 S/4 里那 In-App 可能是合理选择如果是一个相对完整的业务能力、多个外部系统参与、有自己的生命周期那就放到 ABAP environment 来。这个选择直接影响后面所有工作。Say你选 Side-by-SideSizing 和性能设计就要从“我这个服务要撑多少外部调用”出发而不是“我的表有多少行”。架构决策从来不是技术洁癖而是后续所有容量规划的出发点。2. 可扩展应用的设计思路不是把旧代码搬上云就完事2.1 以 RAP 业务对象为骨架行为、数据、语义层一起建模ABAP environment 里的标准应用开发模型是 RAP。RAP 的核心思想是让你把业务对象建模成一个完整的“数据 行为”单元CDS 视图负责数据投影行为定义behavior definition负责操作和校验OData 服务负责暴露端点。为什么强调这一点因为可扩展性首先来自清晰的边界——当每个业务对象都独立成服务时你可以单独对它做性能测试、单独扩容、单独治理并发问题。我在实际项目里见过最典型的反面例子团队把迁移过来的代码堆在一个巨大的类里一个方法两千行直接拼 SQL、直接在 UPDATE 前写各种 IF 判断没有 RAP 的行为层也没有 CDS 的语义模型。结果就是每次需求改动都要动这个类性能问题也集中在这一团乱麻里根本没法按业务对象拆分治理。正确的做法是先把业务能力拆成若干 RAP 业务对象。比如一个库存扩展服务你可以拆成“库存预留”、“库存转移”、“库存盘点”三个业务对象。每个对象有自己的数据模型、行为实现、授权控制。这样一来Sizing 时可以按对象估算负载性能优化时可以按对象做针对性设计扩展时也不会把一个对象的改动波及其他对象。2.2 无状态优先与幂等设计会话管理的两个硬原则经典 ABAP 里开发者习惯在 ABAP 内存、或者通过 IMPORT/EXPORT TO MEMORY、甚至通过 SPA/GPA 参数在会话之间传递状态。但在云运行时会话和进程是由平台调度的你无法保证同一个用户的下一次请求一定落在同一个会话里。说得直接一点依赖 ABAP 会话状态的做法在 ABAP environment 里就是定时炸弹。所以可扩展应用的第一原则是尽量无状态所有业务判断需要的上下文要么从请求参数拿要么从数据库读要么从外部服务取不要指望上一次调用留下的内存状态。如果确实需要跨请求的状态比如多步骤向导把它显式存在业务表里并用一个请求 ID/会话 ID 做关联。第二原则是幂等。尤其是在接收外部系统回调或消息事件时外部系统可能因为超时重试同一个事件会发两次。如果你的服务没有幂等处理就会出现重复扣库存、重复建单这类低级但致命的问题。实现幂等不复杂在一个业务表里记录处理过的消息 ID处理前先查重处理时加锁保证并发安全。这两条原则加上去之后你的服务才能应对弹性伸缩——因为无论实例怎么扩缩、请求怎么漂移结果都一致。2.3 数据访问与批处理把计算留在数据库而不是拉回 ABAP可扩展应用在数据访问层面最容易犯的错是把“数据库该干的活”搬到应用层干。比如你需要几千行的汇总结果正确做法应该是在 CDS 视图里用聚合函数算好只把几百 KB 的结果返回反模式是 SELECT 全表到 ABAP 内表然后在 LOOP 里求和过滤。两者的资源消耗可能差一个数量级。ABAP environment 的优势在于 HANA Cloud 的计算能力很强你要学会“尽量把筛选、投影、连接、聚合下推给数据库”。这不仅是性能优化问题也是可扩展性问题数据库层面做一次集合并行计算比 ABAP 会话里一个线程慢慢循环要可扩展得多。另外凡是涉及批量更新的场景尽量使用批量操作而不是逐行操作。RAP 的行为实现天然支持批量导入FOR ALL INSTANCES你在 MODIFY 时直接传一个内表让框架一次性写库而不是在循环里单条 UPDATE。不要觉得“我把内表传进去就完事了”关键是要保证你的行为方法里没有隐式的单行处理逻辑——很多性能问题恰恰藏在“看起来是批量、实际内部一条条处理”的实现里。2.4 异步与事件驱动什么时候走队列什么时候同步返回不是所有请求都必须同步处理。很多 ABAP 开发者习惯了函数调用的同步思维遇到外部系统慢就跟着慢导致整个服务被拖垮。在分布式架构里正确的思路是把“需要立即返回结果的操作”和“可以稍后处理的操作”分开。如果一个操作要调用外部系统、执行多步骤业务逻辑、且用户不需要即时确认最终结果就别让它阻塞在 HTTP 请求里。ABAP environment 支持通过队列/异步机制处理这类场景你可以先把任务落表返回“已接收”的确认再由后台任务或事件消费者去处理。这样你的服务吞吐就不再受外部系统延迟的直接影响。但异步不是银弹它带来了新的复杂性你需要处理任务失败重试、需要保证事件顺序、需要监控积压量。我一般会这样判断如果外部调用平均响应小于 200ms 且只是查询同步没问题如果涉及写多个系统或调用链超过三个优先考虑异步。这个判断标准不是万能的但能帮你避免把整个系统做成一条同步的串行链路扩展性自然也就上来了。3. Sizing把资源估算从“拍脑袋”变成“算得清”3.1 Sizing 到底在算什么并发、吞吐、数据增长三件事很多人在做资源规划时上来就问“你能不能帮我预估一个 ABAP environment 实例够不够用”但没人能只凭一个模糊的业务描述给出靠谱答案。Sizing 的本质是把业务负载翻译成平台资源的消耗。在 ABAP environment 里资源通常按计算单元compute unit和相关内存、磁盘规格来购买不同服务计划的规格不完全一样但分析方法是一致的。你需要从三个维度来建模。第一个是并发同一时刻有多少用户在操作你的服务、有多少外部系统在调用你的 API。第二个是吞吐高峰时段每小时要处理多少请求每个请求大概要执行多少次数据库读、多少条写、多少外部调用。第三个是数据增长业务表每个月新增多少行历史数据保留多久这个直接决定了 HANA 的内存和存储需求。很多人把这三个维度混在一起谈结果要么过度估算造成成本浪费要么严重低估导致上线就崩。我建议分开建表估算同时标出峰值和均值——平台资源按峰值预留成本核算按均值评估这样至少不会出现“平均负载都正常、一到月底报表就 OOM”的尴尬。3.2 工作负载矩阵一张表把业务量换算成资源消耗我在实际项目里习惯先做一张工作负载矩阵。行是核心业务场景或 API列是频率、单次数据库读次数、单次数据量、CPU 密集程度、内存峰值、外部依赖延迟。这个矩阵不需要精确到字节但需要让团队能横向比较不同场景的资源消耗占比。业务场景每小时调用量单次SQL读次数单次返回数据量CPU密集度外部依赖库存预留查询20005030 KB低无库存转移创建30020010 KB中S/4HANA批处理对账任务10500005 MB高文件存储外部系统回写5008020 KB中无有了这张表你就能做换算。比如“库存转移创建”每小时 300 次每次 200 次 SQL 读合计 6 万次 DB 操作加上 S/4HANA 外部调用延迟可以推算出这个场景需要的数据库吞吐和网络带宽。SAP 官方在计算时通常以事务类型和用户数为基础但项目初期你很难拿到“标准用户数”这种数据工作负载矩阵反而是最贴近实际情况的估算手段。换算时需要一个参考基准。一般云平台的 compute unit 说明里会给出该规格适合的负载描述你可以把矩阵里所有场景的总吞吐量加起来除以单个计算单元的经验吞吐能力得到初始实例数。具体数值以你实际购买的服务计划为准我通常的做法是先按这个数字的 70% 利用率来预留不是把计算单元用满——因为一旦 CPU 长时间超过 70%响应时间就开始明显劣化再往上叠加业务波动性能雪崩是迟早的事。3.3 上线后的验证与校准让 Sizing 跟着监控走Sizing 不是一次性的估算它必须在系统上线后通过实际监控来校准。ABAP environment 部署到 BTP 后你可以从平台层的指标看到应用容器的 CPU 使用率、内存使用率、HTTP 响应时间以及数据库层的 HANA Cloud 资源指标。如果上线后 CPU 长期超过 80%说明初始估算偏低了如果一直不到 30%说明成本可能花多了。我更推荐在正式上线前做一次基线压测。不用特别复杂的工具选一两个核心读场景、一两个核心写场景按 Sizing 矩阵里的峰值吞吐量去压观察响应时间和资源水位。压测结果如果与估算偏差超过 20%就回头检查工作负载矩阵里哪一项估错了是调用频次高了还是单次 SQL 读次数比预想的多。还有两个容易忽略的坑。第一个是高可用与冗余如果你要求更高的可用性等级通常需要部署多个实例或环境Sizing 时必须把冗余系数算进去。第二个是数据增长带来的内存压力HANA Cloud 是列式内存数据库数据量的增长对内存的消耗往往比想象中快得多尤其是明细表。我建议在 Sizing 的初始阶段就预留未来 12 个月的数据增长空间否则你可能上线几个月后就要为加内存而重构数据模型那就被动了。4. 性能反模式治理我把踩过的坑都列给你4.1 高频反模式清单与代码级对照性能反模式这个词听起来很学术实际上就是在说“那些看起来正常、跑起来要命的代码写法”。在 ABAP environment 里我梳理了几类最高频的几乎每个迁移项目都能碰到。最常见的是 N1 查询也就是在循环里逐条读取数据库。比如你在 LOOP 里对每一行做 SELECT SINGLE本来一条查询能解决的问题变成了几百条。这在本地数据库时代尚且能忍在云环境里网络延迟和数据传输成本被放大后直接能把一个简单的查询拖成秒级响应。 反模式循环内逐行查询 LOOP AT lt_orders INTO ls_order. SELECT SINGLE status FROM zorder_status WHERE order_id ls_order-order_id INTO lv_status. ls_order-status lv_status. MODIFY lt_orders FROM ls_order. ENDLOOP. 修正一次 IN 查询批量读取 SELECT order_id, status FROM zorder_status FOR ALL ENTRIES IN lt_orders WHERE order_id lt_orders-order_id INTO DATA(lt_status). SORT lt_status BY order_id. LOOP AT lt_orders INTO ls_order. READ TABLE lt_status INTO DATA(ls_status) WITH KEY order_id ls_order-order_id. IF sy-subrc 0. ls_order-status ls_status-status. ENDIF. MODIFY lt_orders FROM ls_order. ENDLOOP.第二类是数据传输过度典型表现是 SELECT * 或者把整张大表拉到应用层再过滤。要记住ABAP environment 里的数据库是 HANA Cloud它的强项是列式存储和并行计算。你完全可以在 CDS 视图里把过滤条件、关联关系、聚合计算都定义好让数据库干活。我在代码评审时看到 SELECT * 基本是零容忍的因为它不仅浪费内存和带宽而且会让 Sizing 时对数据量规模的估算完全失真。第三类是逐行写库。有些人写批处理时习惯在 LOOP 里一条条 MODIFY但在云架构里单条数据库事务的往返开销是最大的成本之一。你应该把数据一次性收集到一个内表再用批量 MODIFY 一次提交必要时结合行为定义的 FOR ALL INSTANCES 方法实现。逐行写库不仅慢还会让数据库锁的持有时间变长并发一高就容易锁等待。第四类是循环内外部调用。在 LOOP 里一个接一个调用 HTTP destination 或 RFC 到 S/4HANA每一个调用都是秒级网络延迟几百个订单跑下来接口直接超时。正确做法是先确认外部系统是否支持批量接口支持就传一批参数如果不支持批量就要考虑并发调用但要控制并发度避免把对方系统打爆。第五类是有状态滥用。ABAP 会话里存了大量上下文导致会话无法被平台有效地复用和回收资源越堆越高。这种问题在云环境里比经典环境更难排查因为你不能像以前那样查看某个工作进程上挂着哪个用户。我通常在架构设计阶段就明确所有 OData 服务默认无状态只在极少数明确需要时才启用状态管理并设置严格的生命周期控制。4.2 怎么识别反模式从日志和指标里找证据反模式靠代码评审只能抓一部分运行时问题还得靠证据。ABAP environment 不像经典环境那样能随手开 ST05 和 SE30但你仍然有四条路拿到性能数据。第一平台层的容器监控指标包括 CPU、内存、HTTP 响应时间。如果某个服务的 CPU 使用率异常高而业务量没有明显增长大概率有低效代码在空转。第二应用日志。ABAP environment 里可以用应用日志记录每个请求的处理时间和关键步骤耗时我在写代码时习惯对核心服务做一个耗时埋点请求进来记一个时间戳数据库操作完记一个外部调用完记一个最后算出各部分占比。这比任何监控工具都直观。第三数据库层的指标。HANA Cloud 提供数据库会话数、当前 SQL 执行情况、临时内存使用等指标。如果一个服务的内存水位很高很可能不是因为业务量大而是因为某个查询把大量数据加载到了临时表。第四压测时的错误日志。压测不仅是看响应时间更要看超时和 500 错误集中发生在哪种请求上——连接池耗尽、锁冲突、外部依赖超时表现完全不同。拿到这些证据后你要做的事情是给每个反模式标注“影响面”和“优先级”。比如循环内外部调用通常是 P0因为它直接影响吞吐上限SELECT * 如果只影响一个低频报表那就是 P2。不要试图一口气修完所有问题先修影响最大的几个再回头处理细节。4.3 治理流程从反模式清单到持续改进闭环性能反模式治理不是一次性行动而是一套持续流程。我建议从三个层面来做。第一个层面是把反模式清单写进开发规范。这是最省力的手段。在我的团队里代码评审时有一张固定的对照表有没有循环内 SQL、有没有循环内外呼、有没有 SELECT *、有没有逐行写库、有没有无状态约束被破坏。评审时直接对照打钩比一遍遍口头强调“注意性能”有效得多。ABAP 环境里的检查工具也能帮你做静态扫描但人工对照清单仍然是最后的兜底。第二个层面是运行时监控和定期巡检。每个月看一次核心服务的响应时间趋势、CPU 峰值、数据库内存增长曲线。不是为了写汇报而是为了发现那些“缓慢劣化”的问题——比如某张表数据量涨了十倍导致一个原本能接受的查询变慢或者某个批处理任务随着数据量增长开始拖垮其他服务。第三个层面是每次发布前的基线回归。凡是改动核心数据模型或关键服务逻辑都要跑一遍性能回归对比改动前后的响应时间和资源消耗。这个流程听起来重但实际执行时可以很轻跑几个核心压测场景对比关键指标即可。我见过太多系统因为一次“看起来很小的改动”导致性能回退上线后才发现那时再定位成本就高了。5. 常见问题与排查技巧实录5.1 连接管理与会话泄漏为什么接口跑着跑着就超时在 ABAP environment 里做外部集成时最常遇到的问题是 HTTP destination 连接没有被正确复用或释放。你每次调用 HTTP 服务如果都新建一个客户端对象又不显式关闭最后连接池会被耗尽后续请求全部排队等待超时。这个现象在高并发压测时特别明显刚开始响应很快跑几分钟后开始大量超时CPU 占用却不高。我的排查思路分三步。先看平台的连接数指标确认是否达到上限再看代码里是否每个请求都新建了 HTTP client且没有正确 close最后检查 destination 的配置确认是否启用了连接池并设置了合适的超时时间。修起来其实很简单但很多人根本不会想到去查连接管理因为经典 ABAP 里 HTTP 调用不频繁这个问题被掩盖了。另外你自己的 ABAP 会话也可能泄漏。如果代码里创建了运行时资源、锁对象、数据库游标却没有正确处理累积起来也会拖垮整个实例。云环境里没有经典 SAP 那种后台进程管理工具所以只能靠代码规范来预防能用局部变量就不用全局的该释放的锁和游标一定要释放至于会话状态更是能不存就不存。5.2 OData 服务的深层“地狱”$expand 带来的连锁查询OData 是 ABAP environment 最主要的对外接口方式也是性能问题的重灾区。最典型的场景是前端一个列表页为了省事在请求里加了一串 $expand把主对象、明细、明细的明细、相关联的仓库信息一次性拉出来。数据库可能要执行几十上百条 SQL临时表膨胀内存飙高。我在一个项目里遇到过真实案例客户写了一个三层 $expand 的查询跑一次要 8 秒而且调用一多数据库内存水位直接告警。后来我们把查询拆成两个接口第一个只返回主对象和必要字段第二个按需加载明细再加上缓存响应时间从 8 秒降到 1.2 秒数据库压力降了一半多。这不是逼着前端多调接口而是让接口更符合业务真实使用场景。大多数列表页第一眼只需要主数据明细是用户点了某一行才需要。与其把一个巨型 payload 一次塞给前端不如拆成按需加载。还有一个小技巧是检查 CDS view 的关联定义看看有没有把不需要在 OData 里暴露的关联也带上了——很多时候性能问题根本不是代码写错而是数据模型暴露了太多不必要的数据传输路径。5.3 批处理任务的峰值抖动为什么月末总有人来投诉最后说一个特别容易忽略的问题定时批处理任务的资源竞争。你有没有想过为什么月末对账跑批的时候在线业务也跟着变慢原因很简单——批处理任务默认和在线服务共享同一个环境的资源池而批处理往往吃满 CPU 和数据库连接把在线请求挤到队列里排队。这个问题的解法有几个层次。最简单的层次是错峰把重负载批处理安排到业务低峰时段比如凌晨而不是白天。第二个层次是限流或分区把一个大任务拆成多个小任务分时段批量执行降低瞬间资源冲击。第三个层次是在架构上做隔离如果预算和技术条件允许把批处理和在线服务部署到不同的 ABAP environment 实例里彻底隔离资源这属于比较彻底的治理手段。5.4 问题排查速查表症状可能原因优先排查方向请求响应缓慢但CPU不高外部依赖延迟、连接池排队、数据库锁等待查看外部调用耗时和数据库会话状态并发一高就有大量超时连接未释放、有状态会话占用、锁粒度过大检查HTTP客户端释放、会话状态、锁边界数据库内存持续上涨查询返回数据量过大、临时表膨胀、数据增长过快检查SQL返回行数、$expand层级、Sizing模型特定接口每秒请求量低但很慢N1查询、循环内外部调用、SELECT *代码Review对照反模式清单月末批处理时在线业务变慢批处理与在线共享资源池错峰、拆分任务、环境隔离我个人在实际操作中还有一个很深的体会Sizing 不是算一次就完事性能反模式治理也不是上线前冲刺。如果你把架构设计想清楚Sizing 才有意义如果 Sizing 不跟着监控持续校准后面的反模式治理就只是在救火。每次新项目我都是先花半天把工作负载矩阵填完再花一天做基线压测之后把反模式清单挂在代码评审和发布流程里。这套流程不复杂但它让我在两个项目里避免了“上线一时爽、运维火葬场”的结局。先想清楚要支撑什么业务再谈扩展性先守住性能底线再谈新功能——这个顺序别搞反。
返回列表