spring-session+redis+zuul session共享示例

分布式系统中,session共享方案长借助于数据库,将session存放于数据库中,多个应用客户端共连一个数据库,以此达到session共享的方案。数据库中,redis在session共享中通常作为第一选择,spring框架提供了一个集成httpsession和redis的解决方案,这篇文章,就展示spring-session和redis共享session的示例。

工程组件说明

  1. servicecenter 服务注册中心
  2. zuul 网关
  3. UserManagerA 用户服务A
  4. UserManagerB 用户服务B
  5. BuyManager 购买服务

其中,servicecenter单纯做注册中心,zuul做网关路由功能。UserManagerA 和 UserManagerB作为用户服务两台应用服务,BuyManager作为一个购买服务。

用户登录session处理在UserManagerA 和 UserManagerB 服务中,在任意一台服务创建session会话后,在另外一台服务中都可以看到。BuyManager作为另外的一台服务,将展示同一个redis服务中的不同应用可以共享到session信息。

工程代码示例

pom文件

使用spring-session redis方案时,引用的jar支持

		<!--redis的依赖-->
		<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>

其余各项目依赖,根据项目须要引入。

servicecenter

注册中心无其它代码内容,仅一个注册中心配置项文件,设置注册中心地址 localhost:8761

server:
  port: 8761 #服务注册中心端口号
eureka:
  instance:
    hostname: localhost
  client:
    registerWithEureka: false #是否向服务注册中心注册自己
    fetchRegistry: false #是否检索服务
    serviceUrl: #服务注册中心的配置内容,指定服务注册中心位置
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

zuul

网关做路由分发功能

设置网关端口号5000,设置注册中心地址。设置路由分发策略,将/um开头服务分发到um服务实例;将/bm开头服务分发到bm服务实例。

# 服务注册中心地址
eureka.client.service-url.defaultZone=http://localhost:8761/eureka
# 设置程序端口号为5000,服务名为zuul-service
server.port=5000
spring.application.name=zuul
# 将以"/um"开头的url路由到um服务
zuul.routes.um.path=/um/**
zuul.routes.um.serviceId=um
zuul.routes.um.sensitiveHeaders=*
# 将以"/bm"开头的url路由到bm服务
zuul.routes.bm.path=/bm/**
zuul.routes.bm.serviceId=bm
zuul.routes.bm.sensitiveHeaders=*
zuul.host.socket-timeout-millis=60000
zuul.host.connect-timeout-millis=60000

springboot主启动类添加注解,@EnableZuulProxy注解,开启Zuul功能

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

/**
 * 添加@EnableEurekaClient注解,开启EurakaClient功能
 * 添加@EnableZuulProxy注解,开启Zuul功能
 * @author PC
 */
@EnableZuulProxy
@EnableEurekaClient
@SpringBootApplication
public class Zuul {

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

UserManagerA工程

UserManagerB和UserManagerA工程类似,这里只展示一个工程,简要说明差异处。

配置文件,说明工程名称为um,配置redis服务器信息和工程信息,其中UserManagerA端口为8766,UserManagerB工程端口为8769

spring.application.name=um
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
server.port=8766

spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=123456
spring.redis.database=0
spring.redis.timeout=5000
spring.session.store-type=redis

UserManagerA 工程访问入口类 SpringSessionRedisSessionShared

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.fastjson.JSONObject;

@EnableEurekaClient
@RestController
public class SpringSessionRedisSessionShared {

	@Value("${server.port}")
	private String port;
	
	@ResponseBody
	@RequestMapping("/loginIn")
	public String login(HttpServletRequest request, String token) {

		HttpSession session = request.getSession();
		System.out.println("session=" + JSONObject.toJSONString(session));
		String name = (String) session.getAttribute("name");
		
		if (null != name) {
			return port + " success.name=" + name; 
		}

		session.setAttribute("name", token);
		return port + " " + token;
	}
	
	@ResponseBody
	@RequestMapping("/go")
	public String test(HttpServletRequest request, String token) {
		HttpSession session = request.getSession();
		System.out.println("session=" + JSONObject.toJSONString(session));
		String name = (String) session.getAttribute("name");
		System.out.println("spring-session name=" + name);
		if (null != name) {
			return port + " success."; 
		}
		
		return port + " test failed";
	}
}

其中,token为前端传递的一个参数信息,该信息会被放进session中,同步的spring-session框架会将session同步到redis中。

UserManagerA启动类,这里一定添加注解@EnableRedisHttpSession,以示启动redis作为spring-session共享方案。括号中maxInactiveIntervalInSeconds时间为秒数,若不设置,则默认不过期,永久有效。这里设置为120秒,即两分钟。

@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 120)
@SpringBootApplication
public class UserA {

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

BuyManager

配置文件

spring.application.name=bm
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
server.port=8767

spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=123456
spring.redis.database=0
spring.redis.timeout=3000
spring.session.store-type=redis

代码类

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.fastjson.JSONObject;

@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 120)
@RestController
public class HelloController {

	@Value("${server.port}")
	private String port;
	
	@ResponseBody
	@RequestMapping("/hello")
	public String hello(String name) {
		return "Hi, I am " + name + ". This service was supplied by instance " + port;
	}
	
	
	@ResponseBody
	@RequestMapping("/come")
	public String come(HttpServletRequest request) {
		
		HttpSession session = request.getSession();
		String loginUserId = (String) session.getAttribute("name");	
		if (null != loginUserId) {
			System.out.println("session=" + JSONObject.toJSONString(session));
			return "come get session success";
		}
        
		System.out.println("session=" + JSONObject.toJSONString(session));
        
		return "come get session failed";
	}
}

项目测试

依次启动 servicecenter、zuul、UserManagerA、UserManagerB 和 BuyManager工程,启动完毕后,打开注册中心 http://localhost:8761/ 可以看到各实例注册成功

在浏览器输入 http://localhost:5000/um/loginIn?token=spring-session 访问UserManager工程创建session

可以看到,在8769端口对应的实例(UserManagerA工程),创建session成功

此时,在redis客户端查看存在于redis中的session信息

session信息在redis中使用hash列表存储,其中spring:session:sessions:sessionId 为hash key值。

在浏览器中输入http://localhost:5000/um/go 测试session共享情况,由于zuul默认轮询策略,很快可以看到服务请求到8766实例,session请求成功

继而到 http://localhost:5000/bm/come ,这时请求BuyManager工程,可以看到session请求成功

代码中,设置的maxInactiveIntervalInSeconds 为120秒,两分钟后,再去请求服务,各请求均失败

测试过程中,可以看后台应用打印的日志信息,多次打印的session创建时间和sessionId 均相同,表示为同一个session信息。

session={"attributeNames":["name"],"creationTime":1609893405410,"id":"e47ef9eb-041b-497b-a6c8-0d77672cc08b","lastAccessedTime":1609893409057,"maxInactiveInterval":120,"new":false,"servletContext":{},"session":{"attributeNames":["name"],"creationTime":"2021-01-06T00:36:45.410Z","expired":false,"id":"e47ef9eb-041b-497b-a6c8-0d77672cc08b","lastAccessedTime":"2021-01-06T00:36:49.057Z","maxInactiveInterval":"PT2M","new":false},"sessionContext":{"ids":[]},"valueNames":["name"]}

简单示例就到这里,写的仓促,有细节不清楚的还望留言沟通讨论。

猜你喜欢

转载自blog.csdn.net/magi1201/article/details/112252243
今日推荐