日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

利用spring session解决共享Session问题

發布時間:2025/4/5 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 利用spring session解决共享Session问题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

https://blog.csdn.net/patrickyoung6625/article/details/45694157

1.共享Session問題

HttpSession是通過Servlet容器創建和管理的,像Tomcat/Jetty都是保存在內存中的。而如果我們把web服務器搭建成分布式的集群,然后利用LVS或Nginx做負載均衡,那么來自同一用戶的Http請求將有可能被分發到兩個不同的web站點中去。那么問題就來了,如何保證不同的web站點能夠共享同一份session數據呢?

最簡單的想法就是把session數據保存到內存以外的一個統一的地方,例如Memcached/Redis等數據庫中。那么問題又來了,如何替換掉Servlet容器創建和管理HttpSession的實現呢? (1)設計一個Filter,利用HttpServletRequestWrapper,實現自己的 getSession()方法,接管創建和管理Session數據的工作。spring-session就是通過這樣的思路實現的。 (2)利用Servlet容器提供的插件功能,自定義HttpSession的創建和管理策略,并通過配置的方式替換掉默認的策略。不過這種方式有個缺點,就是需要耦合Tomcat/Jetty等Servlet容器的代碼。這方面其實早就有開源項目了,例如memcached-session-manager,以及tomcat-redis-session-manager。暫時都只支持Tomcat6/Tomcat7。

2.Spring Session介紹

Spring Session是Spring的項目之一,GitHub地址:https://github.com/spring-projects/spring-session。

Spring Session提供了一套創建和管理Servlet HttpSession的方案。Spring Session提供了集群Session(Clustered Sessions)功能,默認采用外置的Redis來存儲Session數據,以此來解決Session共享的問題。

?

?

?

下面是來自官網的特性介紹:

?

Features

?

Spring Session provides the following features:

  • API and implementations for managing a user's session
  • HttpSession?- allows replacing the HttpSession in an application container (i.e. Tomcat) neutral way
    • Clustered Sessions?- Spring Session makes it trivial to support clustered sessions without being tied to an application container specific solution.
    • Multiple Browser Sessions?- Spring Session supports managing multiple users' sessions in a single browser instance (i.e. multiple authenticated accounts similar to Google).
    • RESTful APIs?- Spring Session allows providing session ids in headers to work with RESTful APIs
  • WebSocket?- provides the ability to keep the HttpSession alive when receiving WebSocket messages

3.集成Spring Session的正確姿勢

?

下面是實際調試通過的例子,包含下面4個步驟:

(1)第一步,添加Maven依賴

?

根據官網Quick Start展示的依賴,在項目pom.xml中添加后各種找不到類引用。于是查看Spring Session項目的build.gradle文件,居然沒有配置依賴的項目,難道還要我自己去找它的依賴,太不專業了吧?!!!

?

[html]?view plaincopy
  • <dependencies>??
  • ????<dependency>??
  • ????????<groupId>org.springframework.session</groupId>??
  • ????????<artifactId>spring-session</artifactId>??
  • ????????<version>1.0.1.RELEASE</version>??
  • ????</dependency>??
  • </dependencies>??
  • 終于在多番仔細研究Spring Session項目源碼之后,看到了spring-session-data-redis項目:

    ?

    build.gradle文件里配置了Spring Session編譯依賴的3個項目:

    ?

    [plain]?view plaincopy
  • apply?from:?JAVA_GRADLE??
  • apply?from:?MAVEN_GRADLE??
  • ??
  • apply?plugin:?'spring-io'??
  • ??
  • description?=?"Aggregator?for?Spring?Session?and?Spring?Data?Redis"??
  • ??
  • dependencies?{??
  • ????compile?project(':spring-session'),??
  • ????????????"org.springframework.data:spring-data-redis:$springDataRedisVersion",??
  • ????????????"redis.clients:jedis:$jedisVersion",??
  • ????????????"org.apache.commons:commons-pool2:$commonsPoolVersion"??
  • ??
  • ????springIoVersions?"io.spring.platform:platform-versions:${springIoVersion}@properties"??
  • }??
  • 于是,真正的Maven依賴改成spring-session-data-redis就OK了:

    ?

    [html]?view plaincopy
  • <dependency>??
  • ????<groupId>org.springframework.session</groupId>??
  • ????<artifactId>spring-session-data-redis</artifactId>??
  • ????<version>1.0.1.RELEASE</version>??
  • </dependency>??
  • (2)第二步,編寫一個配置類,用來啟用RedisHttpSession功能,并向Spring容器中注冊一個RedisConnectionFactory。

    [java]?view plaincopy
  • import?org.springframework.context.annotation.Bean;??
  • import?org.springframework.data.redis.connection.RedisConnectionFactory;??
  • import?org.springframework.data.redis.connection.jedis.JedisConnectionFactory;??
  • import?org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;??
  • ??
  • @EnableRedisHttpSession(maxInactiveIntervalInSeconds?=?7200)??
  • public?class?RedisHttpSessionConfig?{??
  • ??
  • ????@Bean??
  • ????public?RedisConnectionFactory?connectionFactory()?{??
  • ????????JedisConnectionFactory?connectionFactory?=?new?JedisConnectionFactory();??
  • ????????connectionFactory.setPort(6379);??
  • ????????connectionFactory.setHostName("10.18.15.190");??
  • ????????return?connectionFactory;??
  • ????}??
  • }??
  • (3)第三步,將RedisHttpSessionConfig加入到WebInitializer#getRootConfigClasses()中,讓Spring容器加載RedisHttpSessionConfig類。WebInitializer是一個自定義的AbstractAnnotationConfigDispatcherServletInitializer實現類,該類會在Servlet啟動時加載(當然也可以采用別的加載方法,比如采用掃描@Configuration注解類的方式等等)。

    ?

    [java]?view plaincopy
  • //該類采用Java?Configuration,來代替web.xml?????
  • public?class?WebInitializer?extends?AbstractAnnotationConfigDispatcherServletInitializer?{??
  • ??????
  • ????@Override??
  • ????protected?Class<?>[]?getRootConfigClasses()?{??
  • ????????return?new?Class[]{Config1.class,?Config2.class,?RedisHttpSessionConfig.class};??
  • ????}??
  • ??????
  • ????//......??
  • }??
  • ?

    (4)第四步,編寫一個一個AbstractHttpSessionApplicationInitializer實現類,用于向Servlet容器中添加springSessionRepositoryFilter。

    ?

    [java]?view plaincopy
  • import?org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;??
  • ??
  • public?class?SpringSessionInitializer?extends?AbstractHttpSessionApplicationInitializer?{??
  • }??
  • ?

    4. Spring Session原理

    ?

    (1)前面集成spring-sesion的第二步中,編寫了一個配置類RedisHttpSessionConfig,它包含注解@EnableRedisHttpSession,并通過@Bean注解注冊了一個RedisConnectionFactory到Spring容器中。

    而@EnableRedisHttpSession注解通過Import,引入了RedisHttpSessionConfiguration配置類。該配置類通過@Bean注解,向Spring容器中注冊了一個SessionRepositoryFilter(SessionRepositoryFilter的依賴關系:SessionRepositoryFilter --> SessionRepository --> RedisTemplate --> RedisConnectionFactory)。

    ?

    ?

    [java]?view plaincopy
  • package?org.springframework.session.data.redis.config.annotation.web.http;??
  • ??
  • @Configuration??
  • @EnableScheduling??
  • public?class?RedisHttpSessionConfiguration?implements?ImportAware,?BeanClassLoaderAware?{??
  • ????//......??
  • ??????
  • ????@Bean??
  • ????public?RedisTemplate<String,ExpiringSession>?sessionRedisTemplate(RedisConnectionFactory?connectionFactory)?{??
  • ????????//......??
  • ????????return?template;??
  • ????}??
  • ??????
  • ????@Bean??
  • ????public?RedisOperationsSessionRepository?sessionRepository(RedisTemplate<String,?ExpiringSession>?sessionRedisTemplate)?{??
  • ????????//......??
  • ????????return?sessionRepository;??
  • ????}??
  • ??????
  • ????@Bean??
  • ????public?<S?extends?ExpiringSession>?SessionRepositoryFilter<??extends?ExpiringSession>?springSessionRepositoryFilter(SessionRepository<S>?sessionRepository,?ServletContext?servletContext)?{??
  • ????????//......??
  • ????????return?sessionRepositoryFilter;??
  • ????}??
  • ??????
  • ????//......??
  • }??
  • (2)集成spring-sesion的第四步中,我們編寫了一個SpringSessionInitializer 類,它繼承自AbstractHttpSessionApplicationInitializer。該類不需要重載或實現任何方法,它的作用是在Servlet容器初始化時,從Spring容器中獲取一個默認名叫sessionRepositoryFilter的過濾器類(之前沒有注冊的話這里找不到會報錯),并添加到Servlet過濾器鏈中。

    ?

    ?

    [java]?view plaincopy
  • package?org.springframework.session.web.context;??
  • ??
  • /**?
  • ?*?Registers?the?{@link?DelegatingFilterProxy}?to?use?the?
  • ?*?springSessionRepositoryFilter?before?any?other?registered?{@link?Filter}.??
  • ?*?
  • ?*?......?
  • ?*/??
  • @Order(100)??
  • public?abstract?class?AbstractHttpSessionApplicationInitializer?implements?WebApplicationInitializer?{??
  • ??
  • ????private?static?final?String?SERVLET_CONTEXT_PREFIX?=?"org.springframework.web.servlet.FrameworkServlet.CONTEXT.";??
  • ??
  • ????public?static?final?String?DEFAULT_FILTER_NAME?=?"springSessionRepositoryFilter";??
  • ??
  • ????//......??
  • ??
  • ????public?void?onStartup(ServletContext?servletContext)??
  • ????????????throws?ServletException?{??
  • ????????beforeSessionRepositoryFilter(servletContext);??
  • ????????if(configurationClasses?!=?null)?{??
  • ????????????AnnotationConfigWebApplicationContext?rootAppContext?=?new?AnnotationConfigWebApplicationContext();??
  • ????????????rootAppContext.register(configurationClasses);??
  • ????????????servletContext.addListener(new?ContextLoaderListener(rootAppContext));??
  • ????????}??
  • ????????insertSessionRepositoryFilter(servletContext);//注冊一個SessionRepositoryFilter??
  • ????????afterSessionRepositoryFilter(servletContext);??
  • ????}??
  • ??
  • ????/**?
  • ?????*?Registers?the?springSessionRepositoryFilter?
  • ?????*?@param?servletContext?the?{@link?ServletContext}?
  • ?????*/??
  • ????private?void?insertSessionRepositoryFilter(ServletContext?servletContext)?{??
  • ????????String?filterName?=?DEFAULT_FILTER_NAME;//默認名字是springSessionRepositoryFilter??
  • ????????DelegatingFilterProxy?springSessionRepositoryFilter?=?new?DelegatingFilterProxy(filterName);//該Filter代理會在初始化時從Spring容器中查找springSessionRepositoryFilter,之后實際會使用SessionRepositoryFilter進行doFilter操作?????????
  • ????????String?contextAttribute?=?getWebApplicationContextAttribute();??
  • ????????if(contextAttribute?!=?null)?{??
  • ????????????springSessionRepositoryFilter.setContextAttribute(contextAttribute);??
  • ????????}??
  • ????????registerFilter(servletContext,?true,?filterName,?springSessionRepositoryFilter);??
  • ????}??
  • ??????
  • ????//......??
  • }??

  • SessionRepositoryFilter是一個優先級最高的javax.servlet.Filter,它使用了一個SessionRepositoryRequestWrapper類接管了Http Session的創建和管理工作。

    注意下面給出的是簡化過的示例代碼,與spring-session項目的源代碼有所差異。

    ?

    [java]?view plaincopy
  • @Order(SessionRepositoryFilter.DEFAULT_ORDER)??
  • 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);??
  • ????????}??
  • ??
  • ????????//?...??
  • }??
  • ?

    [java]?view plaincopy
  • 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?...??
  • }??
  • (3)好了,剩下的問題就是,如何在Servlet容器啟動時,加載下面兩個類。幸運的是,這兩個類由于都實現了WebApplicationInitializer接口,會被自動加載。
    • WebInitializer,負責加載配置類。它繼承自AbstractAnnotationConfigDispatcherServletInitializer,實現了WebApplicationInitializer接口
    • SpringSessionInitializer,負責添加sessionRepositoryFilter的過濾器類。它繼承自AbstractHttpSessionApplicationInitializer,實現了WebApplicationInitializer接口

    ?

    在Servlet3.0規范中,Servlet容器啟動時會自動掃描javax.servlet.ServletContainerInitializer的實現類,在實現類中我們可以定制需要加載的類。在spring-web項目中,有一個ServletContainerInitializer實現類SpringServletContainerInitializer,它通過注解@HandlesTypes(WebApplicationInitializer.class),讓Servlet容器在啟動該類時,會自動尋找所有的WebApplicationInitializer實現類。

    ?

    [java]?view plaincopy
  • package?org.springframework.web;??
  • ??
  • @HandlesTypes(WebApplicationInitializer.class)??
  • public?class?SpringServletContainerInitializer?implements?ServletContainerInitializer?{??
  • ??
  • ????/**?
  • ?????*?Delegate?the?{@code?ServletContext}?to?any?{@link?WebApplicationInitializer}?
  • ?????*?implementations?present?on?the?application?classpath.?
  • ?????*?
  • ?????*?<p>Because?this?class?declares?@{@code?HandlesTypes(WebApplicationInitializer.class)},?
  • ?????*?Servlet?3.0+?containers?will?automatically?scan?the?classpath?for?implementations?
  • ?????*?of?Spring's?{@code?WebApplicationInitializer}?interface?and?provide?the?set?of?all?
  • ?????*?such?types?to?the?{@code?webAppInitializerClasses}?parameter?of?this?method.?
  • ?????*?
  • ?????*?<p>If?no?{@code?WebApplicationInitializer}?implementations?are?found?on?the?
  • ?????*?classpath,?this?method?is?effectively?a?no-op.?An?INFO-level?log?message?will?be?
  • ?????*?issued?notifying?the?user?that?the?{@code?ServletContainerInitializer}?has?indeed?
  • ?????*?been?invoked?but?that?no?{@code?WebApplicationInitializer}?implementations?were?
  • ?????*?found.?
  • ?????*?
  • ?????*?<p>Assuming?that?one?or?more?{@code?WebApplicationInitializer}?types?are?detected,?
  • ?????*?they?will?be?instantiated?(and?<em>sorted</em>?if?the?@{@link?
  • ?????*?org.springframework.core.annotation.Order?@Order}?annotation?is?present?or?
  • ?????*?the?{@link?org.springframework.core.Ordered?Ordered}?interface?has?been?
  • ?????*?implemented).?Then?the?{@link?WebApplicationInitializer#onStartup(ServletContext)}?
  • ?????*?method?will?be?invoked?on?each?instance,?delegating?the?{@code?ServletContext}?such?
  • ?????*?that?each?instance?may?register?and?configure?servlets?such?as?Spring's?
  • ?????*?{@code?DispatcherServlet},?listeners?such?as?Spring's?{@code?ContextLoaderListener},?
  • ?????*?or?any?other?Servlet?API?componentry?such?as?filters.?
  • ?????*?
  • ?????*?@param?webAppInitializerClasses?all?implementations?of?
  • ?????*?{@link?WebApplicationInitializer}?found?on?the?application?classpath?
  • ?????*?@param?servletContext?the?servlet?context?to?be?initialized?
  • ?????*?@see?WebApplicationInitializer#onStartup(ServletContext)?
  • ?????*?@see?AnnotationAwareOrderComparator?
  • ?????*/??
  • ????@Override??
  • ????public?void?onStartup(Set<Class<?>>?webAppInitializerClasses,?ServletContext?servletContext)??
  • ????????????throws?ServletException?{??
  • ????????//......??
  • ????}??
  • ??
  • }??
  • ?

    5. 如何在Redis中查看Session數據?

    ?

    (1)Http Session數據在Redis中是以Hash結構存儲的。

    (2)可以看到,還有一個key="spring:session:expirations:1431577740000"的數據,是以Set結構保存的。這個值記錄了所有session數據應該被刪除的時間(即最新的一個session數據過期的時間)。 [plain]?view plaincopy
  • 127.0.0.1:6379>?keys?*??
  • 1)?"spring:session:expirations:1431577740000"??
  • 2)?"spring:session:sessions:e2cef3ae-c8ea-4346-ba6b-9b3b26eee578"??
  • 127.0.0.1:6379>?type?spring:session:sessions:e2cef3ae-c8ea-4346-ba6b-9b3b26eee578??
  • hash??
  • 127.0.0.1:6379>?type?spring:session:expirations:1431577740000??
  • set??
  • ?

    [plain]?view plaincopy
  • 127.0.0.1:6379>?keys?*??
  • 1)?"spring:session:expirations:1431527520000"??
  • 2)?"spring:session:sessions:59f3987c-d1e4-44b3-a83a-32079942888b"??
  • 3)?"spring:session:sessions:11a69da6-138b-42bc-9916-60ae78aa55aa"??
  • 4)?"spring:session:sessions:0a51e2c2-4a3b-4986-a754-d886d8a5d42d"??
  • 5)?"spring:session:expirations:1431527460000"??
  • ??
  • 127.0.0.1:6379>?hkeys?spring:session:sessions:59f3987c-d1e4-44b3-a83a-32079942888b??
  • 1)?"maxInactiveInterval"??
  • 2)?"creationTime"??
  • 3)?"lastAccessedTime"??
  • 4)?"sessionAttr:attr1"??
  • ??
  • 127.0.0.1:6379>?hget?spring:session:sessions:59f3987c-d1e4-44b3-a83a-32079942888b?sessionAttr:attr1??
  • "\xac\xed\x00\x05sr\x00\x11java.lang.Integer\x12\xe2\xa0\xa4\xf7\x81\x878\x02\x00\x01I\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x00\x03"??
  • ??
  • 127.0.0.1:6379>?hget?spring:session:sessions:59f3987c-d1e4-44b3-a83a-32079942888b?creationTime??
  • "\xac\xed\x00\x05sr\x00\x0ejava.lang.Long;\x8b\xe4\x90\xcc\x8f#\xdf\x02\x00\x01J\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x01MM\x94(\xec"??
  • 6.參考文章

    ?

    Spring Session 1.01 Reference

    spring session入門

    集群session共享機制

    轉載于:https://www.cnblogs.com/davidwang456/articles/8994541.html

    總結

    以上是生活随笔為你收集整理的利用spring session解决共享Session问题的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。