Enqueue Lock in Netweaver as JAVA

本文地址: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

使用

准备

  • Design time:需要访问以下库 applocking.jar,frame.jar,exception.jar(根本没用,后面会有说明)

然而实际使用的时候发现未找到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;
  • lifetime : 生命周期
  • connection : 数据库连接
  • tableName : 表名
  • primaryKeys : 主键(可用通配符)
  • mode : 锁模式
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;
  • lifetime : 生命周期
  • name : 锁名( 必须以所属的namespace开头
  • argument : 锁参数
  • mode : 锁模式
  • timeout : 加锁冲突等待超时时间(ms)

参数

  • LIFETIME | 生命周期

    • TableLocking.LIFETIME_USERSESSION : 基于用户session,若不显式unlock会一直持续到session结束
    • TableLocking.LIFETIME_TRANSACTION : 推荐方式。依赖事务,必须激活JTA或者数据库事务,若不显式unlock会在事务结束(commit|rollback)后自动释放
  • MODE | 锁模式

    • TableLocking.MODE_SHARED : 共享锁(S Lock)
    • TableLocking.MODE_EXCLUSIVE_CUMULATIVE : 累积互斥锁(E Lock),同一个transaction中可以累积加锁。
    • TableLocking.MODE_EXCLUSIVE_NONCUMULATIVE : 非累积互斥锁(X Lock),任何时候只能锁定一次
    • *TableLocking.MODE_OPTIMISTIC* : 乐观锁(O Lock)
      与S lock类似,可以与S lock/O lock共存,与E lock/X lock互斥。
      与S lock最大区别:O lock 可以升级到R lock,当一个O lock升到R lock,其他O lock会失效
    • *TableLocking.MODE_OPTIMISTIC_TO_EXCLUSIVE* : 乐观锁升级互斥锁(R Lock)。
      必须保证存在乐观锁,否则会报错 "the lock propagation failed, because the optimistic lock is gone"
    • *TableLocking.MODE_CHECK_SHARED* : 只check不占用
    • *TableLocking.MODE_CHECK_EXCLUSIVE_CUMULATIVE* : 只check不占用
    • *TableLocking.MODE_CHECK_EXCLUSIVE_NONCUMULATIVE* : 只check不占用
  • 通配符

    • TableLocking.WILDCARD_CHARACTER_SINGLE : 代表1个字符
    • TableLocking.WILDCARD_CHARACTER_MULTI : 代表多个字符

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的浏览器环境需要加参数来区分连接,不清楚原因,否则将会出现串行处理请求的情况,无法模拟并发锁的场景

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

  • 服务路径/rest/query/lock?name=flex&mode=E&timeout=0&during=30
  • 服务路径/rest/query/lock?name=flex&mode=E&timeout=0&during=30&_r=2

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


测试截图1

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


测试截图2
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,635评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,628评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,971评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,986评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,006评论 6 394
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,784评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,475评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,364评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,860评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,008评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,152评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,829评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,490评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,035评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,428评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,127评论 2 356