前言
在分布式环境中,我们往往需要考虑很多,不仅仅是原来单体应用的思路,要转变新的思路。数据来源于不同的服务端,产生结构一致的数据,怎么保证数据的一致性,往往是我们值得思考的。
分布式锁 - 几种解决方案 :https://bearjun.com/java/421.html
分布式限流 - 几种解决方案 :https://bearjun.com/java/383.html
接口的幂等性操作: https://bearjun.com/java/375.html
session共享问题
众所周知,session都只是存在当前的域名下,并且是当前的服务中。当分布式环境中,服务器可能存在多台,并且服务之间的跳转会在多个域名中反复跳转。
session复制
具体操作可以看这个文章:https://blog.csdn.net/wlwlwlwl015/article/details/48160433
优点
- web-server(Tomcat)原生支持,只需要修改配置 文件
缺点
- 大型分布式集群情况下,由于所有web-server都全 量保存数据,所以此方案不可取。
- 任意一台web-server保存的数据都是所有web- server的session总和,受到内存限制无法水平扩展 更多的web-server
- session同步需要数据传输,占用大量网络带宽,降 低了服务器群的业务处理能力
客户端存储
这个只是提供一个思路,应该没有这么用的吧。
优点
- 服务器不需存储session,用户保存自己的session信息到cookie中。节省服务端资源
缺点
- 每次http请求,携带用户在cookie中的完整信息, 浪费网络带宽
- session数据放在cookie中,cookie有长度限制 4K,不能保存大量信息
- session数据放在cookie中,存在泄漏、篡改、窃取等安全隐患
hash一致性
这个主要的缺点问题也不是很大,因为session本来都是有有效期的。所以这两种反向代理的方式可以使用。
这个主要就是用nginx的ip_hash绑定到唯一的一个服务器,然后让自己的请求一直请求到该服务器。
upstream aaa {
ip_hash;
server xx.xxx.xx.x:8080;
Server xx.xxx.xx.x:8081;
}
server {
listen 80;
server_name www.bearjun.com;
#root /usr/local/nginx/html;
#index index.html index.htm;
location / {
proxy_pass http:xx.xxx.xx.x;
index index.html index.htm;
}
}
优点
- 只需要改nginx配置,不需要修改应用代码
- 负载均衡,只要hash属性的值分布是均匀的,多台web-server的负载是均衡的
- 可以支持web-server水平扩展(session同步法是不行 的,受内存限制)
缺点
- session还是存在web-server中的,所以web-server重 启可能导致部分session丢失,影响业务,如部分用户 需要重新登录
- 如果web-server水平扩展,rehash后session重新分布,也会有一部分用户路由不到正确的session
统一存储(spring Session)
以下缺点可通过spring session解决。
Spring Session
官方文档地址:https://docs.spring.io/spring-session/docs/2.5.0/reference/html5/
- 导入maven
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
- 配置spring.session
# 存储方式redis/DB/MongDB...
spring.session.store-type=redis
# 超时时间,默认是30m
server.servlet.session.timeout=30m
# Sessions刷新方式
spring.session.redis.flush-mode=on_save
# session存储的前缀
spring.session.redis.namespace=spring:session
- 配置redis信息
spring.redis.host=localhost # Redis server host.
spring.redis.password= # Login password of the redis server.
spring.redis.port=6379 # Redis server port.
- 主配置类添加注解
/**整合redis作为session存储*/
@EnableRedisHttpSession
public class Startup extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Startup.class);
}
}
- 自定义配置
@Configuration
public class SessionConfig {
// 把session的作用域放大,例如auth.bearjun.com和berajun.com的session可以实现共享
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setDomainNamePattern("bearjun.com");
return serializer;
}
// 序列化存储的数据
@Bean
public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
return new GenericJackson2JsonRedisSerializer();
}
}
注意:要存储的对象一定要Serializable接口
优点
- 没有安全隐患
- 可以水平扩展,数据库/缓存水平切分即可
- web-server重启或者扩容都不会有session丢失
缺点
- 增加了一次网络调用,并且需要修改应用代码;如将所有的getSession方法替换为从Redis查数据的方式。redis获取数据比内存慢很多。
评论区