曾经我在我的一篇文章中讲述的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.