Spring Cloud分布式Session共享实践

通常情况下,Tomcat、Jetty等Servlet容器,会默认将Session保存在内存中。如果是单个服务器实例的应用,将Session保存在服务器内存中是一个非常好的方案。但是这种方案有一个缺点,就是不利于扩展。

目前越来越多的应用采用分布式部署,用于实现高可用性和负载均衡等。那么问题来了,如果将同一个应用部署在多个服务器上通过负载均衡对外提供访问,如何实现Session共享?

实际上实现Session共享的方案很多,其中一种常用的就是使用Tomcat、Jetty等服务器提供的Session共享功能,将Session的内容统一存储在一个数据库(如MySQL)或缓存(如Redis)中。

下面我们将在springcloud微服务项目中,使用redis实现简单高效的session共享。

项目介绍

  • eureka-server:注册中心
  • springcloud-session-redis:业务session所在项目
  • springcloud-session-zuul:路由网关

eureka-server延用之前Eureka服务注册与发现一文中的注册中心,不再赘述

springcloud-session-redis项目

新建一个spring boot项目,命名springcloud-session-redis

POM依赖配置

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <project xmlns="http://maven.apache.org/POM/4.0.0"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 5     <modelVersion>4.0.0</modelVersion>
 6 
 7     <groupId>com.carry</groupId>
 8     <artifactId>springcloud-session-redis</artifactId>
 9     <version>0.0.1-SNAPSHOT</version>
10     <packaging>jar</packaging>
11 
12     <name>springcloud-session-redis</name>
13     <description>Demo project for Spring Boot</description>
14 
15     <parent>
16         <groupId>org.springframework.boot</groupId>
17         <artifactId>spring-boot-starter-parent</artifactId>
18         <version>2.0.4.RELEASE</version>
19         <relativePath /> <!-- lookup parent from repository -->
20     </parent>
21 
22     <properties>
23         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
24         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
25         <java.version>1.8</java.version>
26         <spring-cloud.version>Finchley.SR1</spring-cloud.version>
27     </properties>
28 
29     <dependencies>
30         <dependency>
31             <groupId>org.springframework.boot</groupId>
32             <artifactId>spring-boot-starter-data-redis</artifactId>
33         </dependency>
34         <dependency>
35             <groupId>org.springframework.session</groupId>
36             <artifactId>spring-session-data-redis</artifactId>
37         </dependency>
38         <dependency>
39             <groupId>org.apache.commons</groupId>
40             <artifactId>commons-pool2</artifactId>
41         </dependency>
42         <dependency>
43             <groupId>org.springframework.boot</groupId>
44             <artifactId>spring-boot-starter-web</artifactId>
45         </dependency>
46         <dependency>
47             <groupId>org.springframework.cloud</groupId>
48             <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
49         </dependency>
50         <dependency>
51             <groupId>org.springframework.boot</groupId>
52             <artifactId>spring-boot-starter-actuator</artifactId>
53         </dependency>
54 
55     </dependencies>
56 
57     <dependencyManagement>
58         <dependencies>
59             <dependency>
60                 <groupId>org.springframework.cloud</groupId>
61                 <artifactId>spring-cloud-dependencies</artifactId>
62                 <version>${spring-cloud.version}</version>
63                 <type>pom</type>
64                 <scope>import</scope>
65             </dependency>
66         </dependencies>
67     </dependencyManagement>
68 
69     <build>
70         <plugins>
71             <plugin>
72                 <groupId>org.springframework.boot</groupId>
73                 <artifactId>spring-boot-maven-plugin</artifactId>
74             </plugin>
75         </plugins>
76     </build>
77 
78 
79 </project>

配置文件

在application.yml中加入redis、eureka、port等配置

server:
  port: 8090
spring:
  application:
    name: service-session-redis
  redis:
    host: 192.168.68.100
    port: 6379
    password: 123456
    timeout: 6000ms
    lettuce:
      pool:
        max-active: 8
        max-wait: -1ms
        max-idle: 8
        min-idle: 0
    database: 0
eureka:
  client:
    serviceUrl:
      defaultZone: http://admin:123456@localhost:8761/eureka/
management: 
  endpoints:
    web:
      exposure: 
        include: "*"
      cors:
        allowed-origins: "*"
        allowed-methods: "*" 

Redis Session配置类

 1 package com.carry.config;
 2 
 3 import org.springframework.context.annotation.Configuration;
 4 import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
 5 
 6 @Configuration
 7 @EnableRedisHttpSession
 8 public class RedisSessionConfig {
 9 
10 }

控制层Controller中添加测试方法

 1 package com.carry.controller;
 2 
 3 import javax.servlet.http.HttpServletRequest;
 4 import javax.servlet.http.HttpSession;
 5 
 6 import org.springframework.cloud.context.config.annotation.RefreshScope;
 7 import org.springframework.util.StringUtils;
 8 import org.springframework.web.bind.annotation.GetMapping;
 9 import org.springframework.web.bind.annotation.RestController;
10 
11 @RestController
12 @RefreshScope
13 public class UserManagementController {
14 
15     /**
16      * redis sesion共享
17      * 
18      * @param request
19      * @return
20      */
21     @GetMapping("/getUser")
22     public String getUser(HttpServletRequest request) {
23         HttpSession session = request.getSession();
24         String username = (String) session.getAttribute("username");
25         if (StringUtils.isEmpty(username)) {
26             username = "testSessionRedis|" + System.currentTimeMillis();
27             session.setAttribute("username", username);
28         }
29         System.out.println("访问端口:" + request.getServerPort());
30         return username;
31     }
32 }

springcloud-session-zuul项目

新建springboot项目,命名springcloud-session-zuul

POM依赖配置

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <project xmlns="http://maven.apache.org/POM/4.0.0"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 5     <modelVersion>4.0.0</modelVersion>
 6 
 7     <groupId>com.carry</groupId>
 8     <artifactId>springcloud-session-zuul</artifactId>
 9     <version>0.0.1-SNAPSHOT</version>
10     <packaging>jar</packaging>
11 
12     <name>springcloud-session-zuul</name>
13     <description>Demo project for Spring Boot</description>
14 
15     <parent>
16         <groupId>org.springframework.boot</groupId>
17         <artifactId>spring-boot-starter-parent</artifactId>
18         <version>2.0.4.RELEASE</version>
19         <relativePath /> <!-- lookup parent from repository -->
20     </parent>
21 
22     <properties>
23         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
24         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
25         <java.version>1.8</java.version>
26         <spring-cloud.version>Finchley.SR1</spring-cloud.version>
27     </properties>
28 
29     <dependencies>
30         <dependency>
31             <groupId>org.springframework.cloud</groupId>
32             <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
33         </dependency>
34         <dependency>
35             <groupId>org.springframework.cloud</groupId>
36             <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
37         </dependency>
38     </dependencies>
39 
40     <dependencyManagement>
41         <dependencies>
42             <dependency>
43                 <groupId>org.springframework.cloud</groupId>
44                 <artifactId>spring-cloud-dependencies</artifactId>
45                 <version>${spring-cloud.version}</version>
46                 <type>pom</type>
47                 <scope>import</scope>
48             </dependency>
49         </dependencies>
50     </dependencyManagement>
51 
52     <build>
53         <plugins>
54             <plugin>
55                 <groupId>org.springframework.boot</groupId>
56                 <artifactId>spring-boot-maven-plugin</artifactId>
57             </plugin>
58         </plugins>
59     </build>
60 
61 
62 </project>

配置文件

server:
  port: 1100
spring:
  application:
    name: service-session-zuul
zuul: 
  ignoredServices: '*' #忽略所有未配置的service
  host:
    connect-timeout-millis: 20000
    socket-timeout-millis: 20000
  routes:
    redis-session-service: 
      path: /user-session/**
      serviceId: service-session-redis
      sensitiveHeaders: "*" 
ribbon: #ribbon负载均衡参数配置
  ReadTimeout: 5000
  ConnectTimeout: 5000
eureka:
  client:
    serviceUrl:
      defaultZone: http://admin:123456@localhost:8761/eureka/

启动类

 1 package com.carry;
 2 
 3 import org.springframework.boot.SpringApplication;
 4 import org.springframework.boot.autoconfigure.SpringBootApplication;
 5 import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
 6 import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
 7 
 8 @EnableEurekaClient
 9 @SpringBootApplication
10 @EnableZuulProxy
11 public class SpringcloudSessionZuulApplication {
12 
13     public static void main(String[] args) {
14         SpringApplication.run(SpringcloudSessionZuulApplication.class, args);
15     }
16 }

测试

依次启动

  • eureka-server
  • springcloud-session-redis
  • springcloud-session-zuul

多次访问网关地址http://localhost:1100/user-session/getUser,发现后台轮询访问springcloud-session-redis的8090与8091两个实例

而我们页面得到的结果是不变的,由此可以说明session是一致的

注意:springcloud项目中经过网关zuul转发请求后发生session失效问题,这是由于zuul默认会丢弃原来的session并生成新的session,解决方法网关配置文件application.yml 中添加 sensitiveHeaders: “*”

原文地址:https://www.cnblogs.com/carrychan/p/9548013.html