1

两周以来,出现了一个疯狂的问题!它只出现在生产中(而不是在开发、集成、预生产模式中)和下班后......

症状:一旦“某事”发生,用户就不能再注册了。经过几天和几天的调查,我不知道如何重现这个问题。目前,我们有重新启动 tomcat 实例的丑陋解决方法,但这非常丑陋。

有关信息,两次交付之间的差异不会涉及注册过程。

这里我认为是相关的:

领域 :

class UserAccount implements Serializable {
    static transients = ['tokenLogin']
    String email
    String pwd
    Date creationDate = new Date()
    String tokenLogin


    //[skip] dozen of other attributes

    static constraints = {
        email(matches:EmailRegexp,blank:false)
        pwd(blank:false)
        tokenLogin(editable:false)
    }

    public void setEmail(String email) {
        this.email = email
        authId = authenticationChannel.getUsername(email)
        // We must recalculate token login
        this.tokenLogin = generateTokenLogin(email, creationDate, pwd)
    }

    public void setPwd(String pwd) {
        this.pwd = pwd
        // We must recalculate token login
        this.tokenLogin = generateTokenLogin(email, creationDate, pwd)
    }

    public static String generateTokenLogin(String email, Date creationDate, String pwd) {
        // work well
        // even tested when return null or with NPE in it and does not produce the same log
        generated = // work with MessageDigest, MD5, salt, etc. (not disclosed, because we are currently under security audit)
        return generated;
    }
}

控制器 :

def saveAccount = {
    def user = new UserAccount(email: params.email?.trim(), pwd: params.pwd) // More parameters, but here what is relevant
    user.validate()
    if(user.hasErrors()) log.info("${user.errors}") // added since the issue appears
    // Other verification (unicity, clear password verification, etc.)
    // Password encryption
    // user.save()
}

输出日志:

2013-06-03 15:37:32,165 [TP-Processor46] INFO  controller.UserAccountController  - org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'domain.UserAccount' on field 'pwd': rejected value [foobar]; codes [methodInvocation.domain.UserAccount.pwd,methodInvocation.pwd,methodInvocation.java.lang.String,methodInvocation]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [domain.UserAccount.pwd,pwd]; arguments []; default message [pwd]]; default message [Property 'pwd' threw exception; nested exception is java.lang.NullPointerException]

问题 :

  • 这个输出可能是如何出现的?我们看到了这个输出pwd或者email字段,从来没有看到其他人。
  • “methodInvocation”代码的含义是什么?(我在捕捉异常MethodInvocationException时抛出的spring-context中看到过BeanWrapperImpl.setPropertyValue
  • 如何获取更多信息?(现在,我们运行一个修补过的 spring-context.jar,在 BeanWrapperImpl 中显示 StackTrace,但直到现在我们才能重现)

资料:

  • Grails 1.3.7 => 春季 3.0.5-GA
  • mysql jdbc
  • 雄猫 6

start.sh(tomcat 选项)

JAVA_OPTS="-Dapp=prod -Xms1024m -Xmx1024m -XX:MaxPermSize=256m \
-Dfile.encoding=UTF-8 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC \
-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled \
-XX:+HeapDumpOnOutOfMemoryError \
-Xloggc:/path/to/gc.log"

编辑:我们改变和编译spring-context.jar能够看到 StackTrace。我们得到了这个:

org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessException details (1) are:
PropertyAccessException 1:
org.springframework.beans.MethodInvocationException: Property 'pwd' threw exception; nested exception is java.lang.NullPointerException
        at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:1127)
        at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:900)
        at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:76)
        at org.springframework.validation.DataBinder.applyPropertyValues(DataBinder.java:673)
        at org.springframework.validation.DataBinder.doBind(DataBinder.java:569)
        at org.springframework.web.bind.WebDataBinder.doBind(WebDataBinder.java:191)
[snip]
Caused by: java.lang.NullPointerException
        at org.codehaus.groovy.runtime.callsite.PojoMetaClassSite.checkCall(PojoMetaClassSite.java:54)
        at org.codehaus.groovy.runtime.callsite.PojoMetaClassSite.call(PojoMetaClassSite.java:42)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:124)
        at domain.UserAccount.generateTokenLogin(UserAccount.groovy:369)
        at domain.UserAccount$generateTokenLogin.callStatic(Unknown Source)
        at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallStatic(CallSiteArray.java:48)
        at domain.UserAccount$generateTokenLogin.callStatic(Unknown Source)
        at domain.UserAccount.setPwd(UserAccount.groovy:184)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:1114)

根本原因是存在一个creationDate可能null在一些奇怪的条件下。我想解决方案不是调用generateTokenLogin()setter(而且只调用 getter)。

我无法解释为什么我们之前没有这个问题(最近没有升级等)

4

3 回答 3

0

听起来一旦发生这种情况,如果不重新启动就无法修复。我会查看您的应用程序的 permgen 大小。如果它适用于预期生产的所有环境,则可能是内存问题。

简单的测试方法是添加 200megs 左右开始。

于 2013-06-10T12:40:22.010 回答
0

您的预生产服务器与您的生产服务器完全相同吗?你做了哪些未成年人修改,你确定没有影响吗?

可能是生成的战争包含错误,您是否尝试回滚您的修改?

Mayby 你可以把你的 prod war 放到你的 pre-prod 环境中来重现这个问题。

是否有来自插件、grails 版本或您的依赖项中的任何内容的最新更新?

不回答你的问题,但我可以给你调查的想法。

斯奈特

于 2013-06-14T09:48:01.747 回答
0

只是一个疯狂的猜测 - 当你覆盖一个 setter 时,你不应该在这个 setter 中设置那个字段的值,因为它是一种递归,或者至少可以预料到不可预测的行为(像这样),也许可以尝试使用原始字段访问运营商:

public void setPwd(String pwd) {
    this.@pwd = pwd
    // We must recalculate token login
    this.tokenLogin = generateTokenLogin(email, pwd)
}

email字段/二传手也是如此

于 2013-06-10T23:10:59.837 回答