Spring Session + Redis 实现 Session 共享,附带 Nginx 集群

目录

Session 共享概述

准备 Redis

创建 Spring Boot 工程

集群访问测试

Nginx 集群


Session 共享概述

1、传统的单服务架构中,一般来说,只有一个服务器,那么不存在 Session 共享问题,而分布式/集群项目中,Session 共享则是一个必须面对的问题。例如客户端发起一个请求,这个请求到达 Nginx 上之后,被 Nginx 转发到 Tomcat A 上,然后在 Tomcat A 上往 session 中保存了一份数据,下次又来一个请求,这个请求被转发到 Tomcat B 上,此时再去 Session 中获取数据,发现没有之前的数据。

2、解决 Session 共享的方式有很多(传送门),使用 Redis 就是其中一个,它将 session 的 id 放入 redis 中:

3、当所有 Tomcat/web应用需要往 Session 中写数据时,都往 Redis(数据库)中写,当所有 Tomcat 需要读数据时,都从 Redis(数据库) 中读,这样不同的服务就可以使用相同的 Session 数据了。这一切底层的操作都已经由 Spring Session 组件代劳了。

4、Spring Session 使用 Spring 中的代理过滤器,将所有的 Session 操作拦截下来,自动的将数据同步到 Redis 中,自动的从 Redis 中读取数据。对于开发者来说,所有关于 Session 同步的操作都是透明的,所有关于 HttpSession 的操作都与平时普通的操作无异。

准备 Redis

1、直接在 windows 本机上运行 Redis 服务器,不设置密码,使用默认端口,双击运行如下:

2、注意事项:使用的 Redis 版本尽量要高,最好在 2.8 及以上,否则会因为与 Spring Session 版本不匹配报错

[30832] 22 Jun 15:19:39.979 # Warning: no config file specified, using the default config. In order to specify a config file use D:\wmx_infos\zhongKeJiangNan_environment\server20190513\Redis\redis-server.exe /path/to/redis.conf
                _._
           _.-``__ ''-._
      _.-``    `.  `_.  ''-._           Redis 3.2.100 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._
 (    '      ,       .-`  | `,    )     Running in standalone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
 |    `-._   `._    /     _.-'    |     PID: 30832
  `-._    `-._  `-./  _.-'    _.-'
 |`-._`-._    `-.__.-'    _.-'_.-'|
 |    `-._`-._        _.-'_.-'    |           http://redis.io
  `-._    `-._`-.__.-'_.-'    _.-'
 |`-._`-._    `-.__.-'    _.-'_.-'|
 |    `-._`-._        _.-'_.-'    |
  `-._    `-._`-.__.-'_.-'    _.-'
      `-._    `-.__.-'    _.-'
          `-._        _.-'
              `-.__.-'

[30832] 22 Jun 15:19:39.987 # Server started, Redis version 3.2.100
[30832] 22 Jun 15:19:39.987 * DB loaded from disk: 0.001 seconds
[30832] 22 Jun 15:19:39.987 * The server is now ready to accept connections on port 6379

4、使用 Redis-cli.exe 客户端连接操作如下(此时 Redis 数据库中并没有任何数据):

127.0.0.1:6379> ping
PONG
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> dbsize
(integer) 0
127.0.0.1:6379>

(清空 Redis 所有数据库中的的所有数据命令:flushall )

创建 Spring Boot 工程

1、先创建一个 Spring Boot 工程,引入 Web、Spring Session 以及 Redis 组件。

2、本文环境为 Java JDK 1.8 + Spring Boot 2.1.4 + Redis 3.2.100 + Nginx-1.16.0,项目创建后,pom.xml 文件依赖内容如下:

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

3、全局配置文件 application.properties 配置 Redis 如下:

spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=
spring.redis.database=0

#本文使用 Spring Boot 2.1.4,当搭配 version 2.4.5 的 Redis 服务器时,启动应用报错如下:
#io.lettuce.core.RedisCommandExecutionException: ERR Unsupported CONFIG parameter: notify-keyspace-events
#这是因为 redis 版本太低,跟新到 2.8 或者 3.* 版本以上就好了

#redis 服务器地址为本机,端口为 6397,密码为空,数据库为 0 号数据库

更多关于 redis 的配置,可以参考官网:Common application properties

4、配置完成后 ,新建一个 Controller 使用 Spring Session,与使用普通的 HttpSession 一致,其它的 Session 同步到 Redis 等操作,Spring Session 框架已经自动代理完成了。

import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpSession;
import java.util.logging.Logger;

@RestController
public class UserController {
    private Logger logger = Logger.getAnonymousLogger();
    @Value("${server.port}")
    private String server_port;

    @GetMapping("userLogin")
    public String userLogin(HttpSession httpSession) {
        logger.info("用户登陆...");

        httpSession.setAttribute("user", "huaWei");
        JsonNodeFactory nodeFactory = JsonNodeFactory.instance;
        ObjectNode objectNode = nodeFactory.objectNode();
        objectNode.put("port", server_port);
        objectNode.put("sessionId", httpSession.getId());
        objectNode.put("message", "用户登陆,添加 session 数据,user=huaWei");
        return objectNode.toString();
    }

    @GetMapping("userQuit")
    public String userQuit(HttpSession httpSession) {
        logger.info("用户退出...");

        Object user = httpSession.getAttribute("user");//user 不存在时,返回 null
        JsonNodeFactory nodeFactory = JsonNodeFactory.instance;
        ObjectNode objectNode = nodeFactory.objectNode();

        objectNode.put("port", server_port);
        objectNode.put("sessionId", httpSession.getId());
        if (user != null) {
            objectNode.put("user", (String) user);
            objectNode.put("message", "用户退出,移除 session 数据");
            httpSession.removeAttribute("user");//移除HttpSession数据
        } else {
            objectNode.put("user", "null");
            objectNode.put("message", "当前 HttpSession 并没有保持数据");
        }
        return objectNode.toString();
    }
}

5、可以启动项目测试,不过这不是重点,集群访问才是重点,将项目打包(应用打包前先将 Redis 服务器启动):

集群访问测试

1、启动两个实例,分别使用 8081,8082 端口:

java -jar share_session-0.0.1-SNAPSHOT.jar --server.port=8081
java -jar share_session-0.0.1-SNAPSHOT.jar --server.port=8082

2、使用浏览器先访问:http://localhost:8081/userQuit,此时返回(因为此时还没有设置session数据,所以 user为null):

{"port":"8082","sessionId":"0cf858c2-545d-49bd-b767-3462c6d0b985","user":"null","message":"当前 HttpSession 并没有保持数据"}

3、同一个浏览器访问: http://localhost:8082/userLogin,此时返回如下(设置session数据成功):

{"port":"8081","sessionId":"0cf858c2-545d-49bd-b767-3462c6d0b985","message":"用户登陆,添加 session 数据,user=huaWei"}

4、同一个浏览器再访问 http://localhost:8081/userQuit,此时返回(获取 8082 设置的session数据成功):

{"port":"8082","sessionId":"0cf858c2-545d-49bd-b767-3462c6d0b985","user":"huaWei","message":"用户退出,移除 session 数据"}

5、由此可见 8082 应用设置的 session 数据,8081 应用获取成功,也就说明 session 在各应用之间是共享的。Redis 数据库中保存了同步的 Session 数据。

127.0.0.1:6379> keys *
1) "spring:session:expirations:1561195800000"
2) "spring:session:sessions:0cf858c2-545d-49bd-b767-3462c6d0b985"
3) "spring:session:sessions:expires:0cf858c2-545d-49bd-b767-3462c6d0b985"
127.0.0.1:6379>

Nginx 集群

1、关于 session 共享就已经全部完成了,最核心的同步操作都由 Spring Session 代劳了,写起来非常简单。

2、接下来引入 Nginx 服务器进行集群,实现服务实例/应用的自动切换。仍然使用 windows 版本的 Nginx-1.16.0。

3、配置 conf 子目录下的核心配置文件 nginx.conf 如下:

#gzip  on;
upstream serverWmx {
    server 127.0.0.1:8081;
    server 127.0.0.1:8082;
}

server {
    listen       80;
    server_name  localhost;
    #charset koi8-r;
    #access_log  logs/host.access.log  main;
    location / {
        root   html;
        index  index.html index.htm;
        proxy_pass http://serverWmx;
    }

upstream :配置集群的服务器,后面的名称 "serverWmx" 自定义即可,但是不建议使用 "_"字符,否则 Nginx v1.16 版本会报错。

weight:表示权重,意味者将有多少比例的请求从 Nginx 上转发到该服务上,数值越大,被访问的概率越高。

4、双击 nginx.exe 运行 nginx 服务器,Nginx 默认端口为 80,浏览器访问地址如下,它会自动进行转发:

http://localhost:80/userQuit
http://localhost:80/userLogin

发布了458 篇原创文章 · 获赞 884 · 访问量 92万+

猜你喜欢

转载自blog.csdn.net/wangmx1993328/article/details/93406208
今日推荐