Spring Boot、Spring Session、redis实现分布式session共享

Spring Boot版本2.1.1 Spring Cloud版本Greenwich.RELEASE   Redis3.2.100一主二从哨兵模式

Spring Session深入详细原理不再本文范畴之内

项目结构采用的maven子父级

pom.xml:

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.session</groupId>
			<artifactId>spring-session-data-redis</artifactId>
		</dependency>

注意版本  redis版本要2.8+

在启动类添加@EnableRedisHttpSession注解表示开启对Spring Session的支持

产品服务:

package com.chwl.cn;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;

import com.chwl.cn.ribbon.CustomRibbon;

@SpringBootApplication
@MapperScan(basePackages="com.chwl.cn.mapper")
@EnableEurekaClient
@EnableCircuitBreaker//开启hystrix服务熔断回调
@EnableCaching//开启本地缓存
@RibbonClient(name="CHWL-PROVIDER-ORDER",configuration=CustomRibbon.class)//自定义的负载均衡算法,针对当前服务按照自己的实际业务进行编写负载均衡算法
@EnableFeignClients//feign声明式API调用(RestTemplate+Ribbon负载均衡)
@EnableScheduling//开启定时任务
@EnableRedisHttpSession//开启spring session的支持
public class ProductApplication {

	public static void main(String[] args) {
		SpringApplication.run(ProductApplication.class, args);
	}
}

测试的其他服务启动类似上面的

application.yml

server: 
  port: 8003
#开启断路器,Feign接口API的实现类方法处理   
feign: 
  hystrix:
    enabled: true
    
hystrix:
  command:
    default:  #default全局有效,service id指定应用有效
      execution:
        timeout:
          #如果enabled设置为false,则请求超时交给ribbon控制,为true,则超时作为熔断根据
          enabled: true
        isolation:
          thread:
            timeoutInMilliseconds: 3000 #断路器超时时间,默认1000ms
    
spring: 
  application: 
    name: chwl-provider-product         #很重要,很重要,很重要,这是微服务向外部暴露的微服务的名字
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    platform: mysql
    url: jdbc:mysql://xxx.xxx.xxx.xxx:5306/chwl?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false
    username: root
    password: admin
  redis: 
#    database: 1
    host: 127.0.0.1
    port: 6379
    password: 
    timeout: 10000
    lettuce:
          pool:
            minIdle: 0
            maxIdle: 10
            maxWait: 10000
            max-active: 10
    sentinel:
      master: master-6379
      nodes: 127.0.0.1:26379,127.0.0.1:26380,127.0.0.1:26381
#    cluster:
#      nodes:
#        - 192.168.91.5:9001
#        - 192.168.91.5:9002
#        - 192.168.91.5:9003
#        - 192.168.91.5:9004
#        - 192.168.91.5:9005
#        - 192.168.91.5:9006
  rabbitmq:
    host: xxx.xxx.xxx.xxx
    port: xxxx
    username: ypp
    password: ypp910913
    publisher-confirms: true #开启消息确认机制
    publisher-returns:  true #开启发送失败退回
    virtual-host: /  #虚拟主机(一个RabbitMQ服务可以配置多个虚拟主机,每一个虚拟机主机之间是相互隔离,相互独立的,授权用户到指定的virtual-host就可以发送消息到指定队列
    template:
      mandatory: true #保证监听有效
    listener:  
      simple:
        acknowledge-mode: manual #消费者的ack方式为手动 auto自动 none不会发送ACK(与channelTransacted=true不兼容)
        concurrency: 1 #最小消费者数量
        max-concurrency: 10 #最大消费者数量
        retry:
          enabled: true  #支持重试/重发
  session:
    redis:
      namespace: chwl  #添加后,redis中的key为spring:session:myproject
      #on_save 只有当SessionRepository.save(Session)方法被调用时, 才会将session中的数据同步到redis中. 在web 应用中, 当请求完成响应后, 才开始同步. 也就是说在执行response 之前session数据都是缓存在本地的.
      #immediate 实时同步session 数据到redis. 当执行SessionRepository.createSession()时, 会将session数据同步到redis中; 当对session的attribute进行set/remove 等操作时, 也会同步session中的数据到redis中.
      flush-mode: on_save #不配置默认on_save
    store-type: redis #指定redis实现spring session
    timeout: 1800   #session过期时间  30分钟

    
mybatis: 
  config-location: classpath:mybatis/mybatis.cfg.xml
  #typeAliasesPackage: com.ypp.springcloud.entites
  mapper-locations: classpath:mybatis/mapper/**/*Mapper.xml
mapper:
  mappers: com.chwl.cn.basemapper.BaseMapper
  identity: mysql
  
eureka:
  client: #客户端注册进eureka服务列表内
    service-url: 
      #defaultZone: http://localhost:2001/eureka    #这个地址就是EurekaServer注册中心的地址
       defaultZone: http://ypp:[email protected]:2001/eureka/,http://ypp:[email protected]:2002/eureka/
  instance: 
    instance-id: chwl-provider-product
    prefer-ip-address: true     #访问路径可以显示IP地址

核心配置:

扫描二维码关注公众号,回复: 9920642 查看本文章

session:
    redis:
      namespace: chwl  #添加后,redis中的key为spring:session:myproject
      #on_save 只有当SessionRepository.save(Session)方法被调用时, 才会将session中的数据同步到redis中. 在web 应用中, 当请求完成响应后, 才开始同步. 也就是说在执行response 之前session数据都是缓存在本地的.
      #immediate 实时同步session 数据到redis. 当执行SessionRepository.createSession()时, 会将session数据同步到redis中; 当对session的attribute进行set/remove 等操作时, 也会同步session中的数据到redis中.
      flush-mode: on_save #不配置默认on_save
    store-type: redis #指定redis实现spring session
    timeout: 1800   #session过期时间  30分钟

也可以换一种配置,直接在启动类添加

@EnableRedisHttpSession(redisNamespace="chwl",redisFlushMode=RedisFlushMode.ON_SAVE,cleanupCron="0 * * * * *",maxInactiveIntervalInSeconds=1800)//开启spring session的支持
public class ProductApplication {

	public static void main(String[] args) {
		SpringApplication.run(ProductApplication.class, args);
	}
}

cleanupCron:过期会话清理作业的cron表达式。默认每分钟运行一次

maxInactiveIntervalInSeconds:会话超时(秒)。默认设置为1800秒(30分钟),不能为负整数

重要:

springsession会拿服务器时间 跟 redis存储的时间比对,看时间差是否超时、失效

如果时间不同步,可能有些请求就会session失效,或者自己往session中存放的需手工确认失效的功能 

使用就很简单了,这就是Spring Boot的好处

启动网关、eureka注册中心、产品服务、会员服务、订单服务

红色字体为eureka启动了自我保护机制

member服务登录

@RequestMapping("/sessionLogin")
	public String sessionLogin(String memberName,String pwd,HttpServletRequest request) throws Exception {
		MemberEntity m = new MemberEntity();
		m.setMemberName(memberName);
		pwd = MD5Utils.convertMD5(MD5Utils.encrypt32(pwd));
		m.setPwd(pwd);
		List<MemberEntity> list = memberService.select(m);
		if (list != null && list.size() > 0) {
			MemberEntity member = list.get(0);
			request.getSession().setAttribute("member", member);
			return JsonMsg.OK("登录成功");
		} else {
			return JsonMsg.Error("用户名或密码错误");
		}
	}

product/order服务获取session信息

@RequestMapping(value = "/getSessionMember")
	public MemberEntity getSessionMember(HttpServletRequest request) {
		return (MemberEntity) request.getSession().getAttribute("member");
	}

查看redis

   

这里的namespace不是chwl(正常的应该是配置的namespace的chwl)

使用浏览器死活获取不到session信息,都是null,后来发现是网关的问题,把网关服务停了就正常了

将session托管在redis实现session共享成功。但在有zuul这种网关的时候就不行了,因为zuul网关默认把cookie进行了过滤

源码类在ZuulProperties

关键点源码:

/**
	 * List of sensitive headers that are not passed to downstream requests. Defaults to a
	 * "safe" set of headers that commonly contain user credentials. It's OK to remove
	 * those from the list if the downstream service is part of the same system as the
	 * proxy, so they are sharing authentication data. If using a physical URL outside
	 * your own domain, then generally it would be a bad idea to leak user credentials.
	 */
	private Set<String> sensitiveHeaders = new LinkedHashSet<>(
			Arrays.asList("Cookie", "Set-Cookie", "Authorization"));

可以看到zuul对"Cookie", "Set-Cookie", "Authorization"敏感信息进行了过滤导致session不一致获取不到

可以在application.properties配置zuul.sensitive-headers="*"

application.yml

#自定义路由映射    
zuul: 
  routes: 
    chwl-provider-product: /apigateway/product/**
    chwl-provider-order: /apigateway/order/**
    chwl-provider-member: /apigateway/member/**
  sensitive-headers: "*"
  #统一入口为上面的配置,其他入口忽略
  ignored-patterns: /*-provider-*/**
  #忽略整个服务,对外提供接口
  #ignored-services: chwl-provider-product
发布了288 篇原创文章 · 获赞 88 · 访问量 43万+

猜你喜欢

转载自blog.csdn.net/ypp91zr/article/details/90647442
今日推荐