Java 简单的基于数据库的实例同步
我正在开发一个在java应用服务器场中运行的服务,它需要做一些定期的工作(比如,每2分钟一次)。服务必须与外部实体接口,并且有必要同步不同的实例,以便在给定的时间只有一个实例在作业中工作。由于该服务在此作业期间使用DB,因此我考虑基于一个简单的DB表实现同步:Java 简单的基于数据库的实例同步,java,database,jakarta-ee,synchronization,Java,Database,Jakarta Ee,Synchronization,我正在开发一个在java应用服务器场中运行的服务,它需要做一些定期的工作(比如,每2分钟一次)。服务必须与外部实体接口,并且有必要同步不同的实例,以便在给定的时间只有一个实例在作业中工作。由于该服务在此作业期间使用DB,因此我考虑基于一个简单的DB表实现同步: id, owner, stamp 其中id是锁的id,owner是当前所有者,stamp是锁的锁定时间 这些方法将是: tryLock(id, maxAge, owner) - to try to lock a record or br
id, owner, stamp
其中id是锁的id,owner是当前所有者,stamp是锁的锁定时间
这些方法将是:
tryLock(id, maxAge, owner) - to try to lock a record or break an old record
refresh(id, owner) - to update the stamp to signal we're still around working on the job
release(id, owner) - to release the lock
您将如何实现这一点
编辑:删除了我的实现,我将把它作为一个“答案”发布。我提出了以下实现,但我不确定它是否能处理所有紧急情况(我也不完全确定我是否正确使用了BeanManagedTransaction)。另外,如果您认为这个同步问题可以用一种更简单的方法来处理,请给我指出正确的方向
@Service(objectName=Sync.EjbName)
@Management(SyncMgt.class)
@TransactionManagement(value=TransactionManagementType.BEAN)
public class SyncSvc implements SyncMgt {
@PersistenceContext
protected EntityManager entityManager_;
@Resource
protected UserTransaction utx_;
@TransactionAttribute(TransactionAttributeType.REQUIRED)
private boolean update(SyncRecord sr, String owner) {
Date stamp = (owner != null) ? new Date() : null;
Query q;
if (sr.getOwner() != null) {
q = entityManager_.createQuery("UPDATE SyncRecord sr SET sr.owner = :newOwner, sr.stamp = :stamp WHERE sr.id = :id AND sr.owner = :origOwner AND sr.stamp = :origStamp");
q.setParameter("origOwner", sr.getOwner());
q.setParameter("origStamp", sr.getStamp()); // make it fail if someone refreshed in the meantime
}
else {
q = entityManager_.createQuery("UPDATE SyncRecord sr SET sr.owner = :newOwner, sr.stamp = :stamp WHERE sr.id = :id AND sr.owner IS NULL");
}
q.setParameter("id", sr.getId());
q.setParameter("newOwner", owner);
q.setParameter("stamp", stamp);
int res = q.executeUpdate();
if (res != 1) {
return false;
}
return true;
}
@TransactionAttribute(TransactionAttributeType.REQUIRED)
private boolean tryLockImpl(String id, long maxAge, String owner) {
SyncRecord sr = entityManager_.find(SyncRecord.class, id);
if (sr == null) {
// no record yet, create one
sr = new SyncRecord(id, owner);
sr.touch();
entityManager_.persist(sr);
entityManager_.flush();
return true;
}
// found a SyncRecord, let's see who owns it
if (owner.equals(sr.getOwner())) {
// log some warning, re-locking old lock, should use refresh instead
return update(sr, owner);
}
if (sr.getOwner() == null) {
// sr is not held by anyone, safe to grab it
return update(sr, owner);
}
// someone else holds it, let's check the age
if (maxAge >= 0) {
long maxAgeStamp = System.currentTimeMillis() - maxAge;
if (sr.getStamp().getTime() < maxAgeStamp) {
if (update(sr, owner)) {
return true;
}
return false;
}
}
return false;
}
// Sync impl:
/**
* Try to lock "id" for "owner"
* If the lock is held by someone else, but is older than maxAge, break it
*/
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public boolean tryLock(String id, long maxAge, String owner) {
if (id == null)
throw new IllegalArgumentException("id is null");
try {
utx_.begin();
if (tryLockImpl(id, maxAge, owner)) {
utx_.commit();
return true;
}
}
catch (EntityExistsException e) {
// failed to lock, someone beat us to it
}
catch (Throwable e) {
// some fishy error, raise alarm, log, etc
}
try {
utx_.rollback();
}
catch (Throwable e) {
// log the error, not much else we can do at this point
}
return false;
}
/**
* Refresh lock "id" belonging to "owner" (update its stamp)
*/
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public boolean refresh(String id, String owner) {
if (id == null)
throw new IllegalArgumentException("id is null");
try {
utx_.begin();
SyncRecord sr = entityManager_.find(SyncRecord.class, id);
if (sr == null || !owner.equals(sr.getOwner())) {
utx_.rollback();
return false;
}
if (update(sr, owner)) {
utx_.commit();
return true;
}
}
catch (Throwable e) {
// some fishy error, raise alarm, log, etc
}
try {
utx_.rollback();
}
catch (Throwable e) {
// log the error, not much else we can do at this point
}
return false;
}
/**
* release lock "id" held by "owner"
*/
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void release(String id, String owner) {
if (id == null)
throw new IllegalArgumentException("id is null");
try {
utx_.begin();
SyncRecord sr = entityManager_.find(SyncRecord.class, id);
if (sr == null || !owner.equals(sr.getOwner())) {
// we don't own it
utx_.rollback();
return;
}
if (update(sr, null)) {
utx_.commit();
return;
}
}
catch (Throwable e) {
// some fishy error, raise alarm, log, etc
}
try {
utx_.rollback();
}
catch (Throwable e) {
// log the error, not much else we can do at this point
}
}
// LifeCycle impl:
public void start() {}
public void stop() {}
}
@Service(objectName=Sync.EjbName)
@管理(同步管理类)
@TransactionManagement(值=TransactionManagementType.BEAN)
公共类SyncSvc实现SyncMgt{
@持久上下文
受保护的实体管理器实体管理器;
@资源
受保护的用户事务utx;
@TransactionAttribute(TransactionAttributeType.REQUIRED)
私有布尔更新(SyncRecord sr,字符串所有者){
日期戳=(所有者!=null)?新日期():null;
查询q;
if(sr.getOwner()!=null){
q=entityManager_u2;.createQuery(“更新SyncRecord sr SET sr.owner=:newOwner,sr.stamp=:stamp,其中sr.id=:id和sr.owner=:origOwner和sr.stamp=:origStamp”);
q、 setParameter(“origOwner”,sr.getOwner());
q、 setParameter(“origStamp”,sr.getStamp());//如果有人同时刷新,则使其失败
}
否则{
q=entityManager_u2;.createQuery(“更新SyncRecord sr SET sr.owner=:newOwner,sr.stamp=:stamp,其中sr.id=:id和sr.owner为空”);
}
q、 setParameter(“id”,sr.getId());
q、 setParameter(“newOwner”,owner);
q、 setParameter(“stamp”,stamp);
int res=q.executeUpdate();
如果(res!=1){
返回false;
}
返回true;
}
@TransactionAttribute(TransactionAttributeType.REQUIRED)
私有布尔tryLockImpl(字符串id、长最大值、字符串所有者){
SyncRecord sr=entityManager_u2;u2.find(SyncRecord.class,id);
if(sr==null){
//还没有记录,创建一个
sr=新的同步记录(id,所有者);
sr.touch();
实体管理器(sr);
entityManager_u2;u.flush();
返回true;
}
//找到一条同步记录,让我们看看谁拥有它
if(owner.equals(sr.getOwner())){
//记录一些警告,重新锁定旧锁,应改用刷新
返回更新(sr、所有者);
}
if(sr.getOwner()==null){
//sr不被任何人持有,可以安全地抓住它
返回更新(sr、所有者);
}
//其他人拿着它,让我们检查一下年龄
如果(最大年龄>=0){
long maxAgeStamp=System.currentTimeMillis()-maxAge;
if(sr.getStamp().getTime()