如何在Grails中同时处理更多请求的REST Web服务?

时间:2011-12-18 12:55:07

标签: web-services rest grails

我开发了REST Web服务,它能够接受一些数据,从数据库加载用户,稍微更改一下并将其保存回数据库。

以下是REST Web服务的内容:

class RespondentController {
  static allowedMethods = [save: "GET"]
  synchronized def save = {
    Campaign.withTransaction { status ->
       User user = User.get(params['userId'])
       // do some changes in users...
       user.save()
    }
  }
}

问题是当同时有更多请求时。有没有办法改变Web服务,以便它可以同时接受更多的请求(可能是一些配置)?

2011-12-18 10:28:57,808 [http-8080-5] ERROR errors.GrailsExceptionResolver  - Exception occurred when processing request: [GET] /my-rest-ws/respondent
Stacktrace follows:
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [my.app.User#1]
        at my.app.UserController$_closure1.doCall(RespondentController.groovy:19)
        at my.app.UserController$_closure1.doCall(RespondentController.groovy)
        at java.lang.Thread.run(Thread.java:636)
2011-12-18 10:29:02,625 [http-8080-5] ERROR events.PatchedDefaultFlushEventListener  - Could not synchronize database state with session
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [my.app.User#1]
        at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:1792)
        at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2435)
        at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2335)
        at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2635)
        at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:115)
        at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:279)
        at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:263)
        at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:168)
        at org.codehaus.groovy.grails.orm.hibernate.events.PatchedDefaultFlushEventListener.performExecutions(PatchedDefaultFlushEventListener.java:46)
        at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50)
        at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1027)
        at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:365)
        at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:137)
        at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:656)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
        at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:147)
        at sun.reflect.GeneratedMethodAccessor773.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:616)
        at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoCachedMethodSite.invoke(PojoMetaMethodSite.java:188)
        at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:52)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:124)
        at org.codehaus.groovy.grails.plugins.orm.hibernate.HibernatePluginSupport$_addTransactionalMethods_closure22.doCall(HibernatePluginSupport.groovy:502)
        at sun.reflect.GeneratedMethodAccessor666.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:616)
        at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
        at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
        at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1058)
        at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:1070)
        at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:886)
        at groovy.lang.Closure.call(Closure.java:282)
        at org.codehaus.groovy.runtime.metaclass.ClosureStaticMetaMethod.invoke(ClosureStaticMetaMethod.java:59)
        at org.codehaus.groovy.runtime.callsite.StaticMetaMethodSite$StaticMetaMethodSiteNoUnwrapNoCoerce.invoke(StaticMetaMethodSite.java:148)
        at org.codehaus.groovy.runtime.callsite.StaticMetaMethodSite.call(StaticMetaMethodSite.java:88)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:124)
        at my.app.UserController$_closure1.doCall(UserController.groovy:19)

3 个答案:

答案 0 :(得分:3)

为每个请求创建一个新的控制器,因此同步该操作将无济于事,因为它不会被并发用户共享。另外,它是一个Closure(2.0之前的版本),并且同步Closure没有任何意义。

如果要阻止对象的并发编辑,请将其锁定在事务中。创建服务

class UserService {

   void updateUser(long userId, ...) {
      User user = User.lock(userId)
      // do some changes in users...
      user.save()
   }
}

并将其注入您的控制器:

class RespondentController {

   def userService

   def save = {
      long userId = params['userId'] as Long
      userService.updateUser(userId, ...)
   }
}

答案 1 :(得分:0)

最终解决方案就是这个。它确实有效,所以我能够处理数百个并发访问我的其他Web服务:

class RespondentController {
    static scope = "singleton"
    static allowedMethods = [save: "GET"]

    def save() {
        long userId = params['userId'] as Long
        User.lock(userId)
        User user = User.get(userId)
        // do changes in domain classes... 
        // and some stuff which should be done (e.g. send an email...) 
        user.save(flush:true)
    }
}

答案 2 :(得分:0)

  • Synchronized”没有意义,因为Grails每次调用controller

  • 都会创建一个新实例
  • 使用:

    User user = User.lock(userId)

而不是

User.lock(userId)
User user = User.get(userId)