springboot-分布式实例开发(十 一)-Dubbo


上一节介绍了微服务思想,以及RPC协议,这一节教程将围绕着dubbo框架进行讲解

1 RPC与HTTP

网络7层协议如图所示.
在这里插入图片描述
层级关系与对应的协议.
在这里插入图片描述
区别:
1.RPC是传输层协议(4层).而HTTP协议是应用层协议(7层).
2.RPC协议可以直接调用中立接口,HTTP协议不可以.
3.RPC通信协议是长链接,HTTP协议一般采用短连接需要3次握手(可以配置长链接添加请求头Keep-Alive: timeout=20).
(长连接,指在一个连接上可以连续发送多个数据包,在连接保持期间,如果没有数据包发送,需要双方发链路检测包。)
4.RPC协议传递数据是加密压缩传输.HTTP协议需要传递大量的请求头信息.
5.RPC协议一般都有注册中心.有丰富的监控机制.

2 Dubbo

2.1 dubbo介绍

Dubbo是 阿里巴巴公司开源的一个高性能优秀的服务框架(SOA),使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成。
Dubbo是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。

通俗的介绍一下,Dubbo是基于java实现RPC的框架,Dubbo 工作分为 4 个角色,分别是服务提供者、服务消费者、注册中心、和监控中心。如下图是官网的架构图(摘自官网)
在这里插入图片描述
在这里插入图片描述
调用关系说明

  1. 服务容器负责启动,加载,运行服务提供者。
  2. 服务提供者在启动时,向注册中心注册自己提供的服务。
  3. 服务消费者在启动时,向注册中心订阅自己所需的服务。
  4. 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
  5. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
  6. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

仔细一看,我们会发现此架构跟生产者-消费者模型很相似.此框架多了注册中心和监控中心,
用我们的项目再详细讲解一下:
在这里插入图片描述
jt-cart:购物车功能模块,jt-order:订单功能模块
调用过程:

  1. 当服务提供者启动时会将自己的IP:端口:服务名称写入注册中心.
  2. 当注册中心接收服务器信息时,会在本地进行记录.维护服务列表信息. 树形结构
  3. 当消费者启动时,会连接注册中心.获取服务列表数据.将服务列表数据保存到消费者服务器本地.为了后续调用方便.
  4. 当用户需要做业务处理时.消费者接收用户的请求.之后查询服务列表信息找到能够提供服务的IP:端口,之后利用负载均衡的机制挑选其中一台服务器发起请求.
  5. 当后台服务器但宕机时,注册中心会通过心跳检测机制校验服务器是否存活,如果发现服务器宕机.则会更新注册中心本地的服务列表信息.
  6. 当注册中心的服务列表数据发现变化.则全网广播动态的更新全部消费者的服务列表数据.

2.2 Zookeper注册中心

ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。

ZooKeeper的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。

ZooKeeper包含一个简单的原语集,提供Java和C的接口。

简单来说 zookeeper用来注册服务和进行负载均衡,哪一个服务由哪一个机器来提供必需让调用者知道,简单来说就是ip地址和服务名称的对应关系。

总结:Zookeeper是分布式服务调度器.它是一个为分布式应用提供一致性服务的软件
关于一致性说明:zookeeper中必须满足一致性的要求.防止因为主机意外宕机,而用户使用错误的数据. zookeeper是CP原则(请看下面介绍)

2.2.1 CAP原则(了解,可跳过)

在这里插入图片描述
CAP原则又称CAP定理,指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。
C:一致性: 在分布式系统中的所有数据备份,在同一时刻是否同样的值。
A:可用性: 在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)
P:分区容错性: 以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。

2.2.2 zookeper安装

下载地址:http://mirrors.hust.edu.cn/apache/zookeeper/
在这里插入图片描述下载的时候要选带有bin的安装包
在这里插入图片描述

官网地址:https://zookeeper.apache.org/releases.html
在这里插入图片描述连接服务器:并上传安装包到指定位置
在这里插入图片描述

解压文件:

tar -xvf apache-zookeeper-3.6.1-bin.tar.gz

在zk根目录下创建文件夹data/log

mkdir data log

我嫌文件名太长,我就改了个名
在这里插入图片描述到conf文件中,复制配置文件并且修改名称

cp zoo_sample.cfg zoo.cfg

在这里插入图片描述
修改配置(dataLogDir自行添加)
在这里插入图片描述
zk启动关闭命令如下

sh zkServer.sh start     或者  ./zkServer.sh start
sh zkServer.sh stop
sh zkServer.sh status

在这里插入图片描述
Zookeeper集群
在zookeeper根目录中创建新的文件夹zkCluster.

mkdir zkCluster

进入本文件夹中,在创建zk1 zk2 zk3文件夹
在这里插入图片描述
在每个文件夹里创建data/log文件夹.

mkdir {zk1,zk2,zk3}/{data,log}

分别在zk1,zk2,zk3中的data文件夹中创建新的文件myid.其中的内容依次为1/2/3,与zk节点号对应.
例如在data目录下直接输入命令 vim myid。然后点击a,进入编辑状态,输入1,然后保存退出。就会自动创建文件
在这里插入图片描述

输入命令cat myid.可以看到文件的内容
在这里插入图片描述

其他俩文件自行创建。

将zoo_sample.cfg 复制为zoo1.cfg之后修改配置文件.
在这里插入图片描述
在这里插入图片描述
配置完成后将zoo1.cfg复制2份.分别为zoo2.cfg zoo3.cfg
看一下zoo2.cfg的配置,端口和文件路径需要修改,其他的不变。zoo3.cfg也是这样操作
在这里插入图片描述
配置好后,在bin目录下执行命令

sh zkServer.sh start   zoo1.cfg
sh zkServer.sh stop    zoo1.cfg
sh zkServer.sh status  zoo1.cfg

执行三个服务,并查看主从关系
在这里插入图片描述
Zookeeper集群中leader负责监控集群状态,follower主要负责客户端链接获取服务列表信息.同时参与投票.

2.2.3 启动失败分析

  1. 保证myid文件在每个data目录下,里面的数值一定要与每一个zoo.cfg配置的的server编号一致
  2. 若文件创建没问题,编写没有错误,那就查看端口是否占用,
	netstat -nltp | grep 2181

若确实有占用,使用命令杀掉进程
在这里插入图片描述

kill -9 8258

2.3 Dubbo 入门案例

创建父级项目,创建一个空的maven项目,把src目录删除
在这里插入图片描述
编辑pom.xml文件,打包形式为pom格式

  	<packaging>pom</packaging>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.3.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<!--springBoot动态的引入springMVC全部的配置 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<!--引入测试类 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

		<!--添加属性注入依赖 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</artifactId>
			<optional>true</optional>
		</dependency>

		<!--支持热部署 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>springloaded</artifactId>
			<version>1.2.8.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
		</dependency>

		<!--引入插件lombok 自动的set/get/构造方法插件 -->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>

		<!--引入数据库驱动 -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>

		<!--引入druid数据源 -->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
			<version>1.1.12</version>
		</dependency>

		<!--spring整合mybatis-plus -->
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-boot-starter</artifactId>
			<version>3.0.6</version>
		</dependency>

		<!--spring整合redis -->
		<dependency>
			<groupId>redis.clients</groupId>
			<artifactId>jedis</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-redis</artifactId>
		</dependency>
		<!--添加httpClient jar包 -->
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpclient</artifactId>
		</dependency>
		
		<!--引入dubbo配置 -->
		<dependency>
			<groupId>com.alibaba.boot</groupId>
			<artifactId>dubbo-spring-boot-starter</artifactId>
			<version>0.2.0</version>
		</dependency>	
	</dependencies>

创建接口项目dubbo-interface
在父级项目上创建module项目,我们选择spring boot项目
在这里插入图片描述

在这里插入图片描述修改pom文件,将父级依赖改为它自己父级的信息,同时在父级的pom文件中添加module
在这里插入图片描述在这里插入图片描述
同样的操作创建提供者dubbo-provider, 创建消费者dubbo-consumer
在这里插入图片描述说明:由于消费者和提供者都需要接口文件的支持,所以需要依赖接口项目.
在这里插入图片描述配置接口项目
创建User POJO对象

@Data
@Accessors(chain=true)
@TableName("tb_user")
//dubbo框架中对象的传递必须序列化
public class User implements Serializable{
	private static final long serialVersionUID = 1L;
		/**
	 * 用户Id号  主键自增
	 */
	@TableId(type = IdType.AUTO)
	private Long id;
	/**
	 * 用户名
	 */
	private String username;
	/**
	 * 密码
	 */
	private String password;
	/**
	 * 电话
	 */
	private String phone;
	/**
	 * 邮箱
	 */
	private String email;
	/**
	 * 创建时间
	 */
	private Date created;
	/**
	 * 更新时间
	 */
	private Date updated;
}

在这里插入图片描述创建service接口

public interface UserService {
    List<User> findAll();
    @Transactional
    void saveUser(User user);
}

在这里插入图片描述
服务的提供者我们需要在dubbo-provider中完成
在这里插入图片描述UserServceImpl代码:

@Service(timeout=3000)	//3秒超时	
public class UserServiceImpl implements UserService {
	@Autowired
	private UserMapper userMapper;
	@Override
	public List<User> findAll() {
		
		System.out.println("我是第一个服务的提供者");
		return userMapper.selectList(null);
	}
	@Override
	public void saveUser(User user) {
		
		userMapper.insert(user);
	}
}

UserMapper

@Mapper
public interface UserMapper extends BaseMapper<User>{

}

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ly.mapper.UserMapper">

</mapper>

接下来配置application.yml文件

server:
  port: 9000

spring:
  datasource:
    #引入druid数据源
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/jtdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
    username: root
    password: root

#spring整合dubbo   
dubbo:
  scan:
    basePackages: com.ly    #开启包扫描
  application:
    name: provider-user     #必须添加服务名称
  registry:                 #链接注册中心
    address: zookeeper://192.168.180.160:2181
  protocol:
    name: dubbo        
    #注册中心信息存储文件夹的名称
    port: 20880        
    #每个服务都有自己独立的端口号
    #注意事项:服务的提供者可以有多个,但是必须服务名称必须一致.在服务内部.为了区分到底是哪个服务使用端口区分.
      
mybatis-plus:
  type-aliases-package: com.ly.pojo       #配置别名包路径
  mapper-locations: classpath:/mybatis/mappers/*.xml  #添加mapper映射文件
  configuration:
    map-underscore-to-camel-case: true                #开启驼峰映射规则

配制服务消费者
在这里插入图片描述代码:

@RestController
public class UserController {
	/**
	 * check:true 检查是否有服务的提供者
	 * 先启动服务提供者,之后启动消费者
	 */
	@Reference(timeout=3000,check=true,loadbalance = "leastactive")
	private UserService userService;
	@RequestMapping("/findAll")
	public List<User> findAll(){
		return userService.findAll();
	}
	@RequestMapping("/saveUser/{name}/{age}/{sex}")
	public String saveUser(User user) {
		userService.saveUser(user);
		return "用户入库成功!!!";
	}
}

配置yaml文件
在这里插入图片描述
代码

server:
  port: 9001
dubbo:
  scan:
    basePackages: com.ly
  application:
    name: consumer-user
  registry:
    address: zookeeper://192.168.180.160:2181

由于消费者不需要连接数据库,我们需要在启动类加注解,使其忽略加载数据库连接
在这里插入图片描述
OK,我们实验一下。看一下消费者是否可以拿到数据
先 启动服务提供者,最后启动服务消费者。
在这里插入图片描述
可以看到数据已经拿到。

2.3.1 Dubbo链接异常

检查zookeeper启动正常
在这里插入图片描述
防火墙没有关闭
说明:如下报错表示服务器链接超时.

2020-05-04 10:58:33.725 ERROR 4984 --- [  restartedMain] org.apache.curator.ConnectionState       : Connection timed out for connection string (192.168.180.160:2181) and timeout (5000) / elapsed (5032)

需要关闭防火墙

service iptables stop

2.3.2 Dubbo高可用测试

测试负载均衡效果
说明:当启动多个服务的提供者时发现,dubbo框架自己实现了负载均衡的算法.

高可用测试:

  1. 当关闭服务提供者发现用户访问不受限制.
  2. 如果zookeeper宕机用户能否正常使用?
    可以,以为消费者在启动时已经将注册列表信息保存到本地(消费者内存中).所以访问依然可以正常运行.但是现在的运行处于高危状态.

负载均衡算法
在这里插入图片描述

  1. 随机算法 RandomLoadBalance
@Reference(timeout=3000,check=true,loadbalance = "random")
  1. 轮询算法 RoundRobinLoadBalance
@Reference(timeout=3000,check=true,loadbalance = "roundrobin")
  1. IPhash算法 ConsistentHashLoadBalance
@Reference(timeout=3000,check=true,loadbalance = "consistentHash")
  1. LeastActive LeastActiveLoadBalance
    挑选服务器负载小的进行访问.
@Reference(timeout=3000,check=true,loadbalance = "leastactive")

github地址::https://github.com/lmy1965673628/dubbo-demo.git

3.实现单点登录

3.1 注册功能

业务需求

  1. dubbo实现用户信息注册.当用户点击注册按钮时,将用户的数据利用消费者接收数据.之后利用dubbo框架实现RPC调用将数据发送给服务的提供者jt-sso.
  2. jt-sso接收用户请求之后完成入库操作.

添加dubbo的jar包

<!--引入dubbo配置 -->
<dependency>
	<groupId>com.alibaba.boot</groupId>
	<artifactId>dubbo-spring-boot-starter</artifactId>
	<version>0.2.0</version>
</dependency>

配置服务接口:在jt-common中创建:

public interface DubboUserService {

	void insertUser(User user);
	//实现数据库查询
	String doLogin(User user);
}

在这里插入图片描述

配制服务提供者
在jt-sso的模块中修改yml文件


#spring整合dubbo
dubbo:
  scan:
    basePackages: com.ly    #开启包扫描
  application:
    name: provider-user     #必须添加服务名称
  registry:                 #链接注册中心
    address: zookeeper://192.168.180.160:2181?backup=192.168.180.160:2182,192.168.180.160:2183
  protocol:
    name: dubbo
    #注册中心信息存储文件夹的名称
    port: 20880
    #每个服务都有自己独立的端口号
    #注意事项:服务的提供者可以有多个,但是必须服务名称必须一致.在服务内部.为了区分到底是哪个服务使用端口区分.

在这里插入图片描述 添加dubbo实现类
在这里插入图片描述
登录时检验用户名是否正确,并将其放在redis缓存中。key值是随机生成串

@Service(timeout = 3000) //将对象交给dubbo管理
public class DubboUserServiceImpl implements DubboUserService {

	@Autowired(required = false)
	private JedisCluster jedisCluster;
	
	@Autowired
	private UserMapper userMapper;

	@Override
	public void insertUser(User user) {
		//密码加密  注意加密和登录算法必须相同
		String md5Pass = 
		DigestUtils.md5DigestAsHex(user.getPassword().getBytes());
		user.setPassword(md5Pass)
			.setCreated(new Date())
			.setUpdated(user.getCreated());
		userMapper.insert(user);
	}

	@Override
	public String doLogin(User user) {
		String md5Pass = DigestUtils.md5DigestAsHex(user.getPassword().getBytes());
		user.setPassword(md5Pass);
		QueryWrapper<User> queryWrapper = new QueryWrapper<User>(user);
		User userDB = userMapper.selectOne(queryWrapper);
		String key = null;
		if(userDB!=null) {
			//表示用户名密码正确    UUID
			key = DigestUtils.md5DigestAsHex(UUID.randomUUID().toString().getBytes());
			String userJSON = JsonUtil.toJSON(userDB);
			jedisCluster.setex(key,7*24*3600, userJSON);
		}
		return key;
	}
}

编辑服务消费者
配置jt-web的yml文件

#添加dubbo配制
dubbo:
  scan:
    basePackages: com.ly
  application:
    name: consumer-web
  registry:
    address: zookeeper://192.168.180.160:2181

在这里插入图片描述
我们先看一下页面
在这里插入图片描述在这里插入图片描述

去看一下源码
在这里插入图片描述
OK,编辑消费者Controller,即jt-web中的controller

@Controller
@RequestMapping("/user")
public class UserController {
    @Reference(timeout = 3000,check = true)
    private DubboUserService userService;

    /**
     * 实现用户页面跳转
     * http://www.jt.com/user/register.html
     * http://www.jt.com/user/login.html
     */
    @RequestMapping("/{moduleName}")
    public String login(@PathVariable String moduleName) {
        return "/"+moduleName;
    }
    /**
     * 实现用户信息新增
     */
    @RequestMapping("/doRegister")
    @ResponseBody
    public SysResult insertUser(User user) {
        userService.insertUser(user);
        return SysResult.success();
    }
}

修改服务提供者的Service

@Service(timeout = 3000) //将对象交给dubbo管理
public class DubboUserServiceImpl implements DubboUserService {
	
	@Autowired
	private UserMapper userMapper;

	@Override
	public void insertUser(User user) {
		//密码加密  注意加密和登录算法必须相同
		String md5Pass = 
		DigestUtils.md5DigestAsHex(user.getPassword().getBytes());
		user.setPassword(md5Pass)
			.setCreated(new Date())
			.setUpdated(user.getCreated());
		userMapper.insert(user);
	}
}

OK。我们测试一下,看是否能注册成功
在这里插入图片描述看一下数据库
在这里插入图片描述

3.2 单点登录实现方案

3.2.1 SSO介绍

单点登录(SingleSignOn,SSO),就是通过用户的一次性鉴别登录。当用户在身份认证服务器上登录一次以后,即可获得访问单点登录系统中其他联邦系统和应用软件的权限,同时这种实现是不需要管理员对用户的登录状态或其他信息进行修改的,这意味着在多个应用系统中,用户只需一次登录就可以访问所有相互信任的应用系统。这种方式减少了由登录产生的时间消耗,辅助了用户管理,是目前比较流行的
概括:用户只需要登录一次,就可以访问相互信任的系统

企业级单点登录方案
在这里插入图片描述
步骤:

  1. 当用户输入username和password进行登录时.将请求发送给消费者jt-web
  2. jt-web接收用户请求之后,将数据username/password远程调用JT-sso完成校验.(结果正确/用户名和密码错误)
  3. 提供者jt-sso接收用户的请求参数查询后台数据库.
    当查询结果为null时,表示用户输入的信息有误.
    当查询结果不为null.首先生成加密秘钥ticket(token),之后将user信息转化为json串. 之后将ticket当做key,json当做value保存到redis中7天有效.最后将TICKET数据返回给消费者.
    4.jt-web接收到提供者返回数据进行检验.之后将数据一起保存到客户端浏览器的Cookie中.

3.3 页面分析

查看请求路径
在这里插入图片描述
源码查看
在这里插入图片描述
编辑UserController(jt-web)

  @RequestMapping("/doLogin")
    @ResponseBody
    public SysResult doLogin(User user, HttpServletResponse response) {
        //1.获取服务器加密秘钥
        String ticket = userService.doLogin(user);
        if(StringUtils.isEmpty(ticket)) {
            return SysResult.fail();
        }
        //2.需要将数据保存到cookie中
        Cookie cookie = new Cookie("JT_TICKET", ticket);
        cookie.setMaxAge(7*24*3600);
        //设定cookie的使用权限.
        cookie.setPath("/");
        //设定cookie共享!!!!
        cookie.setDomain("jt.com");
        //将cookie写入浏览器
        response.addCookie(cookie);
        return SysResult.success();
    }

编辑DubboUserServiceImpl(jt-sso),也可以说编辑服务提供者

@Override
	public String doLogin(User user) {
		String md5Pass = DigestUtils.md5DigestAsHex(user.getPassword().getBytes());
		user.setPassword(md5Pass);
		QueryWrapper<User> queryWrapper = new QueryWrapper<User>(user);
		User userDB = userMapper.selectOne(queryWrapper);
		String key = null;
		if(userDB!=null) {
			//表示用户名密码正确    UUID
			key = DigestUtils.md5DigestAsHex(UUID.randomUUID().toString().getBytes());
			String userJSON = JsonUtil.toJSON(userDB);
			jedisCluster.setex(key,7*24*3600, userJSON);
		}
		return key;
	}

查看效果
输入正确的用户名和密码后,点击登录就会跳转到首页,我们看一下cookie,已经拿到值
在这里插入图片描述 用户信息回显
登陆成功后,头部并没有显示登陆信息,我们去找一下是否有请求链接,发现一个
在这里插入图片描述查看源码,发现是个跨域请求
在这里插入图片描述 编辑JT-SSO Controller

 /**
     * 实现用户信息查询
     * /user/query/" + _ticket
     */
    @RequestMapping("/query/{ticket}")
    public JSONPObject findUserByTicket(@PathVariable String ticket,String callback) {

        String userJSON = jedisCluster.get(ticket);
        JSONPObject jsonpObject = null;
        if(StringUtils.isEmpty(userJSON)) {
            jsonpObject = new JSONPObject(callback,SysResult.fail());
        }else {
            jsonpObject = new JSONPObject(callback,SysResult.success().setData(userJSON));
        }
        return jsonpObject;
    }

查看效果:
在这里插入图片描述

3.3 用户退出

当用户点击退出按钮时,首先应该删除Cookie信息.之后删除Redis信息.将页面重定向到系统首页.

在这里插入图片描述
编辑Controller

@RequestMapping("/logout")
    public String logout(HttpServletRequest request, HttpServletResponse response) {
        //1.获取cookie数据
        Cookie[] cookies = request.getCookies();
        //2.判断cookie是否有值
        String ticket = null;
        if(cookies.length>0) {
            for (Cookie cookie : cookies) {
                if("JT_TICKET".equals(cookie.getName())) {
                    ticket = cookie.getValue();
                    break;
                }
            }
        }

        //3.删除redis
        if(!StringUtils.isEmpty(ticket)) {
            jedisCluster.del(ticket);
        }

        /**
         * 4.删除cookie
         * setMaxAge
         * 		>0  表示设定超时时间
         * 		=0  表示立即删除cookie
         * 		-1 当会话关闭后,删除cookie
         */
        Cookie cookie = new Cookie("JT_TICKET","");
        cookie.setMaxAge(0); //删除cookie
        cookie.setPath("/");
        cookie.setDomain("jt.com");
        response.addCookie(cookie);
        //重定向到系统首页
        return "redirect:/";
    }

重启服务,再次点击退出按钮,查看效果,可以看到cookie也删除,也可以看到redis缓存也删除
在这里插入图片描述

github

总结

本节通过是用来Dubbo框架实现了单点登录操作,以及实现了js跨域解决其他页面请求。下一节我们继续讲解购物车的实现,使用拦截器实现权限控制,以及如何实现同线程内数据共享

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值