当前位置: 首页 > news >正文

html网站开发实用技术外贸网站服务器选择

html网站开发实用技术,外贸网站服务器选择,php中网站不同模板后台逻辑代码怎么管理,做团购网站有什么难处一、背景 http session#xff08;企业#xff09;一直都是我们做集群时需要解决的一个难题#xff0c;我们知道HttpSession是通过Servlet容器创建和管理的#xff0c;像Tomcat/Jetty都是保存在内存中的。而如果我们把web服务器搭建成分布式的集群#xff0c;然后利用LVS或…一、背景 http 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。 3或者通过nginx之类的负载均衡做ip_hash路由到特定的服务器上。 此策略会出现单点故障问题。 二、spring-session简介  Spring Session是Spring的项目之一GitHub地址https://github.com/spring-projects/spring-session。Spring Session把servlet容器实现的httpSession替换为spring-session专注于解决session管理问题。Spring Session提供了集群SessionClustered Sessions功能默认采用外置的Redis来存储Session数据以此来解决Session共享的问题。 spring-session提供对用户session管理的一系列api和实现。提供了很多可扩展、透明的封装方式用于管理httpSession/WebSocket的处理。 三、支持功能  1轻易把session存储到第三方存储容器框架提供了redis、jvm的map、mongo、gemfire、hazelcast、jdbc等多种存储session的容器的方式。这样可以独立于应用服务器的方式提供高质量的集群。2同一个浏览器同一个网站支持多个session问题。 从而能够很容易地构建更加丰富的终端用户体验。3Restful API不依赖于cookie。可通过header来传递jessionID 。控制session id如何在客户端和服务器之间进行交换这样的话就能很容易地编写Restful API因为它可以从HTTP 头信息中获取session id而不必再依赖于cookie。4WebSocket和spring-session结合同步生命周期管理。当用户使用WebSocket发送请求的时候能够保持HttpSession处于活跃状态。5在非Web请求的处理代码中能够访问session数据比如在JMS消息的处理代码中。   需要说明的很重要的一点就是Spring Session的核心项目并不依赖于Spring框架所以我们甚至能够将其应用于不使用Spring框架的项目中。Spring Session提供了一种独立于应用服务器的方案这种方案能够在Servlet规范之内配置可插拔的session数据存储不依赖于任何应用服务器的特定API。这就意味着Spring Session能够用于实现了servlet规范的所有应用服务器之中Tomcat、Jetty、 WebSphere、WebLogic、JBoss等它能够非常便利地在所有应用服务器中以完全相同的方式进行配置。我们还可以选择任意最适应需求的外部session数据存储。这使得Spring Session成为一个很理想的迁移工具帮助我们将传统的JavaEE应用转移到云中使其成为满足如下 3.1、每个用户有多个账号 假设我们在example.com上运行面向公众的Web应用在这个应用中有些用户会创建多个账号。例如用户Jeff Lebowski可能会有两个账户thedudeexample.com和lebowskiexample.com。和其他Java Web应用一样我们会使用HttpSession来跟踪应用的状态如当前登录的用户。所以当用户希望从thedudeexample.com切换到lebowskiexample.com时他必须要首先退出然后再重新登录回来。 借助Spring Session为每个用户配置多个HTTP session会非常容易这样用户在thedudeexample.com和lebowskiexample.com之间切换的时候就不需要退出和重新登录了。 3.2、多级别的安全预览 假设我们正在构建的Web应用有一个复杂、自定义的权限功能其中应用的UI会基于用户所授予的角色和权限实现自适应。 例如假设应用有四个安全级别public、confidential、secret和top secret。当用户登录应用之后系统会判断用户所具有的最高安全级别并且只会显示该级别和该级别之下的数据。所以具有public权限的用户只能看到public级别的文档具有secret权限的用户能够看到public、confidential和secret级别的文档诸如此类。为了保证用户界面更加友好应用程序应该允许用户预览在较低的安全级别条件下页面是什么样子的。例如top secret权限的用户能够将应用从top secret模式切换到secret模式这样就能站在具有secret权限用户的视角上查看应用是什么样子的。 典型的Web应用会将当前用户的标识及其角色保存在HTTP session中但因为在Web应用中每个登录的用户只能有一个session因此除了用户退出并重新登录进来我们并没有办法在角色之间进行切换除非我们为每个用户自行实现多个session的功能。 借助Spring Session可以很容易地为每个登录用户创建多个session这些session之间是完全独立的因此实现上述的预览功能是非常容易的。例如当前用户以top secret角色进行了登录那么应用可以创建一个新的session这个session的最高安全角色是secret而不是top secret这样的话用户就可以在secret模式预览应用了。 3.3、当使用Web Socket的时候保持登录状态 假设用户登录了example.com上的Web应用那么他们可以使用HTML5的chat客户端实现聊天的功能这个客户端构建在websocket之上。按照servlet规范通过websocket传入的请求并不能保持HTTP session处于活跃状态所以当用户在聊天的过程中HTTP session的倒数计时器会在不断地流逝。即便站在用户的立场上他们一直在使用应用程序HTTP session最终也可能会出现过期。当HTTP session过期时websocket连接将会关闭。 借助Spring Session对于系统中的用户我们能够很容易地实现websocket请求和常规的HTTP请求都能保持HTTP session处于活跃状态。 3.4、非Web请求访问Session数据 假设我们的应用提供了两种访问方式一种使用基于HTTP的REST API而另一种使用基于RabbitMQ的AMQP消息。执行消息处理代码的线程将无法访问应用服务器的HttpSession所以我们必须要以一种自定义的方案来获取HTTP session中的数据这要通过自定义的机制来实现。 通过使用Spring Session只要我们能够知道session的id就可以在应用的任意线程中访问Spring Session。因此Spring Session具备比Servlet HTTP session管理器更为丰富的API只要知道了session id我们就能获取任意特定的session。例如在一个传入的消息中可能会包含用户id的header信息借助它我们就可以直接获取session了。 四、Spring Session是如何运行的 我们已经讨论了在传统的应用服务器中HTTP session管理存在不足的各种场景接下来看一下Spring Session是如何解决这些问题的。 4.1、Spring Session的架构 当实现session管理器的时候有两个必须要解决的核心问题。首先如何创建集群环境下高可用的session要求能够可靠并高效地存储数据。其次不管请求是HTTP、WebSocket、AMQP还是其他的协议对于传入的请求该如何确定该用哪个session实例。实质上关键问题在于在发起请求的协议上session id该如何进行传输 Spring Session认为第一个问题也就是在高可用可扩展的集群中存储数据已经通过各种数据存储方案得到了解决如Redis、GemFire以及Apache Geode等等因此Spring Session定义了一组标准的接口可以通过实现这些接口间接访问底层的数据存储。Spring Session定义了如下核心接口Session、ExpiringSession以及SessionRepository针对不同的数据存储它们需要分别实现。 org.springframework.session.Session接口定义了session的基本功能如设置和移除属性。这个接口并不关心底层技术因此能够比servlet HttpSession适用于更为广泛的场景中。org.springframework.session.ExpiringSession扩展了Session接口它提供了判断session是否过期的属性。RedisSession是这个接口的一个样例实现。org.springframework.session.SessionRepository定义了创建、保存、删除以及检索session的方法。将Session实例真正保存到数据存储的逻辑是在这个接口的实现中编码完成的。例如RedisOperationsSessionRepository就是这个接口的一个实现它会在Redis中创建、存储和删除session。Spring Session认为将请求与特定的session实例关联起来的问题是与协议相关的因为在请求/响应周期中客户端和服务器之间需要协商同意一种传递session id的方式。例如如果请求是通过HTTP传递进来的那么session可以通过HTTP cookie或HTTP Header信息与请求进行关联。如果使用HTTPS的话那么可以借助SSL session id实现请求与session的关联。如果使用JMS的话那么JMS的Header信息能够用来存储请求和响应之间的session id。 对于HTTP协议来说Spring Session定义了HttpSessionStrategy接口以及两个默认实现即CookieHttpSessionStrategy和HeaderHttpSessionStrategy其中前者使用HTTP cookie将请求与session id关联而后者使用HTTP header将请求与session关联。   4.2、Spring Session对HTTP的支持 Spring Session对HTTP的支持是通过标准的servlet filter来实现的这个filter必须要配置为拦截所有的web应用请求并且它应该是filter链中的第一个filter。Spring Session filter会确保随后调用javax.servlet.http.HttpServletRequest的getSession()方法时都会返回Spring Session的HttpSession实例而不是应用服务器默认的HttpSession。 如果要理解它的话最简单的方式就是查看Spring Session实际所使用的源码。首先我们了解一下标准servlet扩展点的一些背景知识在实现Spring Session的时候会使用这些知识。 4.2.1、Spring Session对filer的request,response的装饰 在2001年Servlet 2.3规范引入了ServletRequestWrapper。它的javadoc文档这样写道ServletRequestWrapper“提供了ServletRequest接口的便利实现开发人员如果希望将请求适配到Servlet的话可以编写它的子类。这个类实现了包装Wrapper或者说是装饰Decorator模式。对方法的调用默认会通过包装的请求对象来执行”。如下的代码样例抽取自Tomcat展现了ServletRequestWrapper是如何实现的。 javax.servlet-api-3.1.0.jar package javax.servlet; public class ServletRequestWrapper implements ServletRequest {private ServletRequest request;/*** 创建ServletRequest适配器它包装了给定的请求对象。* throws java.lang.IllegalArgumentException if the request is null*/public ServletRequestWrapper(ServletRequest request) {if (request null) {throw new IllegalArgumentException(Request cannot be null); }this.request request;}public ServletRequest getRequest() {return this.request;}//... } Servlet 2.3规范还定义了HttpServletRequestWrapper它是ServletRequestWrapper的子类能够快速提供HttpServletRequest的自定义实现如下的代码是从Tomcat抽取出来的展现了HttpServletRequesWrapper类是如何运行的。 javax.servlet-api-3.1.0.jar package javax.servlet.http; public class HttpServletRequestWrapper extends ServletRequestWrapper implements HttpServletRequest {/** * Constructs a request object wrapping the given request.* throws java.lang.IllegalArgumentException if the request is null*/public HttpServletRequestWrapper(HttpServletRequest request) {super(request);}private HttpServletRequest _getHttpServletRequest() {return (HttpServletRequest) super.getRequest();}//... } 所以借助这些包装类就能编写代码来扩展HttpServletRequest重载返回HttpSession的方法让它返回由外部存储所提供的实现。如下的代码是从Spring Session项目中提取出来的但是我将原来的注释替换为我自己的注释用来在本文中解释代码所以在阅读下面的代码片段时请留意注释。 spring-session-1.3.1.RELEASE.jar package org.springframework.session.web.http; private final class SessionRepositoryRequestWrapperextends HttpServletRequestWrapper {private Boolean requestedSessionIdValid;private boolean requestedSessionInvalidated;private final HttpServletResponse response;private final ServletContext servletContext;/*** 注意这个构造器非常简单它接受稍后会用到的参数并且委托给它所扩展的HttpServletRequestWrapper*/private SessionRepositoryRequestWrapper(HttpServletRequest request,HttpServletResponse response, ServletContext servletContext) {super(request);this.response response;this.servletContext servletContext;}/*** 使用HttpSessionStrategy写sessionid到返回对象同时调用外部存储设备持久化session信息* sessionRepository相当于DAO有关于session持久化的4个方法*/private void commitSession() {HttpSessionWrapper wrappedSession getCurrentSession();if (wrappedSession null) {if (isInvalidateClientSession()) {SessionRepositoryFilter.this.httpSessionStrategy.onInvalidateSession(this, this.response);}}else {S session wrappedSession.getSession();SessionRepositoryFilter.this.sessionRepository.save(session);if (!isRequestedSessionIdValid()|| !session.getId().equals(getRequestedSessionId())) {SessionRepositoryFilter.this.httpSessionStrategy.onNewSession(session,this, this.response);}}}/*** 在这里Spring Session项目不再将调用委托给应用服务器而是实现自己的逻辑返回由外部数据存储作为支撑的HttpSession实例。* 基本的实现是先检查是不是已经有session了。* 如果有的话就将currentSession返回* 否则的话它会检查当前的请求中是否有session id。* 如果有的话将会根据这个session id从它的SessionRepository中加载session。* 如果session repository中没有session或者在当前请求中* 没有当前session id与请求关联的话* 那么它会创建一个新的session并将其持久化到session repository中。*/Overridepublic HttpSessionWrapper getSession(boolean create) {HttpSessionWrapper currentSession getCurrentSession();if (currentSession ! null) {return currentSession;}String requestedSessionId getRequestedSessionId();if (requestedSessionId ! null getAttribute(INVALID_SESSION_ID_ATTR) null) {S session getSession(requestedSessionId);if (session ! null) {this.requestedSessionIdValid true;currentSession new HttpSessionWrapper(session, getServletContext());currentSession.setNew(false);setCurrentSession(currentSession);return currentSession;}else {// This is an invalid session id. No need to ask again if// request.getSession is invoked for the duration of this requestif (SESSION_LOGGER.isDebugEnabled()) {SESSION_LOGGER.debug(No session found by id: Caching result for getSession(false) for this HttpServletRequest.);}setAttribute(INVALID_SESSION_ID_ATTR, true);}}if (!create) {return null;}if (SESSION_LOGGER.isDebugEnabled()) {SESSION_LOGGER.debug(A new session was created. To help you troubleshoot where the session was created we provided a StackTrace (this is not an error). You can prevent this from appearing by disabling DEBUG logging for SESSION_LOGGER_NAME,new RuntimeException(For debugging purposes only (not an error)));}S session SessionRepositoryFilter.this.sessionRepository.createSession();session.setLastAccessedTime(System.currentTimeMillis());currentSession new HttpSessionWrapper(session, getServletContext());setCurrentSession(currentSession);return currentSession;}Overridepublic ServletContext getServletContext() {if (this.servletContext ! null) {return this.servletContext;}// Servlet 3.0return super.getServletContext();}Overridepublic HttpSessionWrapper getSession() {return getSession(true);}Overridepublic String getRequestedSessionId() {return SessionRepositoryFilter.this.httpSessionStrategy.getRequestedSessionId(this);}/*** Allows creating an HttpSession from a Session instance.** author Rob Winch* since 1.0*/private final class HttpSessionWrapper extends ExpiringSessionHttpSessionS {HttpSessionWrapper(S session, ServletContext servletContext) {super(session, servletContext);}Overridepublic void invalidate() {super.invalidate();SessionRepositoryRequestWrapper.this.requestedSessionInvalidated true;setCurrentSession(null);SessionRepositoryFilter.this.sessionRepository.delete(getId());}}} response有对应SessionRepositoryResponseWrapper。 /**这个就是Servlet response的重写类了*/private final class SessionRepositoryResponseWrapperextends OnCommittedResponseWrapper {private final SessionRepositoryRequestWrapper request;SessionRepositoryResponseWrapper(SessionRepositoryRequestWrapper request,HttpServletResponse response) {super(response);if (request null) {throw new IllegalArgumentException(request cannot be null);}this.request request;}/** 这步是持久化session到存储容器我们可能会在一个控制层里多次调用session的操作方法如果我们每次对session的操作都持久化到存储容器必定会带来性能的影响。比如redis所以我们可以在整个控制层执行完毕了response返回信息到浏览器时才持久化session**/Overrideprotected void onResponseCommitted() {this.request.commitSession();}} 4.2.2、Spring Session中SessionRepositoryFilter的处理 Spring Session定义了SessionRepositoryFilter它实现了Servlet Filter接口。我抽取了这个filter的关键部分将其列在下面的代码片段中我还添加了一些注释用来在本文中阐述这些代码所以同样的请阅读下面代码的注释部分。 package org.springframework.session.web.http; Order(SessionRepositoryFilter.DEFAULT_ORDER) public class SessionRepositoryFilterS extends ExpiringSessionextends OncePerRequestFilter {/** session存储容器接口redis、mongoDB、genfire等数据库都是实现该接口 **/private final SessionRepositoryS sessionRepository;private ServletContext servletContext;/** sessionID的传递方式接口。目前spring-session自带两个实现类1.cookie方式 CookieHttpSessionStrategy2.http header 方式HeaderHttpSessionStrategy当然我们也可以自定义其他方式。**/private MultiHttpSessionStrategy httpSessionStrategy new CookieHttpSessionStrategy();public void setHttpSessionStrategy(HttpSessionStrategy httpSessionStrategy) {if (httpSessionStrategy null) {throw new IllegalArgumentException(httpSessionStrategy cannot be null);}/** 通过前面的spring-session功能介绍我们知道spring-session可以支持单浏览器多session 就是通过MultiHttpSessionStrategyAdapter来实现的。每个浏览器拥有一个sessionID但是这个sessionID拥有多个别名根据浏览器的tab。如别名1 sessionID别名2 sessionID...而这个别名通过url来传递这就是单浏览器多session原理了**/this.httpSessionStrategy new MultiHttpSessionStrategyAdapter(httpSessionStrategy);}public void setHttpSessionStrategy(MultiHttpSessionStrategy httpSessionStrategy) {if (httpSessionStrategy null) {throw new IllegalArgumentException(httpSessionStrategy cannot be null);}this.httpSessionStrategy httpSessionStrategy;}/** 这个方法是魔力真正发挥作用的地方。这个方法创建了* 我们上文所述的封装请求对象SessionRepositoryRequestWrapper和一个封装的响应对象SessionRepositoryResponseWrapper然后调用其余的filter链。* 这里关键在于当这个filter后面的应用代码执行时* 如果要获得session的话得到的将会是Spring Session的HttpServletSession实例它是由后端的外部数据存储作为支撑的。*/Overrideprotected void doFilterInternal(HttpServletRequest request,HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);SessionRepositoryRequestWrapper wrappedRequest new SessionRepositoryRequestWrapper(request, response, this.servletContext);SessionRepositoryResponseWrapper wrappedResponse new SessionRepositoryResponseWrapper(wrappedRequest, response);HttpServletRequest strategyRequest this.httpSessionStrategy.wrapRequest(wrappedRequest, wrappedResponse);HttpServletResponse strategyResponse this.httpSessionStrategy.wrapResponse(wrappedRequest, wrappedResponse);try {filterChain.doFilter(strategyRequest, strategyResponse);}finally {wrappedRequest.commitSession(); //filter链处理完成后写session信息到response及外围持久化设备源码见上面的SessionRepositoryRequestWrapper}} 4.2.3、Spring Session中sessionRepository是session存储容器接口操作session信息的读取及存储 session存储容器接口redis、mongoDB、genfire等数据库都是实现该接口 1、sessionRepository 先看SessionRepository接口的4个方法 package org.springframework.session;public interface SessionRepositoryS extends Session {/*** 创建*/S createSession();/*** 保存*/void save(S session);/*** 读取*/S getSession(String id);/*** 删除*/void delete(String id);  实现类FindByIndexNameSessionRepository.java package org.springframework.session; public interface FindByIndexNameSessionRepositoryS extends Sessionextends SessionRepositoryS {String PRINCIPAL_NAME_INDEX_NAME FindByIndexNameSessionRepository.class.getName().concat(.PRINCIPAL_NAME_INDEX_NAME);MapString, S findByIndexNameAndIndexValue(String indexName, String indexValue); } springsession项目启动后redis会有   Redis的实现类 package org.springframework.session.data.redis;public class RedisOperationsSessionRepository implementsFindByIndexNameSessionRepositoryRedisOperationsSessionRepository.RedisSession,MessageListener {/*** RedisSession的构造函数新建一个session,往里看源码是通过UUID生成MapSession.this(UUID.randomUUID().toString()); */public RedisSession createSession() {RedisSession redisSession new RedisSession();if (this.defaultMaxInactiveInterval ! null) {redisSession.setMaxInactiveIntervalInSeconds(this.defaultMaxInactiveInterval);}return redisSession;}/*** 调用RedisTemplate.convertAndSend()保存到redis中*/public void save(RedisSession session) {session.saveDelta();if (session.isNew()) {String sessionCreatedKey getSessionCreatedChannel(session.getId());this.sessionRedisOperations.convertAndSend(sessionCreatedKey, session.delta);session.setNew(false);}}/*** 先构造MapSession再查找对应的session*/private RedisSession getSession(String id, boolean allowExpired) {MapObject, Object entries getSessionBoundHashOperations(id).entries();if (entries.isEmpty()) {return null;}MapSession loaded loadSession(id, entries);if (!allowExpired loaded.isExpired()) {return null;}RedisSession result new RedisSession(loaded);result.originalLastAccessTime loaded.getLastAccessedTime();return result;}/*** 如果没有找到对应的session直接返回如果找到就删除*/public void delete(String sessionId) {RedisSession session getSession(sessionId, true);if (session null) {return;}cleanupPrincipalIndex(session);this.expirationPolicy.onDelete(session);String expireKey getExpiredKey(session.getId());this.sessionRedisOperations.delete(expireKey);session.setMaxInactiveIntervalInSeconds(0);save(session);} } 2、Session接口包路径package org.springframework.session; Redis的session实现类其中MapSession中保存关联属性创建完session会设置lastAccessTime。 package org.springframework.session.data.redis;final class RedisSession implements ExpiringSession {/*** Creates a new instance ensuring to mark all of the new attributes to be* persisted in the next save operation.*/RedisSession() {this(new MapSession());this.delta.put(CREATION_TIME_ATTR, getCreationTime());this.delta.put(MAX_INACTIVE_ATTR, getMaxInactiveIntervalInSeconds());this.delta.put(LAST_ACCESSED_ATTR, getLastAccessedTime());this.isNew true;this.flushImmediateIfNecessary();}} 4.2.4、Spring Session的ServletFilter配置    从4.2.1~4.2.3得到的关键信息是Spring Session对HTTP的支持所依靠的是一个简单老式的ServletFilter借助servlet规范中标准的特性来实现Spring Session的功能。最后一个问题是如何配置这个ServletFilter了配置Spring Session Filter很容易在Spring Boot中只需要在Spring Boot的配置类上使用 EnableRedisHttpSession注解就可以了如下面的代码片段所示 EnableRedisHttpSession注解的源码 package org.springframework.session.data.redis.config.annotation.web.http; Retention(java.lang.annotation.RetentionPolicy.RUNTIME) Target({ java.lang.annotation.ElementType.TYPE }) Documented Import(RedisHttpSessionConfiguration.class) Configuration public interface EnableRedisHttpSession {int maxInactiveIntervalInSeconds() default 1800;String redisNamespace() default ;String redisNamespace() default ; } RedisHttpSessionConfiguration是SpringHttpSessionConfiguration的redis的实现类。先看SpringHttpSessionConfiguration.java的源码在这里定义了bean名称为springSessionRepositoryFilter的Filter对所有请求[/*]都处理。这一点在启动日志也可以说明。 package org.springframework.session.config.annotation.web.http; Configuration public class SpringHttpSessionConfiguration implements ApplicationContextAware { //... Bean public SessionEventHttpSessionListenerAdapter sessionEventHttpSessionListenerAdapter() { return new SessionEventHttpSessionListenerAdapter(this.httpSessionListeners); } Bean public S extends ExpiringSession SessionRepositoryFilter? extends ExpiringSession springSessionRepositoryFilter( SessionRepositoryS sessionRepository) { SessionRepositoryFilterS sessionRepositoryFilter new SessionRepositoryFilterS( sessionRepository); sessionRepositoryFilter.setServletContext(this.servletContext); if (this.httpSessionStrategy instanceof MultiHttpSessionStrategy) { sessionRepositoryFilter.setHttpSessionStrategy( (MultiHttpSessionStrategy) this.httpSessionStrategy); } else { sessionRepositoryFilter.setHttpSessionStrategy(this.httpSessionStrategy); } return sessionRepositoryFilter; } //... }   RedisHttpSessionConfiguration.java的源码 package org.springframework.session.data.redis.config.annotation.web.http;Configuration EnableScheduling public class RedisHttpSessionConfiguration extends SpringHttpSessionConfigurationimplements EmbeddedValueResolverAware, ImportAware {private Integer maxInactiveIntervalInSeconds 1800;private ConfigureRedisAction configureRedisAction new ConfigureNotifyKeyspaceEventsAction();private String redisNamespace ;private RedisFlushMode redisFlushMode RedisFlushMode.ON_SAVE;private RedisSerializerObject defaultRedisSerializer;private Executor redisTaskExecutor;private Executor redisSubscriptionExecutor;private StringValueResolver embeddedValueResolver;Beanpublic RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory connectionFactory,RedisOperationsSessionRepository messageListener) {RedisMessageListenerContainer container new RedisMessageListenerContainer();container.setConnectionFactory(connectionFactory);if (this.redisTaskExecutor ! null) {container.setTaskExecutor(this.redisTaskExecutor);}if (this.redisSubscriptionExecutor ! null) {container.setSubscriptionExecutor(this.redisSubscriptionExecutor);}container.addMessageListener(messageListener,Arrays.asList(new PatternTopic(__keyevent*:del),new PatternTopic(__keyevent*:expired)));container.addMessageListener(messageListener, Arrays.asList(new PatternTopic(messageListener.getSessionCreatedChannelPrefix() *)));return container;}Beanpublic RedisTemplateObject, Object sessionRedisTemplate(RedisConnectionFactory connectionFactory) {RedisTemplateObject, Object template new RedisTemplateObject, Object();template.setKeySerializer(new StringRedisSerializer());template.setHashKeySerializer(new StringRedisSerializer());if (this.defaultRedisSerializer ! null) {template.setDefaultSerializer(this.defaultRedisSerializer);}template.setConnectionFactory(connectionFactory);return template;}Beanpublic RedisOperationsSessionRepository sessionRepository(Qualifier(sessionRedisTemplate) RedisOperationsObject, Object sessionRedisTemplate,ApplicationEventPublisher applicationEventPublisher) {RedisOperationsSessionRepository sessionRepository new RedisOperationsSessionRepository(sessionRedisTemplate);sessionRepository.setApplicationEventPublisher(applicationEventPublisher);sessionRepository.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);if (this.defaultRedisSerializer ! null) {sessionRepository.setDefaultSerializer(this.defaultRedisSerializer);}String redisNamespace getRedisNamespace();if (StringUtils.hasText(redisNamespace)) {sessionRepository.setRedisKeyNamespace(redisNamespace);}sessionRepository.setRedisFlushMode(this.redisFlushMode);return sessionRepository;}//... }   4.2.5、sessionListener support 4.2.5.1、在RedisOperationSessionRepository实现了MessageListener接口redis有消息通知的时候onMessage方法被调用然后接下来把消息封装成各种事件 然后通过ApplicationEventPublisher和listener协作观察者模式前者发送事件后者监听处理。 4.2.5.2、SessionEventHttpSessionListenerAdapter实现了ApplicationListener有新事件时onApplicationEvent方法被调用。所以只需要在这个类里面添加我们所需要的处理逻辑的listener即可。 注假如我们启动2个tomcat那么当一个session过期时那么我们的listener在这2个tomcat都会收到事件通知。会导致重复。如果需要基于session listener做一些事情则需要注意这点。 配置自己的事件处理器只需要实现HttpSessionListener接口同时实现两个方法即可。如下 Component public class MyListener implements HttpSessionListener {Overridepublic void sessionCreated(HttpSessionEvent se) {System.out.println(sessionCreated() se);System.out.println(online 1);}Overridepublic void sessionDestroyed(HttpSessionEvent se) {System.out.println(sessionDestroyed() se);System.out.println(online - 1);}}     ---如果想配置session的过期时间那么需要在RedisHttpSessionConfiguration里面配置有个参数maxInactiveIntervalInSeconds默认1800秒。因为session的管理被放到了外部的存储所以web.xml里面的关于session过期的配置不在生效。   4.2.6、MultiHttpSessionStrategyAdapter单浏览器多session支持 /*** A delegating implementation of {link MultiHttpSessionStrategy}.*/static class MultiHttpSessionStrategyAdapter implements MultiHttpSessionStrategy {private HttpSessionStrategy delegate;/*** Create a new {link MultiHttpSessionStrategyAdapter} instance.* param delegate the delegate HTTP session strategy*/MultiHttpSessionStrategyAdapter(HttpSessionStrategy delegate) {this.delegate delegate;}public String getRequestedSessionId(HttpServletRequest request) {return this.delegate.getRequestedSessionId(request);}public void onNewSession(Session session, HttpServletRequest request,HttpServletResponse response) {this.delegate.onNewSession(session, request, response);}public void onInvalidateSession(HttpServletRequest request,HttpServletResponse response) {this.delegate.onInvalidateSession(request, response);}//...} Spring Session会为每个用户保留多个session这是通过使用名为“_s”的session别名参数实现的。例如如果到达的请求为http://example.com/doSomething?_s0 那么Spring Session将会读取“_s”参数的值并通过它确定这个请求所使用的是默认session。 如果到达的请求是http://example.com/doSomething?_s1的话那么Spring Session就能知道这个请求所要使用的session别名为1.如果请求没有指定“_s”参数的话例如http://example.com/doSomething那么Spring Session将其视为使用默认的session也就是说_s0。 要为某个浏览器创建新的session只需要调用javax.servlet.http.HttpServletRequest.getSession()就可以了就像我们通常所做的那样Spring Session将会返回正确的session或者按照标准Servlet规范的语义创建一个新的session。下面的表格描述了针对同一个浏览器窗口getSession()面对不同url时的行为。 HTTP请求URL Session别名 getSession()的行为 example.com/resource 0 如果存在session与别名0关联的话就返回该session否则的话创建一个新的session并将其与别名0关联。 example.com/resource?_s1 1 如果存在session与别名1关联的话就返回该session否则的话创建一个新的session并将其与别名1关联。 example.com/resource?_s0 0 如果存在session与别名0关联的话就返回该session否则的话创建一个新的session并将其与别名0关联。 example.com/resource?_sabc abc 如果存在session与别名abc关联的话就返回该session否则的话创建一个新的session并将其与别名abc关联。 如上面的表格所示session别名不一定必须是整型它只需要区别于其他分配给用户的session别名就可以了。但是整型的session别名可能是最易于使用的Spring Session提供了HttpSessionManager接口这个接口包含了一些使用session别名的工具方法。 五、回顾 5.1、spring-session的包结构介绍   org.springframework.session包    定义一些接口如Session接口、SessionRepository接口存储接口、 org.springframework.session.web包    SessionRepositoryFilter重写Filter    集成Servlet把上面的filter加入到filter chain、cookie和Http header方式存放到jsession单浏览器多session支持等 org.springframework.session.data、org.springframework.session.jdbc、org.springframework.session.hazelcast主要是各类存储容器的实现如redis、jvm的map、mongo、gemfire、hazelcast、jdbc等 org.springframework.session.event包    定义session生命周期相关的事件 org.springframework.session.http包    配置spring-session   5.2、spring-session重写servlet request 及 redis实现存储相关问题 spring-session无缝替换应用服务器的request大概原理是 1.自定义个Filter实现doFilter方法 2.继承 HttpServletRequestWrapper 、HttpServletResponseWrapper 类重写getSession等相关方法(在这些方法里调用相关的 session存储容器操作类)。 3.在 第一步的doFilter中new 第二步 自定义的request和response的类。并把它们分别传递 到 过滤器链 4.把该filter配置到 过滤器链的第一个位置上 Redis存储容器实现。 主要实现存储公共基础类-FindByIndexNameSessionRepository ,里面主要有根据indexName从redis中查找session、根据sessionID对redis中的session增删改查的方法。 关于redis的session存储容器实际上spring-session是有些缺陷的。比如无法做到session的过期以及销毁的实时发布事件以及getCurrentSession中可能存在的一些并发问题小问题。但整体来说还是可用性很高的毕竟我们自己写一套这类框架成本很高。 以上只是针对redis session的存储容器其他存储容器可能会比redis更好比如gemfire至少在事件发布上是完整了根据它实现了事件猜的转载于:https://www.cnblogs.com/duanxz/p/3471448.html
http://www.huolong8.cn/news/281416/

相关文章:

  • 潍坊免费模板建站标识设计是什么
  • 社交网站 建站wordpress释放内存
  • 域名网站备案查询响应式网站建站系统
  • GTA5房产网站建设中北京泵网站建设
  • 夸克破解可看禁用网站门户网站的优点
  • 网站建设服务市场分析wordpress添加文章属性
  • 开放平台的核心目的是苏州网络推广优化
  • 汽车门户网站管理系统的详细设计与实现南宁正规的seo费用
  • wordpress注册邮箱空白怎么做网站排名优化
  • 淘宝做详情页代码网站网站关键词排名优化方法
  • 门户网站内容建设图书网站建设费用明细
  • 易优建站网站建设储蓄卡
  • gif网站素材网站外部优化
  • 建设旅游网站建议mysql做wp网站
  • 网站怎么做才吸引人做公司网站源代码怎么写
  • 做房间预定网站需要什么软件网页设计与制作课件清华大学
  • 外贸网站推广营销网站查询工信部
  • php 怎么做 网站吗app怎么创建
  • 网站开发用的框架一般做网站要什么编程
  • 网站建设域名多少钱建设工程资料下载网站
  • 网站开发实习徐州市工程造价信息网
  • 专业做网站的公司论坛网站模板
  • 怎么获取网站数据做统计数据凡科网站做商城
  • 论文网站建设与运营深圳网页设计与制作本科工资多少钱
  • 用wordpress建站一个人可以吗网站模版带后台
  • 做网站优化有必要ui界面
  • 自己做网站图片存在哪里wordpress扁平模板
  • 网站关键词描述字数问卷星网站开发市场调查问卷
  • 临漳企业做网站推广谷歌浏览器入口
  • 怎么增加网站流量网站建立的步骤是( )