一、简单的背景描述1、为了说明问题,本文简单微服务架构的例子如下2.组件说明分布式架构,每个组件是一个集群或主备。具体说明如下:zuul服务:网关,所有API调用都要经过zuul服务。微服务1&微服务2:实现业务功能,对数据库进行增删改查。eureka:组件注册,zuul服务,微服务等组件都注册到eureka,管理组件调用地址。db-master&db-slave:数据库集群,一主二从。redismaster&redisslave:redis集群,缓存。session对象主要存放在这里。3、组件间API调用①:网关zuul收到的API请求路由到业务实现组件。②:网关zuul和业务组件在redis中存储session对象或者从redis中获取session对象。③:业务组件实现数据的增删改查。④:通过springCloudfeign组件调用业务组件。⑤:网关zuul和微服务组件注册到eureka组件,或者从eureka获取组件调用地址。2.存在的问题基于上述微服务的分布式架构如果按照传统的方式,session对象是存储在内存中的。zuul网关将请求路由到不同的微服务1或微服务2时,无法共享内存中的session对象,无法判断用户的登录状态,也无法获取到session对象中保存的全局数据。三、解决方案1、Spring通过EnableRedisHttpSession注解管理session对象,支持基于redis存储session,全局共享session对象。二、微服务架构下共享session对象的实现说明1)客户端API向zuul请求,zuul根据spring管理session将session对象存储在redis中,并将生成的sessionId返回给客户端。2)Zuul将请求路由到微服务,将sessionId通过cookie头发送给微服务。3)微服务通过sessionId从redis获取生成的session对象。4)微servcie1调用微服务2时,sessionId也通过cookie头发送给微服务2,微服务2通过sessionId从redis获取session对象。5)客户端再次调用时,将步骤a)返回的sessionId添加到cookie头中,zuul和微服务会共享这个session,直到redis中存储的session失效。5、具体实现1)通过springframework的EnableRedisHttpSession注解管理session,zuul和微服务组件实现这个类来存储和获取redis中存储的session对象。importorg.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;@Configuration@EnableRedisHttpSession(maxInactiveIntervalInSeconds=GlobalConstants.SESSION_TIMOUT,redisFlushMode=RedisFlushMode.IMMEDIATE)publicclassSessionConfig{}EnableRedisHttpSession注解参数说明:maxInactiveIntervalInSeconds:会话过期时间配置。redisFlushMode:redis会话刷新模式。配置为RedisFlushMode.IMMEDIATE,可以保证zuul存储在redis中的session对象在向微服务请求时能够立即获取到。在实际开发过程中,由于没有这个配置值,有时zuul将session对象存储在redis中,微服务却无法立即获取到。2)在zuulfilter方法中调用addZuulRequestHeader添加请求头,将sessionId通过cookie头路由到微服务。公共类AccessFilter扩展ZuulFilter{@AutowiredHttpServletRequesthttpServletRequest;@AutowiredHttpServletResponsehttpServletResponse;@OverridepublicObjectrun(){RequestContextctx=RequestContext.getCurrentContext();StringsessionId=httpServletRequest.getSession().getId();ctx.addZuulRequestHeader("Cookie","SESSION="+sessionId);ctx.setSendZuulResponse(true);//路由请求ctx.setResponseStatusCode(200);//返回200正确响应3)微服务??1通过feign调用微服务2时,实现RequestInterceptor接口。通过添加cookie头将sessionId带到微服务2。@ConfigurationpublicclassMyRequestInterceptorimplementsRequestInterceptor{@AutowiredHttpServletRequest请求;@Overridepublicvoidapply(RequestTemplaterequestTemplate){logger.info("MyRequestInterceptorapplybegin.");(null!=sessionId){requestTemplate.header("Cookie","SESSION="+sessionId);}}catch(Exceptione){logger.error("MyRequestInterceptor异常:",e);}}}6.验证1)通过postman请求zuul服务地址,调用登录接口。2)查看各个组件的sessionIdzuulsessionId:microservice1sessionId:microservice1callsmicroservice2sessionId:结论:可以看到zuul和微服务中的sessionId是一样的,都是586b*c9a4,API调用流程通过这种方式实现了共享中的session对象。
