Enqueue Lock in Netweaver as JAV

2019-08-15  本文已影响0人  Flex_Frank

本文地址:https://flexibility.github.io/MD/SAP/Java/01-Enqueue-Lock-in-NW-as-JAVA

[TOC]

参考

SAP Help Portal : Developing Java EE 5 Applications/Task/Developing Persistence/Locks

概念

锁和事务隔离

锁的概念和 事务隔离( transaction isolation )相关。

事务隔离 是为了规避多个并发(simultaneously running)的事务不被相互影响。隔离的级别通过对数据库表的行的读和改的锁定来实现

锁定机制

有以下两种锁定机制:

  1. Database locks。通过数据库提供的锁机制来实现,缺点是不同的数据库对锁的语义(semantics)没有统一标准。
  2. Logical locks。锁定机制由NW as Java提供和集中管理( Enqueue Server 通过集中的lock table进行管理)。
    J2EE应用使用由 Locking Adapter Service 提供的LogicalLockingTableLocking接口来访问 Locking Manager 来和 Enqueue Server 进行通信

Enqueue Server

Enqueue Server 不和持久层存储介质( persistent storage )交互,如DB,文件系统等;只和主存中的 central lock table 进行通信
每一次锁定请求就有一次应用和 Enqueue Server 之间的通信。合适的 锁定粒度(granularity)可以减少网络通信,比如使用通配符(wildcard character)来锁定范围数据。

read/write/shared/exclusive locks

使用逻辑锁可以锁定单行数据或者区域数据。分为读锁(write locks)和 写锁(write locks)。

对于一个对象,最多可以存在一个写锁读锁 可以存在多个。写锁读锁 是互斥(mutually exclusive)的。

因此,读锁 也叫 共享锁(shared locks), 写锁 也叫 排他锁(exclusive locks)

Enqueue Locks Guidelines

Enqueue Locks Guidelines

  • Enqueue locking applies both to the SAP NetWeaver AS for Java system database and external databases supported by SAP NetWeaver AS for Java.
  • All application components must rely on enqueue locking. Locking works if all components rely on the same locking principle.
  • All application components must commit to apply the same locking protocol.
  • With enqueue locking, JDBC data sources must operate on the lowest, READ_UNCOMMITTED transaction isolation level.
  • Enqueue locking works with Open SQL, Native SQL, and Vendor SQL.
  • To obtain an enqueue lock, you must have an active JTA or database transaction
  • We recommend that you choose and use one persistence API consistently:

    With ORM technologies such as EJB CMP and JDO, you do not need to set locks explicitly. Both frameworks implicitly lock persistent objects by default.

    In all other cases, you set and release enqueue locks using the interfaces of the Application Locking Service:
    • To lock specific database items you use the TableLocking API.
    • In all other cases, you use the LogicalLocking API.
  • We recommend that you set locks only when necessary and for as short a time as possible. To release locks you use one of the following approaches:
    • To release locks in the application, you use the dedicated methods of the TableLocking and LogicalLocking APIs.
    • Locks exist within the scope of a transaction. When a transaction ends, the AS Java automatically releases the locks set during the transaction.
  • To manage, test, and view statistics about enqueue locks, you use the Application Locking Service.

More information: Managing Locks

使用

准备

然而实际使用的时候发现未找到frame.jar,applocking.jar之间报过时,exception.jar增加了命名空间,变成了com.sap.exception。
代码根本编译不了,然后看了下700~750的文档,发现这块的文档基本都没变过,还是这几个lib,估计基本都放弃更新文档了,毕竟。。。

经过不停的尝试,最终发现 tc/je/locking/apitc/bl/exception/lib 这两个service对dc可以。

DC依赖

DC

jar包

jar包

API

APIs

TableLocking API
public void lock(
    byte lifetime, 
    Connection connection, 
    String tableName, 
    Map primaryKeys, 
    char mode
) 
throws  LockException, 
        TechnicalLockException,
        IllegalArgumentException;
LogicalLocking API
public void lock(
    byte lifetime,
    java.lang.String name,
    java.lang.String argument,
    char mode,
    int timeout
)
throws  com.sap.engine.frame.core.locking.LockException,
        com.sap.engine.frame.core.locking.TechnicalLockException,
        java.lang.IllegalArgumentException;

参数

DEMO(LogicalLock)

package flex.test.rest;

import java.sql.Connection;
import java.util.Map;

import javax.naming.InitialContext;
import javax.transaction.UserTransaction;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

import afce.base.ejb.common.type.FlexMap;
import afce.base.ejb.util.DBUtil;

import com.ibm.basis.sqldao.executor.DBConnection;
import com.sap.engine.frame.core.locking.LockException;
import com.sap.engine.services.applocking.LogicalLocking;
import com.sap.engine.services.applocking.LogicalLockingFactory;


@Path("/query")
public class TestRestCFG {

    @GET
    @Path("/lock")
    @Produces("application/json;charset=utf-8")
    public Map<String,Object> test(
            @DefaultValue("test01") @QueryParam("name") String p_lock_name,
            @DefaultValue("E") @QueryParam("mode") String p_lock_mode,
            @DefaultValue("1000") @QueryParam("timeout") int p_lock_timeout,
            @DefaultValue("30") @QueryParam("during") int p_lock_during
    ){
        
        char c_lock_mode = (p_lock_mode==null||p_lock_mode.length()<1)?'E':p_lock_mode.charAt(0);
//      E - LogicalLocking.MODE_EXCLUSIVE_CUMULATIVE
//      X - LogicalLocking.MODE_EXCLUSIVE_NONCUMULATIVE
//      S - LogicalLocking.MODE_SHARED
//      O - LogicalLocking.MODE_OPTIMISTIC
//      R - LogicalLocking.MODE_OPTIMISTIC_TO_EXCLUSIVE
        
        FlexMap<String, Object> oret = FlexMap.SO();
        
        String lock_NS = "Flex.lock";
        String lock_name = lock_NS + "." + p_lock_name;
        String lock_para = "flex01";

        
        UserTransaction ut = null;
        Connection conn = null;
        try {

            conn = DBConnection.getConnection();
            ut = DBUtil.getUT(conn);
            ut.begin();
        
        InitialContext ctx = new InitialContext();
            LogicalLockingFactory lf = (LogicalLockingFactory) ctx.lookup(LogicalLockingFactory.JNDI_NAME);
            LogicalLocking ll = lf.createLogicalLocking(lock_NS, "Flex Lock NS");
            oret.put("before lock", "------"+new java.util.Date().toString());
            oret.put("LOCK_NAME", lock_name)
                    .put("LOCK_PARA", lock_para)
                    .put("LOCK_MODE", p_lock_mode)
                    .put("LOCK_TIMEOUT", p_lock_timeout)
                    .put("LOCK_DURING", p_lock_during)
                    ;

            ll.lock(
                    LogicalLocking.LIFETIME_TRANSACTION, 
                    lock_name, lock_para, 
//                  LogicalLocking.MODE_EXCLUSIVE_CUMULATIVE
                    c_lock_mode
                    ,p_lock_timeout
            );

            oret.put("after lock", "------"+new java.util.Date().toString());
            try {java.util.concurrent.TimeUnit.SECONDS.sleep(p_lock_during);}
            catch (InterruptedException e) {e.printStackTrace();}
            oret.put("last", "------"+new java.util.Date().toString());

        }
        catch (LockException e) {
            oret.put("exception", 
                    "请求的锁对象<"+lock_name+">"+"正在被【"+
                    e.getCollisionUserName()+
                    "】锁定!"+
                    "("+lock_para+")"
            );
            oret.put("exception-msg",e.getMessage()); 
        }
        catch (Exception e) {
            oret.put("exception", e.getMessage()+new java.util.Date().toString());
        }
        finally{DBConnection.freeConnection(conn);}
        
        return oret.o();
    }
    
}

提供了rest服务进行锁定,如果锁定成功,将会占用30秒,如果未占用成功则直接返回报错信息.
*https://IP端口/应用/rest/query/lock?name=flex&mode=E&timeout=0&during=30

测试的时候,如果在一个session的浏览器环境需要加参数来区分连接,不清楚原因,否则将会出现串行处理请求的情况,无法模拟并发锁的场景

可以增加参数来区分连接:

测试截图:
打开2个互斥锁


测试截图1

打开3个共享锁后打开1个互斥锁


测试截图2
上一篇下一篇

猜你喜欢

热点阅读