会话属性间歇性丢失-Spring Session + Pivotal GemFire实现 [英] Session attributes missing intermittently - Spring Session + Pivotal GemFire Implementation

查看:106
本文介绍了会话属性间歇性丢失-Spring Session + Pivotal GemFire实现的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在春季会话中的某个时候,通过Pivotal GemFire集成解决了一个奇怪的问题.

Facing a strange issue for sometime in Spring Session with Pivotal GemFire integration.

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

We have multiple HTTP requests, which eventually does set/get of session attributes in a varying order based on several conditions.

在给定的时间点...

At some given point...

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

...正在检索null.我们已经交叉验证了在两个session.getAttribute(..)调用之间没有调用任何session.setAttribute(..),其中有一个错过了对象.

... is retrieving null. We have cross verified that no session.setAttribute(..) is invoked in between two session.getAttribute(..) calls out of which one misses the object.

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

We enabled trace logging in GemFire client. There we see a mismatch in hashmaps being read/written. Sharing the logs:

>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;}

即使没有调用任何session.setAttribute(..),属性CUSTOMER_SEARCH_RESPONSE仍然丢失.

Attribute CUSTOMER_SEARCH_RESPONSE is missing even though no session.setAttribute(..) for it was invoked.

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

This is not WRT to one attribute and also not consistent. A re-run might not show this issue.

推荐答案

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

After working with another Pivotal (GemFire) customer on a similar problem (also using Spring Session and Pivotal GemFire (SSDG) to manage HTTP session state in a highly concurrent Web application/environment), we uncovered the underlying issue(s) and ultimately found BUGS in Pivotal GemFire!

简而言之,由于 种族条件 ,这些错误导致 丢失的更新 并发(多用户)Web环境,其中多个HTTP请求可能正在负载下访问和修改同一HTTP会话.而且,并发(用户)越大,负载(同一个HTTP会话的HTTP请求数)越大,这个问题就越明显.

In nutshell, these bugs lead to Lost Updates due to Race Conditions that are exasperated in a highly concurrent (multi-user) Web environment, where multiple HTTP requests might possibly be accessing and modifying the same HTTP session under load. And, the greater the concurrency (users) and the greater the load (number of HTTP requests to the same HTTP session), the more apparent this problem becomes.

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

In fact, I have written several Integration Tests illustrating this problem.

首先,我编写了一个

First, I wrote a Load Integration Test (MultiThreadedClientProxyRegionSessionIntegrationTests). This class spawns 180 Threads (users) performing 10,000 concurrent requests to the same underlying Session. The Session object, though not exactly the same, is modeled after SSDG's GemFireSession object representation.

第二,我写了另一个

Second, I wrote another Integration Test (TwoThreadsClientProxyRegionSessionIntegrationTests) that reliably and repeatedly reproduces the problem.

这两个测试类都是使用 GemFire API 编写的,因此说明了Pivotal GemFire是问题所在,而不是SSDG.

Both of these test classes were written purely with the GemFire API, thereby illustrating that Pivotal GemFire is the problem, not SSDG.

我在

I have written similar test using Spring Session Data GemFire both in my example, and now, also included in the SSDG test suite (along with many other MultiThread/Concurrency based integration tests), itself, ensuring Spring Session (for Pivotal GemFire) will never encounter this problem again, and if it does, I'll know about it sooner than later.

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

In short, the 2 underlying Pivotal GemFire bugs are:

  • GEODE-6152
  • GEODE-6032

解决方法如下:

首先,您必须使用以下命令配置您的Spring Session GemFire缓存客户端应用程序:

First, you must configure your Spring Session, GemFire cache client application with:

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

  1. A client PROXY Region to manage the Session state (default)
  2. Set copy-on-read set to true.
  3. And, you must use GemFire DataSerialization, by setting the sessionSerializerBeanName appropriately:

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

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

请参见此处.

您还需要升级到 Pivotal GemFire的Spring Session 2.1.2.RELEASE(即将发布),因为我做了一些近期的重要改进,例如:

You will also need to upgrade to Spring Session for Pivotal GemFire 2.1.2.RELEASE (to be released soon), since I made several important, recent enhancements, such as:

  1. 问题#12-防止SessionRepository.save(Session)在非肮脏的会话中.
  2. 问题9-添加服务器端配置支持不使用SSDG在服务器上配置Spring Session时的GemFire/Geode DataSerialization.
  3. 问题#17-考虑支持对可自定义的IsDirty应用程序域对象检查.
  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.

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

Using GemFire DataSerialization with Deltas will not prevent, but greatly reduces the possibility of lost updates and other race condition intrinsically inherit in a Web environment, particularly since a Servlet container (e.g. Tomcat) is multi-threaded, processing each HTTP request in a separate thread.

尽管SSDG尽最大努力确保HTTP会话表示形式(即GemFireSession)是线程安全的,但您还必须确保放入HTTP会话中的任何对象也是线程安全的,因为它可以并且很可能将在高度并发的Web应用程序中由1个以上的线程访问,尤其是1个线程中,一个以上的HTTP请求可以一次访问同一HTTP会话(通过会话ID).

While SSDG goes to great effort to ensure the HTTP session representation (i.e. GemFireSession) is thread-safe, you must also ensure that any object you put in the HTTP session is also thread-safe since it can and most likely will be accessed by more than 1 thread in a highly-concurrent Web application, especially 1 where more than a single HTTP request can access the same HTTP session (by session ID) at a time.

无论如何,值得深思.

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

When the configuration above is used, everything works as expected, and when not, lost updates can and will occur dues to the GemFire BUGS!

事实上,我的负载测试表明,在10,000个Session更新中,其中添加了〜9800个Session属性,只有〜1100个成功更新,这意味着〜89%的数据丢失!!

In fact, my load test revealed that out of 10,000 Session updates, where ~9800 Session attributes get added, only ~1100 successfully are, that is a whopping ~89% loss of data!!!

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

However, when the configuration above is applied, all data is accounted for correctly.

希望这会有所帮助!

这篇关于会话属性间歇性丢失-Spring Session + Pivotal GemFire实现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆