并发执行解决ABAP程序效能问题

本文介绍当常规ABAP程序效能优化方法无法解决问题时的并发执行方法。阐述了SAP系统构成、系统吞吐量相关参数及极限值,指出确定可用线程的可靠方法,还介绍了使用SM51查看进程情况,以及用aRFC做并行处理的关键字。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

曾经我在我的一篇文章中讲述的ABAP程序效能优化的一些方法,ABAP程序优化_SAP小白kenny的博客-CSDN博客

但是如果这些方法依旧解决不了我们的问题,本文就介绍了另一种方法。

并发执行:我曾经在一篇文章中介绍了SAP系统的构成,SAP 中System ID / Aplication server / Instance / Client的区别_SAP小白kenny的博客-CSDN博客,了解到一个服务器PRD会拥有多个组,每个组又会拥有多个实例,用户进程是分散在这多个实例上的,并不是一个实例只有一个线程。

系统的吞吐量对CPU的消耗,外部接口,IO等等紧密联系,吞吐量的几个重要参数:QPS(TPS),每秒钟响应request/事务的数量、并发数,同时间处理的request的线程数量、响应时间,平均响应时间。

系统吞吐量通常由QPS(TPS)决定、并发数决定,每套系统都有两个极限值,当一项达到最大值,吞吐量就上不去了,当压力继续增大时,吞吐量反而会下降。

多线程并发执行很重要的一点是确定有多少可用线程,这一点很多文章都表达是RZ12中的最大请求数,但是这里的是百分比,所以我觉得并不可靠。

但是后面了解到的另一种方法我认为更加靠谱:

1、选择合适的组:一般选择的都是:parallel_generators ,当然好像不是每个系统都会有这个组,所以最好在RZ12里面选择一个。

2、清空组里面的线程,得到可用线程数。

3、多线程并发执行并不是按照调用循序执行的,线程执行的先后顺序也无法保证,请注意这一点。

使用SM51可以查看到所有的的AS instance,双击(Tx: SM50)可进入查看该instance上进程的使用情况。

使用aRFC做并行处理,其关键字为

1) STARTING NEW TASK task_name, 这句话会开启一个新的dialog process
2) DESTINATION IN GROUP group_name, 这句话指定dialog process运行在哪个server group上,若使用DEFAULT,则会随机分配一个空闲的AS instance上
3) PERFORMING call_back ON END OF TASK, 异步进程结束后的回调函数
4) RECEIVE RESULTS FROM FUNCTION function_name, 用于接收异步进程处理返回的结果

REPORT ytest.

CLASS zcl_thread_handler DEFINITION.

  PUBLIC SECTION.
    TYPE-POOLS abap .

    CONSTANTS:
      c_default_group TYPE rzlli_apcl VALUE 'parallel_generators', "#EC NOTEXT
      c_task          TYPE char6 VALUE 'PARALL'.            "#EC NOTEXT

    METHODS:
      all_threads_are_finished
        RETURNING
          VALUE(r_empty) TYPE abap_bool,
      clear_thread
        IMPORTING
          !i_task TYPE char8,
      constructor
        IMPORTING
          !i_task_prefix TYPE char6 DEFAULT c_task
          !i_threads     TYPE i
          !i_group       TYPE rzlli_apcl DEFAULT c_default_group,
      handle_resource_failure,
      get_free_thread
        RETURNING
          VALUE(r_thread) TYPE char8 .

  PROTECTED SECTION.

  PRIVATE SECTION.

    TYPES:
      BEGIN OF ty_thread,
        thread TYPE char8,
        used   TYPE abap_bool,
      END OF ty_thread .

    DATA:
      task_prefix  TYPE char6,
      threads_list TYPE TABLE OF ty_thread WITH DEFAULT KEY,
      threads      TYPE i,
      used_threads TYPE i,
      group        TYPE rzlli_apcl.

    METHODS get_free_threads
      RETURNING
        VALUE(r_free_threads) TYPE i .
ENDCLASS.



CLASS zcl_thread_handler IMPLEMENTATION.

  METHOD get_free_threads.
    " Get number of free threads

    CALL FUNCTION 'SPBT_INITIALIZE'
      EXPORTING
        group_name                     = me->group
      IMPORTING
        free_pbt_wps                   = r_free_threads
      EXCEPTIONS
        invalid_group_name             = 1
        internal_error                 = 2
        pbt_env_already_initialized    = 3
        currently_no_resources_avail   = 4
        no_pbt_resources_found         = 5
        cant_init_different_pbt_groups = 6
        OTHERS                         = 7.

    CASE sy-subrc.
      WHEN 0. " Do nothing

      WHEN 3.
        " Already initialised - get current number of free threads
        CALL FUNCTION 'SPBT_GET_CURR_RESOURCE_INFO'
          IMPORTING
            free_pbt_wps                = r_free_threads
          EXCEPTIONS
            internal_error              = 1
            pbt_env_not_initialized_yet = 2
            OTHERS                      = 3.

        IF sy-subrc IS NOT INITIAL.
          " Something has gone seriously wrong, so end it here.
          MESSAGE ID sy-msgid TYPE 'X' NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
        ENDIF.

      WHEN OTHERS.
        " Something has gone seriously wrong, so end it here.
        MESSAGE ID sy-msgid TYPE 'X' NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.

    ENDCASE.
  ENDMETHOD.

  METHOD all_threads_are_finished.
    r_empty = xsdbool( used_threads EQ 0 ).
  ENDMETHOD.

  METHOD clear_thread.
    READ TABLE me->threads_list WITH KEY used = abap_true thread = i_task
         ASSIGNING FIELD-SYMBOL(<thread>).
    <thread>-used = abap_false.
    SUBTRACT 1 FROM used_threads.
  ENDMETHOD.


  METHOD constructor.
    DATA: g_applserver   TYPE rzllitab-applserver.

    me->group = i_group.
    me->task_prefix = i_task_prefix.

    CALL 'C_SAPGPARAM' ID 'NAME' FIELD 'rdisp/myname'
                       ID 'VALUE' FIELD g_applserver.

    SELECT SINGLE classname FROM   rzllitab
               INTO me->group   "Server Group Name
              WHERE applserver = g_applserver
              AND classname = me->group
              AND grouptype = 'S'.   "S:服务器组,空:登陆组

    IF sy-subrc <> 0.
*      SELECT SINGLE classname FROM   rzllitab
*             INTO me->group   "Server Group Name
*            WHERE applserver = g_applserver
*            AND grouptype = 'S'.   "S:服务器组,空:登陆组
      me->group = 'sapdev'.
    ENDIF.

    " No more than 100 threads
    IF i_threads GT 100.
      me->threads = 100.
    ELSEIF i_threads LE 0.
      me->threads = 1.
    ELSE.
      me->threads = i_threads.
    ENDIF.

    DATA(free_threads) = me->get_free_threads( ).

    " Ensure that no more than half of the free threads are used
    free_threads = free_threads / 2 + 1.
    IF free_threads LT me->threads.
      me->threads = free_threads.
    ENDIF.

    " Initialise threads
    DO me->threads TIMES.
      DATA threadn TYPE n LENGTH 2 VALUE '00'.
      INSERT VALUE #( thread = me->task_prefix && threadn used = abap_false )
             INTO TABLE me->threads_list.
      ADD 1 TO threadn.
    ENDDO.
  ENDMETHOD.

  METHOD handle_resource_failure.
    DATA(free_threads) = me->get_free_threads( ).
    IF free_threads LE 1 AND me->threads GT 1.
      SUBTRACT 1 FROM me->threads.
    ENDIF.

    WAIT UP TO 5 SECONDS. " Long enough for the system to update
    WAIT UNTIL me->used_threads LT me->threads. " Now there's an available thread
  ENDMETHOD.

  METHOD get_free_thread.
    " Wait for a free thread
    WAIT UNTIL me->used_threads LT me->threads.

    " Get number of first free thread
    READ TABLE me->threads_list WITH KEY used = abap_false ASSIGNING FIELD-SYMBOL(<thread>).

    ADD 1 TO used_threads.
    <thread>-used = abap_true.
    r_thread = <thread>-thread.
  ENDMETHOD.

ENDCLASS.

INCLUDE bdcrecxy.
DATA:   messtab LIKE bdcmsgcoll OCCURS 0 WITH HEADER LINE.
DATA: BEGIN OF ls_itab,
        line(10) TYPE c,
      END OF ls_itab,
      lt_itab LIKE TABLE OF ls_itab.

DATA: g_taskname(10) TYPE c,
      g_classname    TYPE rzlli_apcl,
      g_applserver   TYPE rzllitab-applserver,
      excp_flag(1)   TYPE c.

DATA: snd_job          TYPE i,
      open_task_num    TYPE i,
      rcv_jobs         TYPE i,
      functioncall1(1) TYPE c.
DATA: lv_line TYPE i.

START-OF-SELECTION.

  lv_line = '30'.

  DATA: lo_parallel TYPE REF TO zcl_thread_handler.

  CREATE OBJECT lo_parallel
    EXPORTING
      i_task_prefix = 'Task'
      i_threads     = lv_line
      i_group       = 'parallel_generators'.

  DO 30 TIMES.

    DATA(thread) = lo_parallel->get_free_thread( ).
    g_taskname = thread.

    CALL FUNCTION 'YTEST_001'
      STARTING NEW TASK g_taskname
      DESTINATION IN GROUP g_classname
      PERFORMING frm_subroutine_done ON END OF TASK
      EXPORTING
        iv_input    = g_taskname
      EXCEPTIONS
        error_found = 1
        OTHERS      = 2.

  ENDDO.

  WAIT UNTIL lo_parallel->all_threads_are_finished( ).

*&---------------------------------------------------------------------*
*&      Form  FRM_SUBROUTINE_DONE
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
*  -->  p1        text
*  <--  p2        text
*----------------------------------------------------------------------*
FORM frm_subroutine_done USING g_taskname.
  DATA: lv_taskname TYPE char10.
  rcv_jobs = rcv_jobs + 1.

  RECEIVE RESULTS FROM FUNCTION 'YTEST_001'
    IMPORTING
      ev_output = lv_taskname
    EXCEPTIONS
      material_plant_not_found = 1
      plant_not_found          = 2
      OTHERS                   = 3.
  " Free the thread for the next thread to run
  lo_parallel->clear_thread( CONV char8( g_taskname ) ).
  WRITE: lv_taskname.
ENDFORM.                    " FRM_SUBROUTINE_DONE

Parallel processing made easy | SAP Blogs

[ ABAP ] - 使用异步RFC实现并行处理_SAP技术博客-CSDN博客

*&---------------------------------------------------------------------*
*& Form FRM_EXECUTE_PARALLEL
*&---------------------------------------------------------------------*
*& text
*&---------------------------------------------------------------------*
*& -->  p1        text
*& <--  p2        text
*&---------------------------------------------------------------------*
FORM frm_execute_parallel .

  CLEAR:gv_application,gv_free_threads,gv_index.

  " 获取RFC Server group name
  CALL 'C_SAPGPARAM'
    ID 'NAME' FIELD 'rdisp/myname'
    ID 'VALUE' FIELD gv_application.

  SELECT SINGLE classname FROM rzllitab INTO gv_classname
    WHERE applserver = gv_application
    AND grouptype = 'S'.

  CALL FUNCTION 'SPBT_INITIALIZE'
    EXPORTING
      group_name                     = gv_classname
    IMPORTING
      free_pbt_wps                   = gv_free_threads
    EXCEPTIONS
      invalid_group_name             = 1
      internal_error                 = 2
      pbt_env_already_initialized    = 3
      currently_no_resources_avail   = 4
      no_pbt_resources_found         = 5
      cant_init_different_pbt_groups = 6
      OTHERS                         = 7.

  gv_free_threads = gv_free_threads - 3. " 并发数

  IF gv_lines < gv_free_threads.
    gv_free_threads = gv_lines.
  ENDIF.

  LOOP AT gr_store INTO DATA(wa_store).
    ADD 1 TO gv_index.

    APPEND wa_store TO gr_store_single.

    DATA(rv_uuid) = cl_system_uuid=>if_system_uuid_static~create_uuid_c32( ).

    CALL FUNCTION 'ZSD_GET_TRANSACTION_REMOTE'
      STARTING NEW TASK rv_uuid
      DESTINATION IN GROUP gv_classname
      PERFORMING frm_subroutine_done ON END OF TASK " subrouting
      TABLES
        it_date        = s_date[]
        it_partner     = gr_store_single
      EXCEPTIONS
        internal_error = 1
        check_failed   = 2
        no_data        = 3
        OTHERS         = 4.

    IF sy-subrc EQ 0.
      gv_snd_jobs = gv_snd_jobs + 1.
    ENDIF.
    IF gv_index EQ gv_lines.
      "获取并发进程返回的结果
      WAIT UNTIL gv_snd_jobs = 0.
    ELSE.
      IF gv_index GE gv_free_threads.
        WAIT UNTIL gv_snd_jobs LT gv_free_threads.
      ENDIF.
    ENDIF.

  ENDLOOP.

ENDFORM.

FORM frm_subroutine_done USING g_taskname.
*  gv_rcv_jobs = gv_rcv_jobs + 1.  ""Receiving data
  RECEIVE RESULTS FROM FUNCTION 'ZSD_GET_TRANSACTION_REMOTE'
    EXCEPTIONS
      internal_error = 1
      check_failed   = 2
      no_data        = 3
      OTHERS         = 4.

  gv_snd_jobs = gv_snd_jobs - 1.

ENDFORM.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值