2013-02-04 46 views
3

IM麻煩與使用該代碼的簡單REST服務:作爲用於同步訪問

@GET 
@Path("next/{uuid}") 
@Produces({"application/xml", "application/json"}) 
public synchronized Links nextLink(@PathParam("uuid") String uuid) { 
    Links link = null; 
    try { 
     link = super.next(); 
     if (link != null) { 
      link.setStatusCode(5); 
      link.setProcessUUID(uuid); 
      getEntityManager().flush(); 
      Logger.getLogger("Glassfish Rest Service").log(Level.INFO, "Process {0} request url : {1} #id {2} at {3} #", new Object[]{uuid, link.getLinkTxt(), link.getLinkID(), Calendar.getInstance().getTimeInMillis()}); 
     } 
    } catch (NoResultException ex) { 
    } catch (IllegalArgumentException ex) { 
    } 
    return link; 
} 

這應該提供一個鏈接對象,並且將其標記(setStatusCode(5)),以防止下一個訪問以服務發送相同的對象。問題在於,當有很多快速客戶端訪問Web服務時,這個客戶端會爲不同的客戶端提供相同鏈接對象的2或3倍。我該如何解決這個問題?

這裏使用到resquest: @NamedQuery(名稱= 「Links.getNext」,查詢= 「選擇升FROM鏈接升WHERE l.statusCode = 2」)

和super.next()梅索德:

public T next() { 

    javax.persistence.Query q = getEntityManager().createNamedQuery("Links.getNext"); 
    q.setMaxResults(1); 
    T res = (T) q.getSingleResult(); 
    return res; 
} 

THX

+4

不知道我可以回答你吞下異常的問題! –

+0

不知道這種答案可以幫助這裏的任何人...... – user1754385

回答

0

您需要使用某種形式的鎖定,很有可能是樂觀的版本鎖定。這將確保只有一個事務成功,另一個會失敗。

見, http://en.wikibooks.org/wiki/Java_Persistence/Locking

+0

thx很多,這似乎是'理論上'的好解決方案。我仍然測試所有的鎖,並希望我可以很快有一個很好的解決方案 – user1754385

+0

當我嘗試處理JPA2鎖,我有很多org.eclipse.persistence.exceptions。*(*取決於我試圖使用的鎖類型),我可以不抓住或不知道在哪裏趕上 – user1754385

4

一個(根)的生命週期JAX-RS資源是每個請求,等等nextLink方法的(否則正確的)​​關鍵字是可悲無效的。

你需要什麼是同步訪問/更新的手段。 這可以在許多方面進行:

我)你可以外部對象上同步,由一個框架(例如注射:一CDI注入@ApplicationScoped),如:

@ApplicationScoped 
public class SyncLink{ 
    private ReentrantLock lock = new ReentrantLock(); 
    public Lock getLock(){ 
     return lock; 
    } 
} 
.... 
public class MyResource{ 
    @Inject SyncLink sync; 

    @GET 
    @Path("next/{uuid}") 
    @Produces({"application/xml", "application/json"}) 
    public Links nextLink(@PathParam("uuid") String uuid) { 
    sync.getLock().lock(); 
    try{ 
     Links link = null; 
     try { 
     link = super.next(); 
     if (link != null) { 
      link.setStatusCode(5); 
      link.setProcessUUID(uuid); 
      getEntityManager().flush(); 
      Logger.getLogger("Glassfish Rest Service").log(Level.INFO, "Process {0} request url : {1} #id {2} at {3} #", new Object[]{uuid, link.getLinkTxt(), link.getLinkID(), Calendar.getInstance().getTimeInMillis()}); 
     } 
     } catch (NoResultException ex) { 
     } catch (IllegalArgumentException ex) { 
     } 
     return link; 
    }finally{ 
     sync.getLock().unlock(); 
    } 
    } 
} 

II),你可以懶惰和同步類

public class MyResource{ 
    @Inject SyncLink sync; 

    @GET 
    @Path("next/{uuid}") 
    @Produces({"application/xml", "application/json"}) 
    public Links nextLink(@PathParam("uuid") String uuid) { 
    Links link = null; 
    synchronized(MyResource.class){ 
     try { 
     link = super.next(); 
     if (link != null) { 
      link.setStatusCode(5); 
      link.setProcessUUID(uuid); 
      getEntityManager().flush(); 
      Logger.getLogger("Glassfish Rest Service").log(Level.INFO, "Process {0} request url : {1} #id {2} at {3} #", new Object[]{uuid, link.getLinkTxt(), link.getLinkID(), Calendar.getInstance().getTimeInMillis()}); 
     } 
     } catch (NoResultException ex) { 
     } catch (IllegalArgumentException ex) { 
     } 

    } 
    return link; 
    } 
} 

三)你可以同步使用數據庫。在這種情況下,您將調查JPA2中提供的悲觀鎖定。

+0

thx的幫助。 – user1754385

+0

不客氣。你選擇了哪種解決方案? –

+0

我嘗試瞭解決方案I.我現在有更少的「衝突」,即爲客戶端提供的鏈接次數更少,但鎖定效率並非100% – user1754385

0

根據您認爲爭用將在創建新Links的頻率的多少,您應該選擇使用@Version屬性的樂觀鎖定或悲觀鎖定。

我的猜測是樂觀鎖定會爲你更好地工作。無論如何,讓您的Resource類充當Service Facade並將與模型相關的代碼放入無狀態會話Bean EJB中,並通過簡單重試來處理任何OptimisticLockException。

我注意到你提到你在捕獲與鎖定有關的異常方面遇到了問題,它看起來像你正在使用Eclipselink。在這種情況下,你可以嘗試這樣的事:

@Stateless 
public class LinksBean { 

    @PersistenceContext(unitName = "MY_JTA_PU") 
    private EntityManager em; 

    @Resource 
    private SessionContext sctx; 

    public Links createUniqueLink(String uuid) { 
    Links myLink = null; 
    shouldRetry = false; 
    do { 
     try 
     myLink = sctx.getBusinessObject(LinksBean.class).createUniqueLinkInNewTX(uuid); 
     }catch(OptimisticLockException olex) { 
     //Retry 
     shouldRetry = true; 
     }catch(Exception ex) { 
     //Something else bad happened so maybe we don't want to retry 
     log.error("Something bad happened", ex); 
     shouldRetry = false; 
    } while(shouldRetry); 
    return myLink; 
    } 

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 
    public Links createUniqueLinkInNewTX(uuid) { 
     TypedQuery<Links> q = em.createNamedQuery("Links.getNext", Links.class); 
     q.setMaxResults(1); 
     try { 
     myLink = q.getSingleResult(); 
     }catch(NoResultException) { 
     //No more Links that match my criteria 
     myLink = null; 
     } 
     if (myLink != null) { 
     myLink.setProcessUUID(uuid); 
     //If you change your getNext NamedQuery to add 'AND l.uuid IS NULL' you 
     //could probably obviate the need for changing the status code to 5 but if you 
     //really need the status code in addition to the UUID then: 
     myLink.setStatusCode(5); 
     } 
     //When this method returns the transaction will automatically be committed 
     //by the container and the entitymanager will flush. This is the point that any 
     //optimistic lock exception will be thrown by the container. Additionally you 
     //don't need an explicit merge because myLink is managed as the result of the 
     //getSingleResult() call and as such simply using its setters will be enough for 
     //eclipselink to automatically merge it back when it commits the TX 
     return myLink; 
    } 
} 

Your JAX-RS/Jersey Resource class should then look like so: 

@Path("/links") 
@RequestScoped 
public class MyResource{ 
    @EJB LinkBean linkBean; 

    @GET 
    @Path("/next/{uuid}") 
    @Produces({"application/xml", "application/json"}) 
    public Links nextLink(@PathParam("uuid") String uuid) { 
    Links link = null; 
    if (uuid != null) { 
     link = linkBean.createUniqueLink(uuid); 
     Logger.getLogger("Glassfish Rest Service").log(Level.INFO, "Process {0} request url : {1} #id {2} at {3} #", new Object[]{uuid, link.getLinkTxt(), link.getLinkID(), Calendar.getInstance().getTimeInMillis()}); 
     } 
    return link; 
    } 
} 

這是一種方法對皮膚這隻貓的半拋光的例子,還有很多怎麼回事。如果您有任何問題,請告訴我。

此外,從REST結束的事情中,您可能會考慮使用@PUT代替@GET,因爲您的端點具有更新(UUID和/或statusCode)資源狀態的副作用,而不是簡單地獲取它。