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:admin@eureka2001.com:2001/eureka/,http://ypp:admin@eureka2002.com:2002/eureka/
instance:
instance-id: chwl-provider-product
prefer-ip-address: true #访问路径可以显示IP地址
核心配置:
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