【问题标题】:Session attributes missing intermittently - Spring Session + Pivotal GemFire Implementation会话属性间歇性丢失 - Spring Session + Pivotal GemFire 实现
【发布时间】:2018-09-25 11:53:06
【问题描述】:

在 Spring Session 中使用 Pivotal GemFire 集成时遇到了一个奇怪的问题。

我们有多个 HTTP 请求,最终会根据几个条件以不同的顺序设置/获取会话属性。

在某个特定时间点...

    (T) session.getAttribute(sessionKeyN); // (T) is template object

...正在检索null。我们已经交叉验证,在两次session.getAttribute(..) 调用之间没有调用session.setAttribute(..),其中一个未命中对象。

我们在 GemFire 客户端中启用了跟踪日志记录。在那里,我们看到正在读取/写入的哈希映射不匹配。共享日志:

>2018-09-24T02:46:15:342-0700 [20180924_451_5_4,20180912045104000005] TRACE org.apache.geode.DataSerializer: **Writing HashMap with 8 elements**: {LOGIN_DATE_TIME=09-24-2018 02:46:08 AM, TERMINAL=terminal.Terminal@5aa33970, org.springframework.session.FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME=testuser, LOG_TRANSACTION_ID=20180924_451_5_4,20180912045104000005, USER=user.User@70b4b11b, TRANSACTION_HEADER=TransactionHeader@4144221c, XXXX_LOGIN=true, SPRING_SECURITY_CONTEXT=org.springframework.security.core.context.SecurityContextImpl@65bb8fc1: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@65bb8fc1: Principal: testuser; Credentials: [PROTECTED]; Authenticated: true; Details: null;}
>2018-09-24T02:46:15:342-0700 [20180924_451_5_4,20180912045104000005] TRACE org.apache.geode.internal.**InternalDataSerializer: basicWriteObject**: {LOGIN_DATE_TIME=09-24-2018 02:46:08 AM, TERMINAL=terminal.Terminal@5aa33970, org.springframework.session.FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME=testuser, LOG_TRANSACTION_ID=20180924_451_5_4,20180912045104000005, USER=user.User@70b4b11b, TRANSACTION_HEADER=TransactionHeader@4144221c, XXXX_LOGIN=true, SPRING_SECURITY_CONTEXT=org.springframework.security.core.context.SecurityContextImpl@65bb8fc1: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@65bb8fc1: Principal: testuser; Credentials: [PROTECTED]; Authenticated: true; Details: null;}
>2018-09-24T02:46:15:342-0700 [20180924_451_5_4,20180912045104000005] TRACE org.apache.geode.DataSerializer: Writing STRING_BYTES of len=8
>2018-09-24T02:46:15:342-0700 [20180924_451_5_4,20180912045104000005] TRACE org.apache.geode.DataSerializer: Writing String "testuser"
>2018-09-24T02:46:15:342-0700 [20180924_451_5_4,20180912045104000005] TRACE org.apache.geode.DataSerializer: Writing STRING_BYTES of len=36
>2018-09-24T02:46:15:342-0700 [20180924_451_5_4,20180912045104000005] TRACE org.apache.geode.DataSerializer: Writing String "5c4948d9-7438-4dff-badc-fdc0f9997781"
>2018-09-24T02:46:15:342-0700 [20180924_451_5_4,20180912045104000005] TRACE org.apache.geode.internal.InternalDataSerializer: basicWriteObject: { @type = org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository$GemFireSession, id = 5c4948d9-7438-4dff-badc-fdc0f9997781, creationTime = 2018-09-24T09:44:23.180Z, lastAccessedTime = 2018-09-24T09:46:14.909Z, maxInactiveInterval = PT30M, principalName = testuser }
>2018-09-24T02:46:15:342-0700 [20180924_451_5_4,20180912045104000005] TRACE org.apache.geode.internal.InternalDataSerializer: DataSerializer Serializing an instance of org.apache.geode.cache.Operation
>2018-09-24T02:46:15:342-0700 [20180924_451_5_4,20180912045104000005] TRACE org.apache.geode.internal.InternalDataSerializer: basicWriteObject: UPDATE
>2018-09-24T02:46:15:342-0700 [20180924_451_5_4,20180912045104000005] DEBUG org.apache.geode.cache.client.internal.PutOp: PutOpImpl constructing message with operation=UPDATE
>2018-09-24T02:46:15:144-0700 [20180924_451_5_4,20180912045104000005] DEBUG org.apache.geode.internal.cache.LocalRegion: invoking listeners: [org.springframework.session.data.gemfire.GemFireOperationsSessionRepository@4471a4f]
>2018-09-24T02:46:15:144-0700 [20180924_451_5_4,20180912045104000005] TRACE org.apache.geode.internal.cache.LocalRegion: dispatchListenerEvent event=EntryEventImpl[op=LOCAL_LOAD_CREATE;region=/XXXX2wl;key=5c4948d9-7438-4dff-badc-fdc0f9997781;oldValue=null;newValue={ @type = org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository$GemFireSession, id = 5c4948d9-7438-4dff-badc-fdc0f9997781, creationTime = 2018-09-24T09:44:23.180Z, lastAccessedTime = 2018-09-24T09:46:15.079Z, maxInactiveInterval = PT30M, principalName = testuser };callbackArg=null;originRemote=false;originMember=tstplXXXX0004(ClientConfigXXXX2Application:28299:loner):35884:0c27e20a:ClientConfigXXXX2Application;callbacksInvoked;version={v20; rv161; mbr=10.5.230.71(server_devplgemf0066:123628)<v23>:1024; time=1537782375131; remote};isFromServer]
>2018-09-24T02:46:15:144-0700 [20180924_451_5_4,20180912045104000005] TRACE org.apache.geode.internal.cache.versions.VersionTag: deserializing class org.apache.geode.internal.cache.versions.VMVersionTag with flags 0x4
>2018-09-24T02:46:15:144-0700 [20180924_451_5_4,20180912045104000005] TRACE org.apache.geode.internal.InternalDataSerializer: basicReadObject: header=1
>2018-09-24T02:46:15:144-0700 [20180924_451_5_4,20180912045104000005] TRACE org.apache.geode.DataSerializer: **Read HashMap with 9 elements**: {LOGIN_DATE_TIME=09-24-2018 02:46:08 AM, TERMINAL=terminal.Terminal@5a2aa051, **CUSTOMER_SEARCH_RESPONSE=CustomerInfo@600fa25f**, org.springframework.session.FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME=testuser, LOG_TRANSACTION_ID=20180924_451_5_4,20180912045104000005, USER=user.User@7178708f, TRANSACTION_HEADER=TransactionHeader@30215dcd, XXXX_LOGIN=true, SPRING_SECURITY_CONTEXT=org.springframework.security.core.context.SecurityContextImpl@65bb8fc1: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@65bb8fc1: Principal: testuser; Credentials: [PROTECTED]; Authenticated: true; Details: null;}

属性 CUSTOMER_SEARCH_RESPONSE 缺失,即使没有调用 session.setAttribute(..)

这不是一个属性的 WRT,也不一致。重新运行可能不会显示此问题。

【问题讨论】:

    标签: spring spring-session gemfire spring-data-gemfire


    【解决方案1】:

    在与另一位 Pivotal (GemFire) 客户合作解决类似问题后(也使用 Spring Session 和 Pivotal GemFire (SSDG) 在高度并发的 Web 应用程序/环境中管理 HTTP 会话状态),我们发现了根本问题并最终发现了 Pivotal GemFire 中的 BUGS!

    简而言之,这些错误会导致 更新丢失,原因是 Race Conditions并发(多用户)Web 环境,其中多个 HTTP 请求可能在负载下访问和修改同一个 HTTP 会话。而且,并发(用户)越大,负载越大(对同一 HTTP 会话的 HTTP 请求数),这个问题就越明显。

    事实上,我已经写了几个集成测试来说明这个问题。

    首先,我写了一个Load Integration Test (MultiThreadedClientProxyRegionSessionIntegrationTests)。此类产生 180 个线程(用户),对同一底层 Session 执行 10,000 个并发请求。 Session 对象虽然不完全相同,但它是在 SSDG 的 GemFireSession 对象表示之后建模的。

    其次,我写了另一个Integration Test (TwoThreadsClientProxyRegionSessionIntegrationTests),它可靠地反复重现了这个问题。

    这两个测试类都是纯粹用GemFire API 编写的,从而说明问题在于 Pivotal GemFire,而不是 SSDG。

    我已经在我的example 和现在的included in the SSDG test suite(以及许多其他MultiThread/Concurrency based integration tests)中使用Spring Session Data GemFire编写了类似的测试,确保 Spring Session(用于 Pivotal GemFire)再也不会遇到这个问题了,如果是的话,我迟早会知道的。

    简而言之,两个潜在的 Pivotal GemFire 错误是:

    解决方法如下:

    首先,您必须配置您的 Spring Session、GemFire 缓存客户端应用程序:

    1. 一个客户端 PROXY 区域来管理 Session 状态(默认)
    2. copy-on-read 设置为true
    3. 并且,您必须使用 GemFire DataSerialization,通过适当地设置 sessionSerializerBeanName

      @SpringBootApplication @ClientCacheApplication(copyOnRead = true, subscriptionEnabled = true) @EnableGemFireHttpSession( clientRegionShortcut = ClientRegionShortcut.PROXY, sessionSerializerBeanName = GemFireHttpSessionConfiguration.SESSION_DATA_SERIALIZER_BEAN_NAME ) 类 MySpringBootSpringSessionDataGemFireApplication { ... }

    例如,请参阅here

    您还需要升级到 Pivotal GemFire 的 Spring Session 2.1.2.RELEASE(即将发布),因为我最近做了几项重要的增强,例如:

    1. Issue #12 - Prevent SessionRepository.save(Session) on non-dirty Sessions.
    2. Issue #9 - Add server-side configuration support for GemFire/Geode DataSerialization when SSDG is not used to configure Spring Session on the servers.
    3. Issue #17 - Consider support for customizable IsDirty application domain object checking.

    使用带有 Delta 的 GemFire DataSerialization 不会阻止,但会大大降低丢失更新和其他竞争条件在 Web 环境中固有继承的可能性,特别是因为 Servlet 容器(例如 Tomcat)是多线程的,处理每个 HTTP 请求一个单独的线程。

    虽然 SSDG 竭尽全力确保 HTTP 会话表示(即GemFireSession)是线程安全的,但您还必须确保放入 HTTP 会话中的任何对象也是线程安全的,因为它可以而且很可能是线程安全的将被高并发 Web 应用程序中的 1 个以上线程访问,尤其是其中一个以上的 HTTP 请求一次可以访问同一个 HTTP 会话(按会话 ID)的 1 个。

    无论如何,值得深思。

    使用上述配置时,一切都按预期工作,否则,由于 GemFire BUGS 可能并且将会发生丢失更新!

    事实上,我的负载测试表明,在 10,000 个会话更新中,添加了约 9800 个会话属性,只有约 1100 个成功,这是高达约 89% 的数据丢失!!!

    但是,当应用上述配置时,所有数据都会正确计算。

    希望这会有所帮助!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-03
      • 1970-01-01
      • 2010-12-03
      • 1970-01-01
      • 2018-09-17
      相关资源
      最近更新 更多