Как реализовать веб-сервис REST в Grails, одновременно обрабатывая больше запросов? - PullRequest
0 голосов
/ 18 декабря 2011

Я разработал веб-сервис REST, который может принимать некоторые данные, загружать пользователя из базы данных, немного изменять его и сохранять обратно в базу данных.

Вот содержимое веб-службы REST:

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()
    }
  }
}

Проблема в том, что одновременно появляется больше запросов. Есть ли способ, как изменить веб-сервис, чтобы он принимал больше запросов одновременно (это может быть некоторые вещи конфигурации)?

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 ]

3 голосов
/ 18 декабря 2011

Новый контроллер создается для каждого запроса, поэтому синхронизация действия не поможет, так как он не используется одновременно несколькими пользователями. Кроме того, это закрытие (до 2.0), а синхронизированное закрытие не имеет никакого смысла.

Если вы хотите заблокировать одновременное редактирование объекта, заблокируйте его в транзакции. Создать сервис

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, ...)
   }
}
0 голосов
/ 18 декабря 2013
  • "Synchronized" Не имеет смысла, поскольку Grails создает новый экземпляр при каждом вызове controller

  • использование:

    User user = User.lock(userId)

вместо

User.lock(userId)
User user = User.get(userId)
0 голосов
/ 16 января 2012

Окончательное решение - это. Это действительно работает, поэтому я могу обрабатывать сотни одновременных обращений к моему веб-сервису отдыха:

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)
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...