Enqueue Lock in Netweaver as JAVA

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

参考

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值