3. HttpSession 集成
Spring Session 提供了与 javax.servlet.http.HttpSession 的透明集成。这意味着开发者可以用 Spring Session 支持的实现来替换 HttpSession 实现。
Spring Session 支持插入多种不同的数据存储提供者(例如 Apache Geode),以管理 HttpSession 状态。
3.1. 为什么选择 Spring Session 和 HttpSession?
我们已经提到过 Spring Session 提供了与 HttpSession 的透明集成,但这能为我们带来什么好处呢?
-
HttpSession - 使
HttpSession能够 集群(即为了高可用性进行复制),而无需绑定到特定应用容器的解决方案。 -
REST API - 允许在协议头中提供会话 ID,以便与 RESTful API 一起使用。
-
WebSocket - 提供在接收 WebSocket 消息时保持
HttpSession活跃的能力。 -
WebSession - 允许以应用容器中立的方式替换 Spring WebFlux 的
WebSession。
3.2. 使用 Apache Geode 进行 HttpSession 管理
当 Apache Geode 与 Spring Session 一起使用时,Web 应用程序的 javax.servlet.http.HttpSession 可以替换为由 Apache Geode 管理的 集群 实现, 并使用 Spring Session 的 API 方便地进行访问。
使用 Apache Geode 管理会话状态的两种最常见的拓扑结构包括:
此外,Apache Geode 支持使用 WAN 拓扑 进行站点到站点的复制。 配置和使用 Apache Geode 的 WAN 功能的能力独立于 Spring Session, 并且超出了本文档的范围。
使用 Spring Data for Apache Geode 配置 Apache Geode WAN 功能的更多详细信息可以在此处找到。
3.2.1. Apache Geode 客户端-服务器
客户端-服务器拓扑结构很可能是用户在使用 Apache Geode 作为 Spring Session 的提供程序时最常见的配置选择,因为 Apache Geode 服务器与应用程序相比,对 JVM 堆的要求显著不同且独特。使用客户端-服务器拓扑结构可以使应用程序独立于其他应用程序进程管理(例如复制)应用状态。
在客户端-服务器拓扑中,使用 Spring Session 的应用程序将打开一个或多个与远程 Apache Geode 服务器集群的连接,这些服务器将管理对所有 HttpSession 状态的访问。
您可以使用以下任一方式配置客户端-服务器拓扑:
基于Java的Apache Geode客户端-服务器配置
本节描述如何配置 Apache Geode 的客户端-服务器拓扑以使用基于 Java 的配置管理 HttpSession 状态。
使用 Apache Geode(客户端-服务器)的 HttpSession 提供了一个工作示例,演示了如何使用 Java 配置将 Spring Session 与 Apache Geode 集成以管理 HttpSession 状态。您可以阅读下面的基本集成步骤,但建议在将自己的应用程序集成时,跟随详细的 `HttpSession` 使用 Apache Geode(客户端-服务器)指南。 |
Spring Java 配置
在添加所需的依赖项和存储库声明后,我们可以创建 Spring 配置。
Spring 配置负责创建一个 Servlet Filter,该 Servlet 替换了 HttpSession
并使用 Spring Session 和 Apache Geode 提供的实现。
客户端配置
添加以下 Spring 配置:
@ClientCacheApplication(name = "SpringSessionDataGeodeJavaConfigSampleClient", logLevel = "error",
readTimeout = 15000, retryAttempts = 1, subscriptionEnabled = true) (1)
@EnableGemFireHttpSession(poolName = "DEFAULT") (2)
public class ClientConfig extends ClientServerIntegrationTestsSupport {
@Bean
ClientCacheConfigurer gemfireServerReadyConfigurer( (3)
@Value("${spring.data.gemfire.cache.server.port:40404}") int cacheServerPort) {
return (beanName, clientCacheFactoryBean) -> waitForServerToStart("localhost", cacheServerPort);
}
}
| 1 | 首先,我们通过在ClientConfig类上添加注解@ClientCacheApplication,将我们的Web应用程序声明为Apache Geode缓存客户端。此外,我们调整了一些基本的“默认”Pool设置(例如readTimeout)。 |
| 2 | @EnableGemFireHttpSession 创建了一个名为 springSessionRepositoryFilter 的 Spring bean,它实现了 javax.servlet.Filter。该过滤器将 HttpSession 替换为由 Spring Session 提供并由 Apache Geode 支持的实现。此外,配置还会创建必要的客户端 Region (默认情况下为 \"ClusteredSpringSessions\",这是一个 PROXY Region),与同名的服务器端 Region 对应。 所有会话状态都通过 Region 数据访问操作从客户端发送到服务器。 客户端的 Region 使用 \"DEFAULT\" Pool。 |
| 3 | 然后,我们等待以确保 Apache Geode 服务器已启动并正在运行,然后我们再继续。这其实只对自动化(集成)测试目的有用。 |
| 在典型的 Apache Geode 生产部署中,集群通常包括可能数百或数千个服务器(也称为数据节点),客户端更常见的是连接到同一集群中运行的一个或多个 Apache Geode 定位器。定位器将有关集群中可用服务器的元数据、各个服务器的负载以及哪些服务器拥有客户端感兴趣的数据传递给客户端,这对于直接的单跳数据访问和对延迟敏感的应用程序尤为重要。更多详细信息,请参阅 Apache Geode 用户指南中的客户端/服务器部署。 |
| 有关配置 Spring Data Geode 的更多信息,请参阅 参考指南。 |
@EnableGemFireHttpSession 注解使开发人员能够使用以下属性配置 Spring Session 和 Apache Geode 的某些方面:
-
clientRegionShortcut- 指定 Apache Geode 数据管理策略 在客户端上使用 ClientRegionShortcut (默认值为PROXY)。此属性仅在配置客户端Region时使用。 -
indexableSessionAttributes- 按名称标识应为查询目的而索引的会话属性。只有按名称明确标识的会话属性才会被索引。 -
maxInactiveIntervalInSeconds- 控制 HttpSession 的空闲超时时间(默认为 30 分钟)。 -
poolName- 专用 Apache Geode 的名称Pool,用于将客户端连接到服务器集群。 此属性仅在应用程序作为缓存客户端时使用。默认值为gemfirePool。 -
regionName- 指定用于存储和管理HttpSession状态的 Apache GeodeRegion的名称 (默认是 \"ClusteredSpringSessions\")。 -
serverRegionShortcut- 指定 Apache Geode 数据管理策略 在服务器上使用 RegionShortcut (默认值为PARTITION)。此属性仅在配置服务器Regions时使用, 或者当采用 P2P 拓扑时使用。
重要的是要记住,如果客户端Region是PROXY或CACHING_PROXY,则Apache Geode客户端的Region名称必须与服务器的Region名称相同。如果客户端使用的Region用于存储会话状态的是LOCAL,则客户端和服务器的Region名称不需要匹配。但是,请记住,会话状态不会传播到服务器,您将失去使用Apache Geode以分布式、复制的方式在服务器上存储和管理分布式、复制的会话状态信息的所有好处。 |
服务器配置
到目前为止,我们只讨论了等式的一侧。我们还需要一个 Apache Geode 服务器,以便我们的缓存客户端可以与其通信,并将 session 状态发送到服务器进行管理。
在这个示例中,我们将使用以下 Java 配置来配置和运行 Apache Geode 服务器:
@CacheServerApplication(name = "SpringSessionDataGeodeJavaConfigSampleServer", logLevel = "error") (1)
@EnableGemFireHttpSession(maxInactiveIntervalInSeconds = 30) (2)
public class GemFireServer {
@SuppressWarnings("resource")
public static void main(String[] args) {
new AnnotationConfigApplicationContext(GemFireServer.class).registerShutdownHook();
}
}
| 1 | 首先,我们使用@CacheServerApplication注解来简化包含CacheServer的对等缓存实例的创建,以便缓存客户端连接。 |
| 2 | (Optional) 然后,使用 @EnableGemFireHttpSession 注解 GemFireServer 类,以创建必要的服务器端 Region(默认为“ClusteredSpringSessions”),用于存储 HttpSession 状态。此步骤是可选的,因为会话 Region 可以手动创建,也许可以通过外部手段实现。使用 @EnableGemFireHttpSession 注解非常方便且快捷。 |
基于XML的Apache Geode客户端-服务器配置
本节描述如何配置 Apache Geode 的客户端-服务器拓扑以使用基于 XML 的配置来管理 HttpSession 状态。
The 使用 XML 的 Apache Geode(客户端-服务器)HttpSession 提供了一个工作示例,演示了如何通过 XML 配置将 Spring Session 与 Apache Geode 集成以管理 HttpSession 状态。您可以阅读下面的集成基本步骤,但建议在将自己的应用程序集成时,跟随详细的 使用 XML 的 Apache Geode(客户端-服务器)`HttpSession` 指南。 |
Spring XML 配置
在添加所需的依赖项和存储库声明后,我们可以创建 Spring 配置。
Spring 配置负责创建一个 Servlet Filter,它用 Spring Session 和 Apache Geode 支持的实现替换了 javax.servlet.http.HttpSession。
客户端配置
添加以下 Spring 配置:
<context:annotation-config/>
<context:property-placeholder/>
<bean class="sample.client.ClientServerReadyBeanPostProcessor"/>
(1)
<util:properties id="gemfireProperties">
<prop key="log-level">${spring.data.gemfire.cache.log-level:error}</prop>
</util:properties>
(2)
<gfe:client-cache properties-ref="gemfireProperties" pool-name="gemfirePool"/>
(3)
<gfe:pool read-timeout="15000" retry-attempts="1" subscription-enabled="true">
<gfe:server host="localhost" port="${spring.data.gemfire.cache.server.port:40404}"/>
</gfe:pool>
(4)
<bean class="org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration"
p:poolName="DEFAULT"/>
| 1 | (Optional) 首先,我们可以包含一个 Properties bean 来配置 Apache Geode 的某些方面 ClientCache 使用 Pivotal GemFire 属性。在这种情况下,我们只是 通过特定于应用程序的系统属性设置 Apache Geode 的“日志级别”,如果未指定,则默认为“警告”。 |
| 2 | 我们必须创建一个 Apache Geode 的实例 ClientCache。我们使用我们的 gemfireProperties 对其进行初始化。 |
| 3 | 然后,我们配置了Pool个连接来在我们的客户端/服务器拓扑中与 Apache Geode 服务器通信。 在我们的配置中,我们对超时、连接数等使用了合理的设置。此外,我们的Pool 已配置为直接连接到服务器(使用嵌套的gfe:server元素)。 |
| 4 | 最后,注册了一个 GemFireHttpSessionConfiguration bean 以启用 Spring Session 功能。 |
| 在典型的 Apache Geode 生产部署中,集群通常包括可能数百或数千个服务器(也称为数据节点),客户端更常见的是连接到同一集群中运行的一个或多个 Apache Geode 定位器。定位器将有关集群中可用服务器的元数据、各个服务器的负载以及哪些服务器拥有客户端感兴趣的数据传递给客户端,这对于直接的单跳数据访问和对延迟敏感的应用程序尤为重要。更多详细信息,请参阅 Apache Geode 用户指南中的客户端/服务器部署。 |
| 有关配置 Spring Data for Apache Geode 的更多信息,请参阅参考指南。 |
服务器配置
到目前为止,我们只讨论了等式的一侧。我们还需要一个 Apache Geode 服务器,以便我们的缓存客户端可以与其通信,并将 session 状态发送到服务器进行管理。
在这个示例中,我们将使用以下 XML 配置来启动 Apache Geode 服务器:
<context:annotation-config/>
<context:property-placeholder/>
(1)
<util:properties id="gemfireProperties">
<prop key="name">SpringSessionDataGeodeSampleXmlServer</prop>
<prop key="log-level">${spring.data.gemfire.cache.log-level:error}</prop>
</util:properties>
(2)
<gfe:cache properties-ref="gemfireProperties"/>
(3)
<gfe:cache-server port="${spring.data.gemfire.cache.server.port:40404}"/>
(4)
<bean class="org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration"
p:maxInactiveIntervalInSeconds="30"/>
| 1 | (Optional) 首先,我们可以包含一个 Properties bean 来配置 Apache Geode 对等节点的某些方面 Cache, 使用 Pivotal GemFire 属性。在这种情况下,我们只是通过特定于应用程序的系统属性来设置 Apache Geode 的“日志级别”,如果未指定,则默认为“警告”。 |
| 2 | 我们必须配置一个 Apache Geode 对等 Cache 实例。我们使用 Apache Geode 属性对其进行初始化。 |
| 3 | 接下来,我们定义一个带有合理配置的CacheServer,用于我们的缓存客户端应用程序连接到服务器并发送会话状态时使用的bind-address和port。 |
| 4 | 最后,我们通过注册一个 GemFireHttpSessionConfiguration 的实例来启用在客户端 XML 配置中声明的相同 Spring Session 功能, 不同之处在于我们将 session 过期超时时间设置为 30 秒。我们将在后面解释这意味着什么。 |
Apache Geode 服务器通过以下内容进行引导启动:
@Configuration (1)
@ImportResource("META-INF/spring/session-server.xml") (2)
public class GemFireServer {
public static void main(String[] args) {
new AnnotationConfigApplicationContext(GemFireServer.class).registerShutdownHook();
}
}
与其定义一个带有 main 方法的简单 Java 类,您可以考虑使用 Spring Boot。 |
| 1 | 注解 @Configuration 将此 Java 类指定为使用 Spring 的注解配置支持的 Spring 配置元数据来源。 7.9. 基于注解的容器配置。 |
| 2 | 主要的配置来自于META-INF/spring/session-server.xml文件。 |
XML Servlet 容器初始化
我们的 Spring XML 配置 创建了一个名为 springSessionRepositoryFilter 的 Spring bean, 该 bean 实现了 javax.servlet.Filter 接口。 springSessionRepositoryFilter bean 负责使用 Spring Session 和 Apache Geode 提供的自定义实现来替换 javax.servlet.http.HttpSession。
为了使我们的 Filter 发挥其魔力,我们需要指示 Spring 加载我们的 session-client.xml 配置文件。
我们通过以下配置实现此目的:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/session-client.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
The ContextLoaderListener reads the contextConfigLocation context parameter value and picks up our session-client.xml configuration file.
最后,我们需要确保 Servlet 容器(即 Tomcat)在每个请求中都使用我们的 springSessionRepositoryFilter。
以下代码片段为我们执行了最后一步:
<filter>
<filter-name>springSessionRepositoryFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSessionRepositoryFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
DelegatingFilterProxy会根据名称查找一个bean,名称为springSessionRepositoryFilter,并将其转换为Filter类型。对于每个HTTP请求,都会调用DelegatingFilterProxy,它会将请求委派给springSessionRepositoryFilter。
3.2.2. Apache Geode 点对点 (P2P)
一种不太常见的方法是将您的 Spring Session 应用程序配置为 Apache Geode 集群中的对等成员, 使用 点对点 (P2P) 拓扑。 在此配置中,Spring Session 应用程序将成为 Apache Geode 集群中的一个实际服务器(或数据节点), 而不仅仅是像之前那样的缓存客户端。
这种方法的一个优势是应用程序与其状态(即其数据)的紧密联系,特别是HttpSession状态。然而,还有其他有效的方法可以实现类似的数据依赖计算,例如使用 Apache Geode 的 函数执行。 当 Apache Geode 在 Spring Session 中作为提供者时,可以使用 Apache Geode 的任何其他功能。
P2P 拓扑结构在测试目的以及更小、更专注和独立的应用程序(例如那些在微服务架构中发现的应用)中非常有用,而且无疑会提升您应用程序的感知延迟和吞吐量需求。
你可以通过以下方式之一配置点对点(P2P)拓扑:
Apache Geode 点对点 (P2P) 基于 Java 的配置
本节描述如何配置 Apache Geode 的点对点(P2P)拓扑结构以使用基于 Java 的配置来管理 HttpSession 状态。
使用 Apache Geode(点对点)的 HttpSession 提供了一个工作示例,演示了如何通过 Java 配置将 Spring Session 与 Apache Geode 集成以管理 HttpSession 状态。您可以在下方阅读集成的基本步骤,但我们建议在将自己的应用程序集成时,跟随详细的 `HttpSession` 使用 Apache Geode(点对点)指南 进行操作。 |
Spring Java 配置
在添加所需的依赖项和存储库声明后,我们可以创建 Spring 配置。
Spring 配置负责创建一个 Servlet Filter,它使用由 Spring Session 和 Apache Geode 支持的实现来替换 javax.servlet.http.HttpSession。
添加以下 Spring 配置:
@PeerCacheApplication(name = "SpringSessionDataGeodeJavaConfigP2pSample", logLevel = "error") (1)
@EnableGemFireHttpSession(maxInactiveIntervalInSeconds = 30) (2)
public class Config {
}
| 1 | 首先,我们使用@PeerCacheApplication注解来简化对等缓存实例的创建。 |
| 2 | 然后,Config 类使用 @EnableGemFireHttpSession 注解来创建必要的服务器端 Region (默认情况下为“ClusteredSpringSessions”),用于存储 HttpSession 状态。 |
| 有关配置适用于 Apache Geode 的 Spring Data 的更多信息,请参阅 参考指南。 |
@EnableGemFireHttpSession 注解使开发人员能够使用以下属性配置 Spring Session 和 Apache Geode 的某些方面:
-
clientRegionShortcut- 指定 Apache Geode 数据管理策略 在客户端上使用 ClientRegionShortcut (默认值为PROXY)。此属性仅在配置客户端Region时使用。 -
indexableSessionAttributes- 按名称标识应为查询目的而索引的会话属性。只有按名称明确标识的会话属性才会被索引。 -
maxInactiveIntervalInSeconds- 控制 HttpSession 的空闲超时时间(默认为 30 分钟)。 -
poolName- 专用 Apache Geode 的名称Pool,用于将客户端连接到服务器集群。 此属性仅在应用程序作为缓存客户端时使用。默认值为gemfirePool。 -
regionName- 指定用于存储和管理HttpSession状态的 Apache GeodeRegion的名称 (默认是 \"ClusteredSpringSessions\")。 -
serverRegionShortcut- 指定 Apache Geode 数据管理策略 在服务器上使用 RegionShortcut (默认值为PARTITION)。此属性仅在配置服务器Regions时使用, 或者当采用 P2P 拓扑时使用。
Java Servlet 容器初始化
我们的 <<[httpsession-spring-java-configuration-p2p,Spring Java 配置>> 创建了一个名为 springSessionRepositoryFilter 的 Spring bean,它实现了 javax.servlet.Filter。这个 springSessionRepositoryFilter bean 负责使用由 Spring Session 和 Apache Geode 支持的自定义实现来替换 javax.servlet.http.HttpSession。
为了使我们的 Filter 发挥其魔力,Spring 需要加载我们的 Config 类。我们还需要确保我们的 Servlet 容器(即 Tomcat)在每次 HTTP 请求时使用我们的 springSessionRepositoryFilter。
幸好,Spring Session 提供了一个名为 AbstractHttpSessionApplicationInitializer 的实用工具类,可以非常轻松地完成这两个步骤。
你可以从下面找到一个示例:
public class Initializer extends AbstractHttpSessionApplicationInitializer { (1)
public Initializer() {
super(Config.class); (2)
}
}
我们类的名称(Initializer)并不重要。重要的是我们扩展了 AbstractHttpSessionApplicationInitializer。 |
| 1 | 第一步是扩展 AbstractHttpSessionApplicationInitializer。这确保了名为 springSessionRepositoryFilter 的 Spring bean 被注册到我们的 Servlet 容器中,并在每次 HTTP 请求时使用。 |
| 2 | AbstractHttpSessionApplicationInitializer 还提供了一种机制,可以轻松允许 Spring 加载我们的 Config 类。 |
Apache Geode 点对点 (P2P) 基于 XML 的配置
本节描述如何配置 Apache Geode 的点对点(P2P)拓扑以使用基于 XML 的配置来管理 HttpSession 状态。
The 使用 XML 的 HttpSession 与 Apache Geode(点对点) 提供了一个工作示例,演示了如何使用 XML 配置将 Spring Session 与 Apache Geode 集成以管理 HttpSession 状态。您可以阅读下面的基本集成步骤,但建议在将自己的应用程序集成时,跟随详细的 使用 XML 的 `HttpSession` 与 Apache Geode(点对点)指南 进行操作。 |
Spring XML 配置
在添加所需的依赖项和存储库声明后,我们可以创建 Spring 配置。
Spring 配置负责创建一个 Servlet Filter,它使用由 Spring Session 和 Apache Geode 支持的实现来替换 javax.servlet.http.HttpSession。
添加以下 Spring 配置:
<context:annotation-config/>
<context:property-placeholder/>
(1)
<util:properties id="gemfireProperties">
<prop key="name">SpringSessionDataGeodeXmlP2pSample</prop>
<prop key="log-level">${spring.data.gemfire.cache.log-level:error}</prop>
</util:properties>
(2)
<gfe:cache properties-ref="gemfireProperties"/>
(3)
<bean class="org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration"
p:maxInactiveIntervalInSeconds="30"/>
| 1 | (可选) 首先,我们可以包含一个 Properties bean 来配置 Apache Geode 对等节点的某些方面 Cache, 使用 VMware Tanzu GemFire 属性。在这种情况下,我们只是通过特定于应用程序的系统属性来设置 Apache Geode 的“日志级别”,如果未指定,则默认为“警告”。 |
| 2 | 我们必须配置一个 Apache Geode 对等 Cache 实例。我们使用 Apache Geode 属性对其进行初始化。 |
| 3 | 最后,我们通过注册一个 GemFireHttpSessionConfiguration 的实例来启用 Spring Session 功能。 |
| 有关配置 Spring Data for Apache Geode 的更多信息,请参阅参考指南。 |
XML Servlet 容器初始化
The Spring XML 配置 创建了一个名为 springSessionRepositoryFilter 的 Spring bean, 该 bean 实现了 javax.servlet.Filter。 这个 springSessionRepositoryFilter bean 负责用一个由 Spring Session 和 Apache Geode 支持的自定义实现来替换 javax.servlet.http.HttpSession。
为了使我们的 Filter 发挥其魔力,我们需要指示 Spring 加载我们的 session.xml 配置文件。
我们通过以下配置实现此目的:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/*.xml
</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
The ContextLoaderListener 读取contextConfigLocation上下文参数值并加载我们的session.xml配置文件。
最后,我们需要确保 Servlet 容器(即 Tomcat)在每个 HTTP 请求中使用我们的 springSessionRepositoryFilter。
以下代码片段为我们执行了最后一步:
<filter>
<filter-name>springSessionRepositoryFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSessionRepositoryFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
将 DelegatingFilterProxy 会根据名称查找一个 bean,名称为 springSessionRepositoryFilter 并将其转换为 Filter。对于每个 HTTP 请求, DelegatingFilterProxy 被调用,并委托给 springSessionRepositoryFilter。
3.3. 配置HttpSession 使用 Apache Geode 和属性进行管理
虽然 @EnableGemFireHttpSession 注解在 Spring Boot 应用程序中开始使用 Spring Session 和 Apache Geode 时易于使用且非常方便,但在从一个环境迁移到另一个环境时,很快就会遇到限制,例如,当从开发环境迁移到测试环境再迁移到生产环境时。
使用 @EnableGemFireHttpSession 注解属性时,无法在不同环境之间改变配置。因此,Spring Session for Apache Geode 为所有 @EnableGemFireHttpSession 注解属性引入了知名且有文档记录的属性。
| 属性 | 注解属性 | 描述 | 默认 |
|---|---|---|---|
spring.session.data.gemfire.cache.client.pool.name |
|
用于存储/访问会话状态的客户端区域使用的专用池的名称。 |
gemfirePool |
spring.session.data.gemfire.cache.client.region.shortcut |
|
在客户端-服务器拓扑中设置客户端区域数据管理策略。 |
ClientRegionShortcut.PROXY |
spring.session.data.gemfire.cache.server.region.shortcut |
|
设置对等(P2P)拓扑中对等区域的数据管理策略。 |
RegionShortcut.PARTITION |
spring.session.data.gemfire.session.attributes.indexable |
|
以逗号分隔的会话属性列表,这些属性将在会话区域中被索引。 |
|
spring.session.data.gemfire.session.expiration.bean-name |
|
在 Spring 容器中实现过期策略的 bean 的名称 |
|
spring.session.data.gemfire.session.expiration.max-inactive-interval-seconds |
|
会话过期超时(以秒为单位) |
1800 |
spring.session.data.gemfire.session.region.name |
|
用于存储和访问会话状态的客户端或对等区域的名称。 |
集群化的Spring会话 |
spring.session.data.gemfire.session.serializer.bean-name |
|
Spring 容器中实现序列化策略的 bean 的名称 |
SessionPdxSerializer |
所有属性都在@EnableGemFireHttpSession注解属性的Javadoc中进行了说明。 |
因此,当使用 Apache Geode 作为您的提供程序时,通过如下属性调整 Spring Session 的配置非常简单:
@SpringBootApplication
@ClientCacheApplication
@EnableGemFireHttpSession(maxInactiveIntervalInSeconds = 900)
class MySpringSessionApplication {
// ...
}
然后,在 application.properties 中:
#application.properties
spring.session.data.gemfire.cache.client.region.shortcut=CACHING_PROXY
spring.session.data.gemfire.session.expiration.max-inactive-internval-seconds=3600
任何显式定义的属性都会覆盖相应的 @EnableGemFireHttpSession 注解属性。
在上面的示例中,尽管EnableGemFireHttpSession注解的maxInactiveIntervalInSeconds属性被设置为900秒或15分钟,但相应的属性(即spring.session.data.gemfire.session.expiration.max-inactive-interval-seconds)会覆盖该值,并将过期时间设置为3600秒或60分钟。
| 请注意,属性会在运行时覆盖注解属性的值。 |
3.3.1. 属性的属性
你甚至可以更加复杂地使用其他属性来配置你的属性,如下所示:
#application.properties
spring.session.data.gemfire.session.expiration.max-inactive-internval-seconds=${app.geode.region.expiration.timeout:3600}
此外,您可以使用 Spring 配置文件根据环境、应用程序或应用程序需求决定的任何其他条件来调整过期超时(或其他属性)。
| Property placeholders and nesting is a feature of the core Spring Framework and not specific to Spring Session or Spring Session for Apache Geode. |
3.4. 配置HttpSession 使用 Apache Geode 和 Configurer 进行管理
除了属性之外,Spring Session for Apache Geode 还允许您通过 SpringSessionGemFireConfigurer 接口调整 Spring Session 与 Apache Geode 的配置。该接口定义了一个包含默认方法的契约,每个 @EnableGemFireHttpSession 注解属性都可以通过重写这些方法来调整配置。
SpringSessionGemFireConfigurer 在概念上类似于 Spring Web MVC 的 Configurer 接口(例如 o.s.web.servlet.config.annotation.WebMvcConfigurer),它在启动时调整 Web 应用程序配置的各个方面,例如配置异步支持。声明并实现 Configurer 的优势在于,它使您可以编程控制您的配置。这在需要轻松表达复杂、条件性逻辑以确定是否应应用配置的情况下非常有用。
例如,要像我们之前所做的那样调整客户端 Region 数据管理策略和 Session 过期超时时间, 请使用以下内容:
@Configuration
class MySpringSessionConfiguration {
@Bean
SpringSessionGemFireConfigurer exampleSpringSessionGemFireConfigurer() {
return new SpringSessionGemFireConfigurer() {
@Override
public ClientRegionShortcut getClientRegionShortcut() {
return ClientRegionShortcut.CACHING_PROXY;
}
@Override
public int getMaxInactiveIntervalInSeconds() {
return 3600;
}
};
}
}
当然,这个例子并不是很有创意。你完全可以使用更复杂的逻辑来确定每个配置属性的配置方式。
你可以根据需要实现复杂的功能,例如通过使用 Spring 的 @Value 注解,基于其他属性来实现你的 Configurer,如下所示:
@Configuration
class MySpringSessionConfiguration {
@Bean
@Primary
@Profile("production")
SpringSessionGemFireConfigurer exampleSpringSessionGemFireConfigurer(
@Value("${app.geode.region.data-management-policy:CACHING_PROXY}") ClientRegionShortcut shortcut,
@Value("${app.geode.region.expiration.timeout:3600}") int timeout) {
return new SpringSessionGemFireConfigurer() {
@Override
public ClientRegionShortcut getClientRegionShortcut() {
return shortcut;
}
@Override
public int getMaxInactiveIntervalInSeconds() {
return timeout;
}
};
}
}
Spring Boot 将自动解析 @Value 注解属性占位符值或 SpEL 表达式。 然而,如果你没有使用 Spring Boot,则必须显式注册一个静态的 PropertySourcesPlaceholderConfigurer bean 定义。 |
然而,除非您同时使用 Spring profiles,或者通过 Spring 的 @Primary 上下文注解将多个 SpringSessionGemFireConfigurer bean 中的某一个标记为 primary,否则您一次只能在 Spring 容器中声明 1 个 SpringSessionGemFireConfigurer bean。
3.4.1. 配置优先级
A SpringSessionGemFireConfigurer 优先于 @EnableGemFireHttpSession 注解属性或 Spring Boot 中定义的任何知名且有文档记录的 Spring Session for Apache Geode 属性(例如 spring.session.data.gemfire.session.expiration.max-inactive-interval-seconds)
如果您的Web应用程序采用了多种配置方式,将适用以下优先顺序:
-
SpringSessionGemFireConfigurer\"已实现\"的回调方法 -
Documented Spring Session for Apache Geode properties (See corresponding
@EnableGemFireHttpSessionannotation attribute Javadoc; e.g.spring.session.data.gemfire.session.region.name) -
@EnableGemFireHttpSession注解属性
Spring Session for Apache Geode 仅会根据您实际实现的方法,应用 Spring 容器中声明的 SpringSessionGemFireConfigurer bean 的配置。
在上面的示例中,由于您没有实现 getRegionName() 方法,因此管理 HttpSession 状态的 Apache Geode 区域的名称将不由 Configurer 确定。
例举
例如,考虑以下配置:
@ClientCacheApplication
@EnableGemFireHttpSession(
maxInactiveIntervalInSeconds = 3600,
poolName = "DEFAULT"
)
class MySpringSessionConfiguration {
@Bean
SpringSessionGemFireConfigurer sessionExpirationTimeoutConfigurer() {
return new SpringSessionGemFireConfigurer() {
@Override
public int getMaxInactiveIntervalInSeconds() {
return 300;
}
};
}
}
此外,考虑以下 Spring Boot application.properties 文件:
-
Spring Boot
application.properties
spring.session.data.gemfire.session.expiration.max-inactive-interval-seconds = 900 spring.session.data.gemfire.session.region.name = Sessions
会话过期超时时间将为300秒,即5分钟,覆盖属性(即spring.session.data.gemfire.session.expiration.max-inactive-interval-seconds)中设置的900秒(15分钟), 以及显式@EnableGemFireHttpSession.maxInactiveIntervalInSeconds注解属性值中设置的3600秒(1小时)。
由于“sessionExpirationTimeoutConfigurer”bean没有重写getRegionName()方法,Session区域的名称将由属性(即spring.session.data.gemfire.session.region.name)确定,该属性设置为“Sessions”,并覆盖了隐式的@EnableGemFireHttpSession.regionName注解属性的默认值“ClusteredSpringSessions”。
@EnableGemFireHttpSession.poolName 注解属性的值 \"DEFAULT\" 将决定在客户端和服务器之间发送区域操作时使用的连接池名称,以管理服务器上的会话状态,因为既没有设置相应的属性(即 spring.session.data.gemfire.cache.client.pool.name`),也没有通过 \"sessionExpirationTimeoutConfigurer\" bean 覆盖 SpringSessionGemFireConfigurer.getPoolName() 方法。
最后,用于管理会话状态的客户端区域将具有数据管理策略 PROXY,这是 @EnableGemFireHttpSession.clientRegionShortcut 注解属性的默认值,该值未被显式设置,与其对应的属性(即 spring.session.data.gemfire.cache.client.region.shortcut)也未设置。 而且,由于 SpringSessionConfigurer.getClientRegionShortcut() 方法未被重写,因此使用了默认值。
3.5. Apache Geode 过期
默认情况下,Apache Geode 配置了一个区域条目,空闲超时(TTI)过期策略,使用30分钟的过期超时并将条目设置为 INVALIDATE 作为操作。这意味着当用户的会话保持不活动状态(即空闲)超过30分钟时,会话将过期并失效,用户必须开始一个新的会话才能继续使用该应用程序。
然而,如果你对会话状态管理和过期有特定的应用需求,并且使用默认的空闲超时 (TTI) 过期策略无法满足你的用例 (UC),该怎么办呢?
现在,Spring Session for Apache Geode 支持特定应用程序的自定义过期策略。作为应用程序开发者,您可以指定自定义规则来管理由 Spring Session(由 Apache Geode 支持)维护的会话过期。
Spring Session for Apache Geode 提供了新的 SessionExpirationPolicy 策略 接口。
@FunctionalInterface
interface SessionExpirationPolicy {
// determine timeout for expiration of individual Session
Optional<Duration> determineExpirationTimeout(Session session);
// define the action taken on expiration
default ExpirationAction getExpirationAction() {
return ExpirationAction.INVALIDATE;
}
enum ExpirationAction {
DESTROY,
INVALIDATE
}
}
您需要实现此接口以指定应用程序所需的会话过期策略,然后将实例注册为 Spring 应用程序上下文中的一个 bean。
使用 @EnableGemFireHttpSession 注解,sessionExpirationPolicyBeanName 属性来配置实现自定义应用程序策略和会话过期规则的 SessionExpirationPolicy bean 的名称。
例如:
SessionExpirationPolicyclass MySessionExpirationPolicy implements SessionExpirationPolicy {
public Duration determineExpirationTimeout(Session session) {
// return a java.time.Duration specifying the length of time until the Session should expire
}
}
然后,在您的应用程序类中,简单声明以下内容:
SessionExpirationPolicy 配置@SpringBootApplication
@EnableGemFireHttpSession(
maxInactiveIntervalInSeconds = 600,
sessionExpirationPolicyBeanName = "expirationPolicy"
)
class MySpringSessionApplication {
@Bean
SessionExpirationPolicy expirationPolicy() {
return new MySessionExpirationPolicy();
}
}
或者,可以使用spring.session.data.gemfire.session.expiration.bean-name属性配置SessionExpirationPolicy bean的名称, 或者通过在Spring容器中声明一个SpringSessionGemFireConfigurer bean并重写getSessionExpirationPolicyBeanName()方法来实现。 |
您只需要实现 determineExpirationTimeout(:Session):Optional<Duration> 方法,该方法封装了确定 Session 何时过期的规则。Session 的过期超时时间表示为一个 Optional 的 java.time.Duration,它指定了 Session 过期前的时间长度。
determineExpirationTimeout 方法可以是会话特定的,并且可能随着每次调用而改变。
您可以选择实现 getAction 方法,以指定 Session 过期时采取的操作。默认情况下,区域条目(即 Session)会被标记为无效。另一个选项是在过期时销毁区域条目,这将同时移除键(Session ID)和值(Session)。而失效操作仅移除值。
在底层,SessionExpirationPolicy 被适配为 Apache Geode 的 CustomExpiry 接口的一个实例。 这个 Spring Session 的 CustomExpiry 对象随后被设置为 Session 区域的 自定义条目空闲超时过期策略。 |
在过期判定期间,每次过期线程运行时都会对区域中的每个条目(即会话)调用CustomExpiry.getExpiry(:Region.Entry<String, Session>):ExpirationAttributes方法,该方法又会调用我们的SessionExpirationPolicy.determineExpirationTimout(:Session):Optional<Duration>方法。返回的java.time.Duration值会被转换为秒,并作为从ExpirationAttributes方法调用返回的CustomExpiry.getExpiry(..)中的过期超时时间。 |
Apache Geode的过期线程每秒运行一次,评估Region中的每个条目(即Session),以确定该条目是否已过期。您可以通过gemfire.EXPIRY_THREADS属性控制过期线程的数量。更多详细信息,请参阅Apache Geode的文档。 |
3.5.1. 过期超时配置
如果要基于 @EnableGemFireHttpSession 注解、maxInactiveIntervalInSeconds 属性,或者对应的 spring.session.data.gemfire.session.expiration.max-inactive-interval-seconds 属性来设置自定义 SessionExpirationPolicy 的过期超时时间,那么您的自定义 SessionExpirationPolicy 实现还可以实现 SessionExpirationTimeoutAware 接口。
SessionExpirationTimeoutAware 接口定义如下:
interface SessionExpirationTimeoutAware {
void setExpirationTimeout(Duration expirationTimeout);
}
当您的自定义 SessionExpirationPolicy 实现同时实现了 SessionExpirationTimeoutAware 接口时, Apache Geode 的 Spring Session 将为您的实现提供来自 @EnableGemFireHttpSession 注解、maxInactiveIntervalInSeconds 属性的值, 或者如果设置了 spring.session.data.gemfire.session.expiration.max-inactive-interval-seconds 属性,或者是来自 Spring 应用上下文中声明的任何 SpringSessionGemFireConfigurer bean,作为 java.time.Duration 的实例。
如果使用了多个配置选项,以下顺序优先:
-
SpringSessionGemFireConfigurer.getMaxInactiveIntervalInSeconds() -
spring.session.data.gemfire.session.expiration.max-inactive-interval-seconds属性 -
@EnableGemFireHttpSession注解,maxInactiveIntervalInSeconds属性
3.5.2. 固定超时到期
为了提供更大的便利,Spring Session for Apache Geode 提供了 SessionExpirationPolicy 接口的实现,用于固定时长的过期(或如核心 Spring Session 中所述的“绝对会话超时” 问题 #922)。
在某些情况下,比如出于安全原因,可能有必要在固定的时长后(例如每小时)使用户的会话过期,无论用户的会话是否仍然处于活动状态。
Spring Session for Apache Geode 提供了针对此确切用例 (UC) 的 FixedTimeoutSessionExpirationPolicy 实现开箱即用。除了处理固定时长的过期外,它还谨慎地考虑并应用默认的空闲过期超时。
例如,考虑这样一种场景:用户登录后开始一个会话(Session),活跃了10分钟,然后离开,使会话处于空闲状态。如果固定时长的过期超时设置为60分钟,但空闲过期超时仅设置为30分钟,并且用户没有返回,那么该会话应该在40分钟后过期,而不是在固定时长过期时的60分钟。
相反,如果用户连续忙碌40分钟,从而使Session保持活动状态,避免了30分钟的空闲超时,然后离开,那么我们的固定持续时间超时应该启动,并在60分钟时使用户的Session过期,即使用户的空闲超时直到70分钟时才会发生(40分钟(活跃)+ 30分钟(空闲)= 70分钟)。
嗯,这正是 FixedTimeoutSessionExpirationPolicy 所做的。
要配置FixedTimeoutSessionExpirationPolicy,请执行以下操作:
@SpringBootApplication
@EnableGemFireHttpSession(sessionExpirationPolicyBeanName = "fixedTimeoutExpirationPolicy")
class MySpringSessionApplication {
@Bean
SessionExpirationPolicy fixedTimeoutExpirationPolicy() {
return new FixedTimeoutSessionExpirationPolicy(Duration.ofMinutes(60L));
}
}
在上面的示例中,FixedTimeoutSessionExpirationPolicy 在 Spring 应用上下文中被声明为一个 bean,并初始化为 60 分钟的固定持续时间过期超时。因此,用户的会话将在空闲超时(默认为 30 分钟)之后或固定超时(配置为 60 分钟)之后过期,以先发生的情况为准。
通过使用 Spring Session for Apache Geode FixedDurationExpirationSessionRepositoryBeanPostProcessor,还可以实现基于会话访问的延迟、固定持续时间过期超时。此 BPP 将任何特定于数据存储的 SessionRepository 包装在一个 FixedDurationExpirationSessionRepository 实现中,该实现仅在访问会话时评估其过期情况。这种方法与底层数据存储无关,因此可以与任何 Spring Session 提供程序一起使用。过期判定完全基于会话的 creationTime 属性和指定固定持续时间过期超时的必要参数 java.time.Duration。 |
不应在严格的过期超时情况下使用 FixedDurationExpirationSessionRepository,例如当会话在固定的持续时间过期超时后必须立即过期的情况。此外,与 FixedTimeoutSessionExpirationPolicy 不同,FixedDurationExpirationSessionRepository 不考虑空闲过期超时。也就是说,它仅在确定给定会话的过期超时时使用固定的持续时间。 |
3.5.3. SessionExpirationPolicy 链式调用
使用组合软件设计模式,您可以将一组SessionExpirationPolicy实例视为单个实例,其行为就像过滤器链本身一样,类似于Servlet过滤器的链式结构。
组合软件设计模式是一种强大的模式,并且受到 SessionExpirationPolicy、 @FunctionalInterface 的支持,只需从 determineExpirationTimeout 方法中返回一个 java.time.Duration 的 Optional 即可。
这允许每个组合的 SessionExpirationPolicy 仅在过期时间可以由此实例确定时“可选地”返回一个 Duration。或者,此实例可能会将处理推给组合中的下一个 SessionExpirationPolicy,或沿着链条处理,直到返回一个非空的过期超时时间,或者最终没有返回任何过期超时时间。
实际上,这个策略被 FixedTimeoutSessionExpirationPolicy 内部使用,当空闲超时将在固定超时之前发生时,将返回 Optional.empty()。通过不返回过期超时,Apache Geode 将服从默认配置的区域管理会话状态的条目空闲超时过期策略。
3.6. Apache Geode 序列化
为了在客户端和服务器之间传输数据,或者当数据在集群中的对等节点之间进行分发和复制时,必须对数据进行序列化。在这种情况下,相关数据是会话(Session)的状态。
每当在客户端/服务器拓扑中持久化或访问会话时,会话的状态都会通过网络发送。 通常,启用了 Spring Session 的 Spring Boot 应用程序将成为 Apache Geode 节点集群中服务器的客户端。
在服务器端,会话状态可能会分布在集群中的多个服务器(数据节点)上,以复制数据并保证会话状态的高可用性。当使用 Apache Geode 时,数据可以进行分区或分片,并且可以指定冗余级别。当数据被分布以进行复制时,还必须将其序列化,以便在集群中的对等节点之间传输会话状态。
开箱即用,Apache Geode 支持 Java 序列化。使用 Java 序列化 有许多优点, 例如处理对象图中的循环引用,或者被任何用 Java 编写的程序普遍支持。 然而,Java 序列化 非常冗长,并且不是最高效的网络传输格式。
因此,Apache Geode 提供了自己的序列化框架来序列化 Java 类型:
3.6.1. Apache Geode 序列化背景
如上所述,Apache Geode 提供了两种额外的序列化框架: 数据序列化 和 PDX 序列化。
数据序列化
数据序列化 是一种非常高效的格式(即 快速 且 紧凑),与 Java 序列化 相比,开销很小。
它通过仅发送实际发生改变的数据位而不是整个对象来支持增量传播,这不仅减少了网络中传输的数据量,还减少了数据持久化或溢出到磁盘时的IO量。
然而,数据序列化在任何时候通过网络传输数据,或者将数据持久化/溢出到磁盘并从磁盘访问时,都会带来CPU开销,因为接收端需要执行反序列化操作。事实上,每当使用增量传播时,对象必须在接收端反序列化,以便应用“增量”。Apache Geode 通过调用实现了org.apache.geode.Delta接口的对象上的方法来应用增量。显然,你无法在序列化的对象上调用方法。
PDX
PDX,另一方面,代表可移植数据交换,保留了数据发送时的格式。 例如,如果客户端以PDX格式向服务器发送数据,服务器将保留这些数据为PDX序列化的字节 并将其存储在缓存Region中,这是针对数据访问操作的目标缓存。
此外,PDX,顾名思义,是“可移植的”,这意味着它允许 Java 和原生语言客户端(如 C、C++ 和 C# 客户端)在相同的数据集上进行互操作。
PDX 甚至允许在序列化字节上执行 OQL 查询,而无需先反序列化对象来评估查询谓词并执行查询。这可以通过 Apache Geode 实现,因为 Apache Geode 维护了一个“类型注册表”,其中包含使用 PDX 序列化并存储在 Apache Geode 中的对象的类型元数据。
然而,可移植性确实带来了一定的代价,其开销略高于数据序列化。尽管如此,PDX 的效率和灵活性远高于Java 序列化,后者将类型元数据存储在对象的序列化字节中,而不是像 Apache Geode 在使用 PDX 时那样存储在一个独立的类型注册表中。
PDX 不支持增量(Deltas)。从技术上讲,一个 PDX 可序列化的对象可以通过实现 org.apache.geode.Delta 接口用于 增量传播,即使在 PDX 的上下文中,也只会发送“增量”部分。 但是,PDX 序列化的对象必须反序列化以应用增量。 请记住,调用方法来应用增量违背了最初使用 PDX 的目的。
当开发管理 {data-store-name} 集群中数据的原生客户端(例如 C)时,甚至是将原生客户端与 Java 客户端混合使用时,通常集群中的服务器 classpath 上不会提供任何关联的 Java 类型。 使用 PDX 时,不需要在 classpath 中提供 Java 类型,许多仅开发和使用原生客户端来访问存储在 {data-store-name} 中的数据的用户不会为其对应的 C/C++/C# 类型提供任何 Java 类型。
Apache Geode 还支持 JSON 与 PDX 之间的序列化。在这种情况下,由于支持 JSON 的许多不同语言(例如 JavaScript、Python、Ruby)可以与 Apache Geode 一起使用,因此服务器的类路径中很可能不会提供 Java 类型。
尽管如此,即使使用 PDX,用户也必须小心,避免在集群中的服务器上对 PDX 序列化的对象进行反序列化。
例如,考虑对以下 Java 类型的对象进行 OQL 查询,该对象被序列化为 PDX…
@Region("People")
class Person {
private LocalDate birthDate;
private String name;
public int getAge() {
// no explicit 'age' field/property in Person
// age is just implemented in terms of the 'birthDate' field
}
}
随后,如果 OQL 查询调用 Person 对象上的方法,例如:
SELECT * FROM /People p WHERE p.age >= 21
然后,这将导致一个PDX序列化的Person对象被反序列化,因为age不是Person的字段, 而是包含基于另一个字段(即Person,也就是birthDate)计算的方法。同样,在OQL查询中调用任何java.lang.Object方法,例如Object.toString(),也会导致发生反序列化。
Apache Geode 确实提供了 read-serialized 配置设置,以便任何可能在 Function 内部调用的缓存 Region.get(key) 操作不会导致 PDX 序列化的对象被反序列化。但是,没有什么能阻止设计不当的 OQL 查询导致反序列化,所以要小心。
数据序列化 + PDX + Java 序列化
Apache Geode 可以同时支持所有三种序列化格式。
例如,您的应用程序领域模型可能包含实现 java.io.Serialiable 接口的对象, 并且您可能正在结合使用 数据序列化 框架和 PDX。
| 虽然可以将Java序列化与Data Serialization和PDX一起使用,但通常建议并推荐使用一种序列化策略。 |
| Unlike Java 序列化, 数据序列化 和 PDX 序列化 不处理对象图循环。 |
有关 Apache Geode 的序列化机制的更多背景信息,请参见 此处。
3.6.2. 使用 Spring Session 进行序列化
以前,适用于 Apache Geode 的 Spring Session 仅支持 Apache Geode 的 数据序列化 格式。这样做的主要动机是利用 Apache Geode 的 增量传播 功能,因为会话的状态可能会非常大。
然而,从 Spring Session for Apache Geode 2.0 开始,PDX 也得到了支持,并且现在已成为新的默认序列化选项。在 Spring Session for Apache Geode 2.0 中,默认值更改为 PDX,主要是因为 PDX 是用户最广泛使用和请求的格式。
PDX 无疑是最灵活的格式,以至于您甚至不需要在集群服务器的类路径中使用 Spring Session for Apache Geode 或其任何传递依赖项,就可以将 Spring Session 与 Apache Geode 一起使用。事实上,使用 PDX 时,您甚至也不需要将存储在(HTTP)会话中的应用程序域对象类型放在服务器的类路径中。
本质上,当使用 PDX 序列化时,Apache Geode 不要求相关的 Java 类型存在于服务器的类路径中。只要集群中的服务器不发生反序列化,就是安全的。
@EnableGemFireHttpSession 注解引入了 新的 sessionSerializerBeanName 属性,用户可以使用该属性来配置在 Spring 容器中声明和注册的 bean 的名称,该 bean 实现了所需的序列化策略。Spring Session for Apache Geode 使用此序列化策略来序列化会话状态。
开箱即用,Spring Session for Apache Geode 提供了两种序列化策略:一种用于 PDX,另一种用于 数据序列化。它会自动在 Spring 容器中注册这两个序列化策略的 Bean。 然而,在运行时实际上只使用了其中一种策略,即 PDX!
在 Spring 容器中注册的两个实现 数据序列化 和 PDX 的 bean 分别命名为 SessionDataSerializer 和 SessionPdxSerializer。默认情况下,sessionSerializerBeanName 属性 被设置为 SessionPdxSerializer,就好像用户使用以下方式注解了其启用了 Spring Boot 和 Spring Session 的应用程序 配置类:
@SpringBootApplication
@EnableGemFireHttpSession(sessionSerializerBeanName = "SessionPdxSerializer")
class MySpringSessionApplication { }
通过将 sessionSerializerBeanName 属性设置为 SessionDataSerializer,可以轻松地将序列化策略更改为 数据序列化,如下所示:
@SpringBootApplication
@EnableGemFireHttpSession(sessionSerializerBeanName = "SessionDataSerializer")
class MySpringSessionApplication { }
由于这两个值非常常见,Spring Session for Apache Geode 在 GemFireHttpSessionConfiguration 类中为每个值提供了常量: GemFireHttpSessionConfiguration.SESSION_PDX_SERIALIZER_BEAN_NAME 和 GemFireHttpSessionConfiguration.SESSION_DATA_SERIALIZER_BEAN_NAME。因此,您可以按如下方式显式配置 PDX:
import org.springframework.session.data.geode.config.annotation.web.http.GemFireHttpSessionConfiguration;
@SpringBootApplication
@EnableGemFireHttpSession(sessionSerializerBeanName = GemFireHttpSessionConfiguration.SESSION_PDX_SERIALIZER_BEAN_NAME)
class MySpringSessionApplication { }
通过1个属性和2个开箱即用的提供的bean定义,您可以指定希望在由Apache Geode支持的启用了Spring Boot、Spring Session的应用程序中使用的序列化框架。
3.6.3. Spring Session for Apache Geode 序列化框架
为了抽象出 Apache Geode 的 数据序列化 和 PDX 序列化 框架的细节, Spring Session for Apache Geode 提供了自己的序列化框架(外观),该框架封装了 Apache Geode 的序列化框架。
序列化 API 位于 org.springframework.session.data.gemfire.serialization 包下。此 API 中的主要接口是 org.springframework.session.data.gemfire.serialization.SessionSerializer。
接口定义如下:
SessionSerializer 接口interface SessionSerializer<T, IN, OUT> {
void serialize(T session, OUT out);
T deserialize(IN in);
boolean canSerialize(Class<?> type);
boolean canSerialize(Object obj) {
// calls Object.getClass() in a null-safe way and then calls and returns canSerialize(:Class)
}
}
基本上,该接口允许您序列化和反序列化一个 Spring Session 对象。
The IN and OUT 类型参数以及对应类型的方法参数提供了对负责将 Session 写入字节流或从字节流中读取 Session 的对象的引用。实际的参数将根据配置的基础 Apache Geode 序列化策略而特定于类型。
例如,当使用 Apache Geode 的 PDX 序列化框架时,IN 和 OUT 将分别为 org.apache.geode.pdx.PdxReader 和 org.apache.geode.pdx.PdxWriter 的实例。 当配置了 Apache Geode 的 数据序列化框架时,则 IN 和 OUT 将分别为 java.io.DataInput 和 java.io.DataOutput 的实例。
这些参数由框架自动提供给SessionSerializer实现,并且如前所述,基于配置的底层 Apache Geode 序列化策略。
本质上,尽管 Spring Session for Apache Geode 提供了围绕 Apache Geode 的序列化框架的外观,但在底层,Apache Geode 仍然期望使用这些序列化框架之一来将数据序列化到 Apache Geode 或从其中序列化出来。
那么SessionSerializer接口到底有什么作用呢?
实际上,它允许用户自定义会话状态中哪些部分会被序列化并存储在 Apache Geode 中。应用开发者可以提供他们自己的、特定于应用的 SessionSerializer 实现,将其注册为 Spring 容器中的一个 bean,然后配置它以供 Spring Session for Apache Geode 使用,从而序列化会话状态,具体如下:
@EnableGemFireHttpSession(sessionSerializerBeanName = "MyCustomSessionSerializer")
class MySpringSessionDataGemFireApplication {
@Bean("MyCustomSessionSerializer")
SessionSerializer<Session, ?, ?> myCustomSessionSerializer() {
// ...
}
}
实现一个 SessionSerializer
Spring Session for Apache Geode 提供了帮助,当用户想要实现一个自定义的 SessionSerializer,并使其适配到 Apache Geode 的序列化框架之一时。
如果用户只是直接实现 org.springframework.session.data.gemfire.serialization.SessionSerializer 接口, 而没有继承 Spring Session for Apache Geode 提供的与 Apache Geode 的序列化框架之一相关的抽象基类, 那么 Spring Session for Apache Geode 将把用户的自定义 SessionSerializer 实现包装在 org.springframework.session.data.gemfire.serialization.pdx.support.PdxSerializerSessionSerializerAdapter 的实例中, 并将其作为 org.apache.geode.pdx.PdxSerializer 注册到 Apache Geode。
Spring Session for Apache Geode 不会覆盖用户可能已通过其他方式注册的任何现有的 PdxSerializer 实现。实际上,Apache Geode 的 org.apache.geode.pdx.PdxSerializer 接口存在几种不同的实现:
-
Apache Geode 本身提供了
org.apache.geode.pdx.ReflectionBasedAutoSerializer。 -
Spring Data for Apache Geode (SDG) 提供了
org.springframework.data.gemfire.mapping.MappingPdxSerializer, 它被用于 SD Repository 抽象和 SDG 的扩展中,以处理将 PDX 序列化类型映射到应用程序 Repository 接口中定义的应用程序域对象类型。
这是通过获取缓存中任何当前注册的 PdxSerializer 实例并将其与包装用户自定义应用程序 SessionSerializer 实现的 PdxSerializerSessionSerializerAdapter 组合,然后将此“复合” PdxSerializer 重新注册到 Apache Geode 缓存中来完成的。这个“复合” PdxSerializer 实现由 Spring Session for Apache Geode 的 org.springframework.session.data.gemfire.pdx.support.ComposablePdxSerializer 类提供,当实体以 PDX 格式存储在 Apache Geode 中时。
如果当前没有其他 PdxSerializer 注册到 Apache Geode 缓存中,则适配器将直接注册。
当然,您可以通过以下方法之一强制使用自定义的底层 Apache Geode 序列化策略,SessionSerializer 实现:
-
自定义
SessionSerializer实现可以实现 Apache Geode 的org.apache.geode.pdx.PdxSerializer接口,或者为了方便起见,继承 Spring Session for Apache Geode 的org.springframework.session.data.gemfire.serialization.pdx.AbstractPdxSerializableSessionSerializer类,Spring Session for Apache Geode 将把自定义的SessionSerializer注册为 Apache Geode 的PdxSerializer。 -
自定义
SessionSerializer实现可以扩展 Apache Geode 的org.apache.geode.DataSerializer类,或者为了方便起见,扩展 Spring Session for Apache Geode 的org.springframework.session.data.gemfire.serialization.data.AbstractDataSerializableSessionSerializer类,Spring Session for Apache Geode 将注册自定义的SessionSerializer作为DataSerializer到 Apache Geode。 -
最后,用户可以像以前一样创建一个自定义的
SessionSerializer实现,而无需指定使用哪个 Apache Geode 序列化框架,因为自定义的SessionSeriaizer实现未实现任何 Apache Geode 序列化接口, 也未继承 Spring Session for Apache Geode 提供的任何抽象基类,但仍然可以将其注册为 Apache Geode 中的DataSerializer, 只需在 Spring 容器中声明一个额外的 Spring Session for Apache Geode bean,类型为org.springframework.session.data.gemfire.serialization.data.support.DataSerializerSessionSerializerAdapter,如下所示…
@EnableGemFireHttpSession(sessionSerializerBeanName = "customSessionSerializer")
class Application {
@Bean
DataSerializerSessionSerializerAdapter dataSerializerSessionSerializer() {
return new DataSerializerSessionSerializerAdapter();
}
@Bean
SessionSerializer<Session, ?, ?> customSessionSerializer() {
// ...
}
}
只需在 Spring 容器中注册为 bean 的 DataSerializerSessionSerializerAdapter 的存在, 任何中立的自定义 SessionSerializer 实现都将被视为并注册为 Apache Geode 中的 DataSerializer。
数据序列化的额外支持
| 如果您正在使用 Spring (Boot) 配置和引导集群中的 Apache Geode 服务器,请随时跳过本节,因为通常情况下,接下来的信息并不适用。当然,这也取决于您声明的依赖项和 Spring 配置。但是,如果您使用 Gfsh 启动集群中的服务器,那么请务必继续阅读。 |
背景
当使用 Apache Geode 的 DataSerialization 框架时,特别是从客户端将(HTTP)会话状态序列化到集群中的服务器时,必须注意在集群中配置 Apache Geode 服务器的适当依赖项。 当利用增量时尤其如此,正如前面关于 Data Serialization 的部分所解释的那样。
当使用 DataSerialization 框架作为序列化策略,将来自您的 Web 应用程序客户端的(HTTP)会话状态序列化到服务器时,服务器必须正确配置用于表示(HTTP)会话及其内容的 Spring Session for Apache Geode 类型。这意味着需要将 Spring 的 JAR 文件包含在服务器的类路径中。
此外,使用 DataSerialization 可能还需要您包含应用程序域的 JAR 文件,这些文件被您的 Web 应用程序使用并作为会话属性值放入(HTTP)会话中,特别是当:
-
您的类型实现了
org.apache.geode.DataSerializable接口。 -
您的类型实现了
org.apache.geode.Delta接口。 -
您已经注册了一个
org.apache.geode.DataSerializer,用于标识和序列化类型。 -
您的类型实现了
java.io.Serializable接口。
当然,您必须确保应用程序域对象类型放入(HTTP)会话中时以某种形式可序列化。但是,如果满足以下条件,您并不严格要求使用 DataSerialization,也不一定需要将应用程序域对象类型放在服务器的类路径中:
-
您的类型实现了
org.apache.geode.pdx.PdxSerializable接口。 -
或者,您已经注册了一个
org.apache.geode.pdx.PdxSerializer,它可以正确识别和序列化您的应用程序域对象类型。
Apache Geode 在确定用于序列化对象图的序列化策略时,将按照以下优先顺序应用:
-
首先,
DataSerializable对象和/或任何注册的DataSerializers用于标识要序列化的对象。 -
然后
PdxSerializable对象和/或任何已注册的PdxSerializer用于标识要序列化的对象。 -
And finally, all
java.io.Serializabletypes.
这也意味着,如果某个特定的应用领域对象类型(例如 A)实现了 java.io.Serializable, 然而,一个(自定义的)PdxSerializer 已经在 Apache Geode 中注册,并且标识了相同的应用领域对象类型(即 A),那么在这种情况下,Apache Geode 将使用 PDX 来序列化 \"A\",而不会使用 Java 序列化。
这特别有用,因为您可以使用DataSerialization来序列化(HTTP)Session对象,利用增量更新和DataSerialization的所有强大功能,但随后可以使用PDX来序列化您的应用程序域对象类型,从而大大简化了配置和/或所需的工作量。
现在我们已经大致了解了为什么会有这样的支持,那么如何启用它呢?
配置
首先,创建一个 Apache Geode cache.xml,如下所示:
cache.xml 配置<?xml version="1.0" encoding="UTF-8"?>
<cache xmlns="http://geode.apache.org/schema/cache"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://geode.apache.org/schema/cache https://geode.apache.org/schema/cache/cache-1.0.xsd"
version="1.0">
<initializer>
<class-name>
org.springframework.session.data.gemfire.serialization.data.support.DataSerializableSessionSerializerInitializer
</class-name>
</initializer>
</cache>
然后,在*Gfsh*中使用以下命令启动您的服务器:
gfsh> start server --name=InitializedServer --cache-xml-file=/path/to/cache.xml --classpath=...
使用适当的依赖项配置 Apache Geode 服务器 classpath 是比较棘手的部分,但通常情况下,以下方法应该可行:
gfsh> set variable --name=REPO_HOME --value=${USER_HOME}/.m2/repository
gfsh> start server ... --classpath=\
${REPO_HOME}/org/springframework/spring-core/{spring-version}/spring-core-{spring-version}.jar\
:${REPO_HOME}/org/springframework/spring-aop/{spring-version}/spring-aop-{spring-version}.jar\
:${REPO_HOME}/org/springframework/spring-beans/{spring-version}/spring-beans-{spring-version}.jar\
:${REPO_HOME}/org/springframework/spring-context/{spring-version}/spring-context-{spring-version}.jar\
:${REPO_HOME}/org/springframework/spring-context-support/{spring-version}/spring-context-support-{spring-version}.jar\
:${REPO_HOME}/org/springframework/spring-expression/{spring-version}/spring-expression-{spring-version}.jar\
:${REPO_HOME}/org/springframework/spring-jcl/{spring-version}/spring-jcl-{spring-version}.jar\
:${REPO_HOME}/org/springframework/spring-tx/{spring-version}/spring-tx-{spring-version}.jar\
:${REPO_HOME}/org/springframework/data/spring-data-commons/{spring-data-commons-version}/spring-data-commons-{spring-data-commons-version}.jar\
:${REPO_HOME}/org/springframework/data/spring-data-geode/{spring-data-geode-version}/spring-data-geode-{spring-data-geode-version}.jar\
:${REPO_HOME}/org/springframework/session/spring-session-core/{spring-session-core-version}/spring-session-core-{spring-session-core-version}.jar\
:${REPO_HOME}/org/springframework/session/spring-session-data-geode/{spring-session-data-geode-version}/spring-session-data-geode-{spring-session-data-geode-version}.jar\
:${REPO_HOME}/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar
请记住,您可能还需要将应用程序的域对象 JAR 文件添加到服务器的类路径中。
要全面了解其工作原理,请参阅示例。
自定义变更检测
默认情况下,任何时候只要会话被修改(例如,lastAccessedTime 更新为当前时间),Spring Session for Apache Geode (SSDG) 就会将会话视为已更改。在使用 Apache Geode 的 数据序列化 框架时,充分利用 Apache Geode 的 增量传播 功能也是非常有用和有价值的。
当使用数据序列化时,SSDG还使用增量传播仅在客户端和服务器之间发送会话状态的更改。这包括可能已添加、删除或更新的任何会话属性。
默认情况下,任何时候调用Session.setAttribute(name, value)时,Session属性都被认为是“脏的”, 并会在客户端和服务器之间的增量中发送。即使您的应用程序域对象未发生更改,也是如此。
通常情况下,除非您的对象已发生更改,否则没有任何理由调用 Session.setAttribute(..)。然而,如果这种情况可能发生,并且您的对象相对较大(具有复杂对象层次结构),那么您可以考虑以下两种方法之一:
-
在您的应用程序领域对象模型中实现Delta接口,尽管非常有用,但具有很强的侵入性,或者…
-
提供 SSDG 的
org.springframework.session.data.gemfire.support.IsDirtyPredicate策略接口的自定义实现。
开箱即用,SSDG 提供了 IsDirtyPredicate 策略接口的 5 种实现:
| 类 | 描述 | 默认 |
|---|---|---|
|
新的会话属性值始终被视为已更改。 |
|
|
新的会话属性值永远不会被视为脏数据。 |
|
|
当旧值和新值不同时,新的 Session 属性值被认为是脏的,如果新值的类型没有实现 |
是的 |
|
新的 Session 属性值被认为是脏的,当且仅当旧值与新值通过 |
|
|
新的会话属性值被认为是脏的,当且仅当使用恒等等于运算符(即 |
如上表所示,DeltaAwareDirtyPredicate 是 SSDG 使用的默认实现。 DeltaAwareDirtyPredicate 会自动考虑实现了 Apache Geode Delta 接口的应用程序域对象。但是,即使您的应用程序域对象未实现 Delta 接口,DeltaAwareDirtyPredicate 仍然可以工作。只要调用了 Session.setAttribute(name, newValue) 且新值与旧值不同,或者新值未实现 Delta 接口,SSDG 就会认为您的应用程序域对象已被修改。
您可以通过在 Spring 容器中声明一个 IsDirtyPredicate 接口类型的 bean,轻松更改 SSDG 的脏实现和决策策略:
IsDirtyPredicate 策略@EnableGemFireHttpSession
class ApplicationConfiguration {
@Bean
IsDirtyPredicate equalsDirtyPredicate() {
return EqualsDirtyPredicate.INSTANCE;
}
}
组合
IsDirtyPredicate 接口还提供了 andThen(:IsDirtyPredicate) 和 orThen(:IsDirtyPredicate) 方法, 用于将两个或多个 IsDirtyPredicate 实现组合在一起,以组织复杂的逻辑和规则, 来确定某个应用程序域对象是否为脏。
例如,你可以使用 OR 运算符同时组合 EqualsDirtyPredicate 和 DeltaAwareDirtyPredicate:
EqualsDirtyPredicate 与 DeltaAwareDirtyPredicate 组合@EnableGemFireHttpSession
class ApplicationConfiguration {
@Bean
IsDirtyPredicate equalsOrThenDeltaDirtyPredicate() {
return EqualsDirtyPredicate.INSTANCE
.orThen(DeltaAwareDirtyPredicate.INSTANCE);
}
}
您甚至可以基于特定应用程序域对象类型实现自己的自定义 IsDirtyPredicates:
IsDirtyPredicate 实现class CustomerDirtyPredicate implements IsDirtyPredicate {
public boolean isDirty(Object oldCustomer, Object newCustomer) {
if (newCustomer instanceof Customer) {
// custom logic to determine if a new Customer is dirty
}
return true;
}
}
class AccountDirtyPredicate implements IsDirtyPredicate {
public boolean isDirty(Object oldAccount, Object newAccount) {
if (newAccount instanceof Account) {
// custom logic to determine if a new Account is dirty
}
return true;
}
}
然后将 CustomerDirtyPredicate 与 AccountDirtyPredicate 以及用于回退的默认谓词结合,如下所示:
IsDirtyPredicates@EnableGemFireHttpSession
class ApplicationConfiguration {
@Bean
IsDirtyPredicate typeSpecificDirtyPredicate() {
return new CustomerDirtyPredicate()
.andThen(new AccountDirtyPredicate())
.andThen(IsDirtyPredicate.ALWAYS_DIRTY);
}
}
组合和可能性是无限的。
实现自定义 IsDirtyPredicate 策略时请务必小心。如果您错误地判断您的应用程序域对象未被修改,而实际上它已被修改,那么它将不会在会话增量中从客户端发送到服务器。 |
更改会话表示形式
Spring Session for Apache Geode 在内部维护了 (HTTP) Session 和 Session 属性的两种表示形式。每种表示形式取决于是否支持 Apache Geode 的“增量”。
Apache Geode 增量传播 仅在使用 数据序列化 时由 Spring Session for Apache Geode 启用,原因已在前面讨论过。
策略实际上是:
-
如果配置了 Apache Geode 数据序列化,则支持 增量,并使用
DeltaCapableGemFireSession和DeltaCapableGemFireSessionAttributes表示形式。 -
如果配置了 Apache Geode PDX 序列化,那么 增量传播 将被禁用, 并使用
GemFireSession和GemFireSessionAttributes表示形式。
可以覆盖 Spring Session 为 Apache Geode 使用的这些内部表示形式,并且允许用户 提供他们自己的与会话相关的类型。唯一的严格要求是,会话实现 必须实现核心 Spring Session 的 org.springframework.session.Session 接口。
举例来说,假设您想定义自己的 Session 实现。
首先,你定义了Session类型。也许你的自定义Session类型甚至封装并处理了Session属性,而无需定义单独的类型。
class MySession implements org.springframework.session.Session {
// ...
}
然后,您需要扩展 org.springframework.session.data.gemfire.GemFireOperationsSessionRepository 类,并重写 createSession() 方法以创建自定义 Session 实现类的实例。
class MySessionRepository extends GemFireOperationsSessionRepository {
@Override
public Session createSession() {
return new MySession();
}
}
如果你提供了自己的自定义 SessionSerializer 实现,并且配置了 Apache Geode PDX 序列化,那么你已经完成了。
然而,如果你配置了 Apache Geode 数据序列化,那么你必须额外提供一个自定义的 SessionSerializer 接口实现,并且让它直接继承 Apache Geode 的 org.apache.geode.DataSerializer 类,或者继承 Spring Session for Apache Geode 的 org.springframework.session.data.gemfire.serialization.data.AbstractDataSerializableSessionSerializer 类, 并重写 getSupportedClasses():Class<?>[] 方法。
例如:
class MySessionSerializer extends AbstractDataSerializableSessionSerializer {
@Override
public Class<?>[] getSupportedClasses() {
return new Class[] { MySession.class };
}
}
很遗憾,getSupportedClasses() 无法返回通用的 Spring Session org.springframework.session.Session 接口类型。如果可以的话,我们就可以避免在自定义的 DataSerializer 实现中显式重写 getSupportedClasses() 方法。但是,Apache Geode 的 数据序列化 框架只能匹配确切的类类型,因为它错误地将类类型按名称进行内部存储和引用,这就要求用户重写并实现 getSupportedClasses() 方法。
3.7. HttpSession集成的工作原理
幸好,javax.servlet.http.HttpSession 和 javax.servlet.http.HttpServletRequest(用于获取 HttpSession 的 API)都是接口。这意味着我们可以为这些 API 提供自己的实现。
本节描述了 Spring Session 如何提供与 javax.servlet.http.HttpSession 的透明集成。 目的是让用户了解底层的运行机制。此功能已经实现并集成,因此您无需自己实现此逻辑。 |
首先,我们创建一个自定义的 javax.servlet.http.HttpServletRequest,它返回一个自定义的 javax.servlet.http.HttpSession 实现。它的样子类似于以下内容:
public class SessionRepositoryRequestWrapper extends HttpServletRequestWrapper {
public SessionRepositoryRequestWrapper(HttpServletRequest original) {
super(original);
}
public HttpSession getSession() {
return getSession(true);
}
public HttpSession getSession(boolean createNew) {
// create an HttpSession implementation from Spring Session
}
// ... other methods delegate to the original HttpServletRequest ...
}
任何返回 javax.servlet.http.HttpSession 的方法都会被覆盖。所有其他方法由 javax.servlet.http.HttpServletRequestWrapper 实现,并简单地委托给原始的 javax.servlet.http.HttpServletRequest 实现。
我们使用名为SessionRepositoryFilter的ServletFilter替换了javax.servlet.http.HttpServletRequest实现。伪代码如下所示:
public class SessionRepositoryFilter implements Filter {
public doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
SessionRepositoryRequestWrapper customRequest = new SessionRepositoryRequestWrapper(httpRequest);
chain.doFilter(customRequest, response, chain);
}
// ...
}
通过将自定义的 javax.servlet.http.HttpServletRequest 实现传入 FilterChain,我们确保在我们的 Filter 之后调用的任何内容都使用自定义的 javax.servlet.http.HttpSession 实现。
这突显了为什么 Spring Session 的 SessionRepositoryFilter 必须放置在任何与 javax.servlet.http.HttpSession 交互的内容之前。
3.8. HttpSessionListener
Spring Session 支持 HttpSessionListener,通过将 SessionCreatedEvent 和 SessionDestroyedEvent 转换为 HttpSessionEvent 并声明 SessionEventHttpSessionListenerAdapter 来实现。
要使用此支持,您需要:
-
确保您的
SessionRepository实现支持并配置为触发SessionCreatedEvent和 `SessionDestroyedEvent`。 -
配置
SessionEventHttpSessionListenerAdapter作为 Spring 颗粒度。 -
注入每个
HttpSessionListener到SessionEventHttpSessionListenerAdapter
如果正在使用HttpSession with Apache Geode中记录的配置支持, 那么您需要做的就是将每个HttpSessionListener注册为一个bean。
例如,假设您希望支持 Spring Security 的并发控制,并且需要使用 HttpSessionEventPublisher, 那么您可以简单地将 HttpSessionEventPublisher 添加为一个 bean。
3.10. SessionRepository
A SessionRepository 负责创建、持久化和访问 Session 实例及其状态。
如果可能,开发人员不应直接与 SessionRepository 或 Session 进行交互。相反,开发人员 应优选通过 javax.servlet.http.HttpSession、WebSocket 和 WebSession 集成间接地与 SessionRepository 和 Session 进行交互。
3.11. FindByIndexNameSessionRepository
Spring Session 最基础的使用 Session 的 API 是 SessionRepository。该 API 故意设计得非常简单, 以便能够轻松提供具有基本功能的其他实现。
Some SessionRepository 实现可能还会选择实现 FindByIndexNameSessionRepository。 例如,Spring Session 对 Apache Geode 的支持实现了 FindByIndexNameSessionRepository。
FindByIndexNameSessionRepository 增加了一个用于查找特定用户所有会话的单一方法。 这是通过确保名为 FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME 的会话属性被填充了用户名来实现的。 由于 Spring Session 无法感知所使用的认证机制,因此确保该属性被填充是开发人员的责任。
|
Some implementations of |
3.12. 启用SpringHttpSession
可以将 @EnableSpringHttpSession 注解添加到任何 @Configuration 类,以将 SessionRepositoryFilter 作为名为 \"springSessionRepositoryFilter\" 的 bean 暴露在 Spring 容器中。
为了利用该注解,必须提供一个单独的 SessionRepository bean。
3.13. 启用GemFireHttpSession
The @EnableGemFireHttpSession 注解可以添加到任何 @Configuration 类中,以替代 @EnableSpringHttpSession 注解,从而将 SessionRepositoryFilter 暴露为 Spring 容器中的一个 bean,名称为 “springSessionRepositoryFilter”,并将 Apache Geode 设置为管理 javax.servlet.http.HttpSession 状态的提供者。
当使用 @EnableGemFireHttpSession 注解时,会自动导入额外的配置,同时也提供了名为 GemFireOperationsSessionRepository 的 Apache Geode 特定实现的 SessionRepository 接口。
3.14. GemFireOperationsSessionRepository
GemFireOperationsSessionRepository 是一个使用 Spring Session 为 Apache Geode 的 GemFireOperationsSessionRepository 实现的 SessionRepository 实现。
在 Web 环境中,此存储库与 SessionRepositoryFilter 结合使用。
此实现支持 SessionCreatedEvents、SessionDeletedEvents 和 SessionDestroyedEvents 到 SessionEventHttpSessionListenerAdapter。
3.14.1. 使用索引与 Apache Geode
虽然关于正确定义索引的最佳实践对Apache Geode性能的积极影响超出了本文档的范围,但重要的是要认识到,Spring Session for Apache Geode 创建并使用索引来高效查询和查找会话。
开箱即用,适用于 Apache Geode 的 Spring Session 会在主体名称上创建一个 Hash 类型的索引。有两种不同的内置策略用于查找主体名称。第一种策略是,名称为 FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME 的 Session 属性值将被索引到相同的索引名称。
例如:
String indexName = FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME;
session.setAttribute(indexName, username);
Map<String, Session> idToSessions =
this.sessionRepository.findByIndexNameAndIndexValue(indexName, username);
3.14.2. 使用索引与 Apache Geode 和 Spring Security
或者,Spring Session for Apache Geode 会将 Spring Security 的当前 Authentication#getName() 映射到索引 FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME。
例如,如果你正在使用 Spring Security,你可以通过以下方式找到当前用户会话:
SecurityContext securityContext = SecurityContextHolder.getContext();
Authentication authentication = securityContext.getAuthentication();
String indexName = FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME;
Map<String, Session> idToSessions =
this.sessionRepository.findByIndexNameAndIndexValue(indexName, authentication.getName());
3.14.3. 使用自定义索引与 Apache Geode
这使得开发者可以使用 GemFireOperationsSessionRepository 以编程方式高效地查询并查找所有具有给定主体名称的会话。
此外,当开发者确定一个或多个需要由 Apache Geode 索引的命名会话属性时,Spring Session for Apache Geode 将在实现的会话的 Map 类型 attributes 属性(即任何任意的会话属性)上创建基于范围的索引。
可以使用indexableSessionAttributes属性在@EnableGemFireHttpSession注解中指定会话属性的索引。开发人员希望启用Spring Session对HttpSession的支持时(由Apache Geode提供支持),会将其添加到Spring应用程序的@Configuration类中。
String indexName = "name1";
session.setAttribute(indexName, indexValue);
Map<String, Session> idToSessions =
this.sessionRepository.findByIndexNameAndIndexValue(indexName, indexValue);
Only Session attribute names identified in the @EnableGemFireHttpSession annotation’s indexableSessionAttributes attribute will have an Index defined. All other Session attributes will not be indexed. |
然而,这里有一个注意事项。存储在可索引的 Session 属性中的任何值都必须实现 java.lang.Comparable<T> 接口。如果这些对象值没有实现 Comparable,那么当为具有持久化 Session 数据的区域定义索引时,或者在运行时尝试为可索引的 Session 属性分配一个不是 Comparable 的值并保存 Session 到 Apache Geode 时,Apache Geode 将会在启动时抛出错误。
任何未索引的会话属性可能会存储非Comparable值。 |
要了解更多关于 Apache Geode 的基于范围的索引信息,请参阅 在 Map 字段上创建索引。
要了解有关 Apache Geode 索引的更多信息,请参阅 使用索引。