Redis+SpringBoot 사례 분석
1. 프로젝트 환경
프런트엔드 기술 스택: Vue-Cli
프런트엔드 소프트웨어: WebStorm 2020.3
프런트엔드 스타일: Bootstrap
백엔드 기술 스택: SpringBoot
백엔드 소프트웨어: IntelliJ IEDA2019
JavaJDK: 1.8
서버: Alibaba Cloud Centos 7
-
기타: MyBatis, Redis, MySql, Docker, Shiro
2. 프로젝트 시연
ShoppingProject01_Pub : Project05; Bad Person_Axios; 사용자가 이메일을 통해 등록하고 제출을 클릭하면 웹사이트에서 사용자에게 활성화 코드 링크가 포함된 이메일을 보냅니다. 사용자는 링크를 클릭하여 계정 활성화 포털을 실현합니다.- 2) SMS 등록 및 로그인:
- 사용자가 자신의 휴대폰 번호로 등록할 때 "인증 코드 받기" 버튼을 클릭하면 웹사이트에서 보낸 인증 코드가 포함된 문자 메시지가 휴대폰으로 전송됩니다. Redis 기반으로 인증코드는 5분 동안 유효하며, 각 휴대폰 번호는 SMS 인증코드 포털을 3번만 받을 수 있습니다.
3) Alipay 결제:
Android 버전의 Alipay 샌드박스 앱을 다운로드하면 사용자는 QR 코드를 스캔하여 alipay를 통해 웹사이트에서 상품을 구매할 수 있습니다. 배경 MySql은 주문 행동 포털을 기록하고 웹페이지는 다음과 같이 표시됩니다. 그림 1에 나와 있습니다. - 그림 1 제품 표시 페이지
4) 사용자 분류:
사용자가 연간 VIP 멤버십을 구매하기 위해 QR 코드를 스캔하면 웹 사이트의 제품이 절반 가격으로 구매되고 배경 MySql 기록 사용자의 역할이 변경됩니다.
5) 사용자 포인트 순위 목록:
상품을 구매하는 사용자는 포인트가 증가합니다. 웹페이지는 그림 2와 같습니다.그림 2 순위 표시 페이지
2) 프로젝트가 서버에 배포된 후에는 서버의 Redis에 연결할 수 없습니다. 해결 방법: (1) Docker 대신 Redis를 서버에 배포합니다. (2) Redis 포트를 7000으로 변경합니다. (3) 활성 방화벽 상태에서 서버 및 Alibaba Cloud의 7000 포트를 해제합니다. conf 파일.
3) git이 로컬 소스 코드를 gitee에 업로드했을 때 잘못된 조작으로 인해 gitee의 이전 코드가 로컬 소스 코드를 덮어쓰게 되었는데, 이는 다음날 발견되었습니다. 해결책: 소스 코드 jar 패키지가 서버에 남아 있기 때문에 디컴파일 도구 jd_gui를 사용하면 수명을 절반으로 줄일 수 있습니다. 또한 git 업로드 파일은 포털을 참조합니다. 3. 메인 모듈 설명
-
1.1 Vue-Cli 개요:
(2) Vue-Cli는 webpack을 기반으로 구축되었으며 합리적인 기본 구성을 제공합니다. 패키징 도구 webpack은 단일 페이지와 다양한 개발 구성 요소를 집계할 수 있습니다.
1) 프론트엔드와 백엔드 분리 및 단일 페이지 웹 애플리케이션이 특징( SPA), Vue-Cli는 스캐폴딩 사양이 있는 Vue 프로젝트를 생성할 수 있습니다. Vue-Cli의 장점은 다음과 같습니다.
(1) 스캐폴딩 사양을 기반으로 한 개발이 매우 유연해집니다. (3) Vue-Cli는 프런트 엔드 생태계에서 최고의 도구를 물려받은 풍부한 공식 플러그인 컬렉션입니다.
2) 설치 프로세스:
(1) WebStorm 설치(개발용), node.js 설치, vue-cli 설치, axios 설치(도메인 간 요청 시작용) 및 부트스트랩 스타일 도입.3) 배포 프로세스:
npm run build # 在WebStorm终端执行,生成dist文件夹 docker pull nginx:1.19.10 # 不建议Vue-cli项目部署到tomcat,因为tomcat属于动态服务器,启动需要java环境,是为了解析动态语言jsp的;像纯静态的就部署到静态服务器nginx上。 mkdir html # 为了做docker容器内外的数据卷映射 mv dist/ html/ docker run -p 80:80 --name nginx01 -d -v /root/html/dist/:/usr/share/nginx/html nginx:1.19.10 # 数据卷映射 # 此时可访问 http://120.79.133.235:80/index.html
(1) WebStorm에서는 그림 3과 같이 개발 프로세스가 주로 src 파일을 중심으로 이루어집니다.
그림 3 WebStorm 디렉터리
[1] 먼저 라우팅(라우터) 및 구성 요소(구성 요소[공용 구성 요소], 보기[비공개 구성 요소])를 마스터합니다. 구성 요소가 생성된 후에는 경로에 등록해야 합니다. ] 어설션은 부트스트랩 스타일을 캡슐화하고 main.js에서 가져옵니다. [3] 도메인 간 요청을 보내기 위해 axios 인스턴스는 utils에 캡슐화되며 코드는 다음과 같습니다.
import axios from 'axios' // 创建默认实例 const instance = axios.create({ baseURL: 'http://120.79.133.235:8989/eb', // timeout: 10000, }); // 请求拦截器 instance.interceptors.request.use(config=>{ console.log("请求拦截器"); return config; }) // 响应拦截器 instance.interceptors.response.use(response=>{ console.log("响应拦截器"); return response; }, err=>{ console.log("响应出现错误时进入的拦截器"); }); // 暴露instance实例对象 export default instance;
// Get请求 // 向后端接口发当前页码,获取当前页面的商品List instance.get("/item/findAllItem?page="+this.page).then(res=>{ that.items = res.data.items; that.totalPage = res.data.totalPage; that.page = res.data.page; }); // Post请求 // 向后端接口发送当前商品id和用户id,获取商品购买状态 instance.post("/order/alipay/callback",{itemId:this.itemid,userId:this.user.id}).then(res=>{ if ( res.data.code == 20000 ) { alert("提示:您已购买该商品"); } else { alert("提示:您未购买该商品"); } }); }
// 跳转到MailReg组件 this.$router.push({name:"MailReg"}); // 跳转到item组件,并传递当前商品的id this.$router.push({path:"/item",query:{ItemId:myid}}); // item组件接收方法: this.itemid = this.$route.query.ItemId; // 另外不同组件可以依据token获取登录用户信息,需要用到redis,详见下文
2 用户积分排行榜模块说明:
1.1 Reids概述:
1) Redis是一种基于内存的数据存储NoSql;
2) Redis支持丰富的数据类型(String, List, Set, ZSet, Hash);
3) Redis有两种持久化方法: (1)快照(snapshot)存储,也叫rdb持久化,保存当前时刻的数据状态;(2) AOF(append only file)存储,将所有redis的写命令记录到日志文件中,Redis支持持久化间隔最快也是一秒,所以它是事务不安全的,即是可能丢失数据的。
4)Redis的应用场景:
(1) 利用Redis字符串完成项目中手机验证码存储的实现。------本项目采用
(2) 利用Redis字符串类型完成具有时效性的业务功能,如订单还有40分钟即将关闭。
(3) 利用Redis实现分布式集群系统中的Session共享。
(4) 利用Redis的ZSet数据类型(可排序set类型:元素+分数)实现排行榜功能。 ------本项目采用
(5) 利用Redis完成分布式缓存。 ------本项目实现MySql中数据的缓存
(6) 利用Redis存储认证之后的token信息。 ------非常方便,本项目采用。
(7) 利用Redis解决分布式集群系统中分布式锁问题。
1.2 基于Redis实现前端组件从后端获取用户信息:
Step1:前端Login.vue组件中用户输入登录信息提交的接口如下:
// 这里调用了后端/user/login接口,获取当前登录用户的token,存入Session的localStorage中,在后续网页浏览过程中可随时调取这个token instance.post("/user/login",this.user).then(res=>{ if ( res.data.state ) { alert(res.data.msg+",点击确定进入主页"); // 前端存储token信息 localStorage.setItem("token",res.data.token); that.$router.push({path:"/itemList"}); } else { alert(res.data.msg); that.user = {}; } });
Step2:后端/user/login接口实现如下:
// Controller层 @PostMapping("login") public Map<String, Object> loginAccount(@RequestBody User user, HttpSession session) { return userService.loginAccount(user, session); } // Service层 // 情况3:查询到一个用户时 // 获取主体对象 try { Subject subject = SecurityUtils.getSubject(); subject.login(new UsernamePasswordToken(user.getName(), user.getPassword())); User userDB = userListDB.get(0); String token = session.getId(); if (userDB.getScore() == null) { userDB.setScore(0.0); userDAO.updateUserScore(userDB); } redisTemplate.opsForValue().set("TOKEN_" + token, userDB, 30, TimeUnit.MINUTES); redisTemplate.opsForZSet().add("userRank", userDB, userDB.getScore()); map.put("token", token); map.put("state",true); map.put("msg","登录成功"); return map; ...
Redis整合SpringBoot有两种Template,即RedisTemplate和StringRedisTemplate。其中StringRedisTemplate是RedisTemplate的子类,两个方法基本一致,不同之处在于操作的数据类型不同,RedisTemplate中的两个泛型都是Object,意味着存储的key和value都可以是一个对象,而StringRedisTemplate的两个泛型都是String,意味着StringRedisTemplate的key和value都只能是字符串。
在Step2中,我将token和数据库中的用户信息userDB绑定在一起存入了Redis中,后续前端组件获取登录用户信息的代码如下:
// 从localStorage获取token let token = localStorage.getItem("token"); let that = this; // 发送axios请求,根据token获取用户信息 instance.get("/user/token?token="+token).then(res=>{ that.user = res.data; console.log(that.user); })
后端/user/token的接口如下:
@GetMapping({"token"}) public User findUser(String token) { System.out.println("接收的token信息:" + token); return (User)redisTemplate.opsForValue().get("TOKEN_" + token); }
Step3:用户退出登录时,应消除浏览器中对应的token,后端接口代码如下:
// 退出登录 @DeleteMapping({"logout"}) public Map<String, Object> logout(String token) { Map<String, Object> map = new HashMap<>(); try { redisTemplate.delete("TOKEN_" + token); Subject subject = SecurityUtils.getSubject(); subject.logout(); map.put("state", true); map.put("msg", "提示:退出账户成功"); return map; } catch (Exception e) { e.printStackTrace(); map.put("state", false); map.put("msg", "提示:退出账户失败"); return map; } }
1.3 基于Redis的用户积分排行榜实现:
MySql中的用户信息如图4所示:
Redis中的UserRank如图5所示:
Step1:当用户登录时,他的首要任务是接入UserRank对应的信息,后端代码如下:
if (userDB.getScore() == null) { userDB.setScore(0.0); userDAO.updateUserScore(userDB); } redisTemplate.opsForValue().set("TOKEN_" + token, userDB, 30, TimeUnit.MINUTES); redisTemplate.opsForZSet().add("userRank", userDB, userDB.getScore());
userDB是数据库中当前登录用户的信息(一定是有的,你注册了,对吧?),若用户首次登录我将他的分数在数据库设置为0.0,之后我在Redis的ZSet中加入这个用户,你知道,Set集合不会存储重复key值的元素,因此不会同一个用户出现在UserRank中两次。两个template完成了token绑定User,User绑定UserRank中Score的过程,之后的分数更新过程会反复使用这两个template实现。
Step2:当用户信息更新时,相应的与用户信息有关的两个template都要发生变化,代码如下:
// key值序列化 redisTemplate.setKeySerializer(new StringRedisSerializer()); // 由当前用户的token获取当前用户的信息 User firstUser = (User)redisTemplate.opsForValue().get("TOKEN_" + token); // 删除zSet中的当前用户 redisTemplate.opsForZSet().remove("userRank", firstUser); // 产生新的当前用户(昵称改变) List<User> userListDB = this.userDAO.selectUserByName(user.getName()); User secondUser = userListDB.get(0); // 更新token中当前用户的信息 redisTemplate.opsForValue().set("TOKEN_" + token, secondUser, 30, TimeUnit.MINUTES); // 产生zSet中的当前用户 redisTemplate.opsForZSet().add("userRank", secondUser, secondUser.getScore());
Step3:当用户扫码支付时,首次进入的后端controller如下:
// 支付单件商品 @GetMapping("payForItem") public byte[] alipay(String itemid,String userid, String token) { this.token = token; log.info("itemid=====>"+itemid); log.info("userid=====>"+userid); PayVo payVo = new PayVo(); payVo.setItemId(itemid); payVo.setUserId(userid); System.out.println(payVo.getUserId()); return alipayService.alipay(payVo); }
在alipayService有一个小型用户分级,即vip用户购物价格减半:
// 1:支付的用户 String userId = payVo.getUserId(); // my 1: 依据用户id查询用户 User user = userService.selectUserById(Integer.valueOf(userId)); // 依据商品id查询商品 Item item = itemService.getItemById(payVo.getItemId()); // my 1: 依据用户id查询用户 if ( item == null ) return null; // 2: 支付金额 String tempMoney = item.getPrice().toString(); String money = ""; if ( user.getRole().equals("normal") ) { money = tempMoney; } if ( user.getRole().equals("vip") ) { Double tempMoney2 = Double.valueOf(tempMoney)*0.5; money = String.valueOf(tempMoney2); }
在payForItem相同文件下,调用了payCommonService,在这里会实现用户积分更新和用户等级更新:
payCommonService.payUserPublic(bodyJsonObject, userId, user.getName(), orderNumber, tradeno, "alipay", this.token);
将"VIP"这件商品的id设置为“666”,当用户购买该商品时,当前用户更新过程如下:
if ( itemId.equals("666") ) { int myuserId = Integer.valueOf(userId); User userDB = userService.selectUserById(myuserId); // key值序列化 this.redisTemplate.setKeySerializer(new StringRedisSerializer()); // 由当前token获取当前用户信息 User firstUser = (User)redisTemplate.opsForValue().get("TOKEN_" + token); // 由当前用户信息删除当前用户zSet redisTemplate.opsForZSet().remove("userRank", firstUser); // 更新当前用户信息身份 userDB.setRole("vip"); // 更新当前用户新身份的分数 userService.updateUserRole(userDB); List<User> userListDB = this.userDAO.selectUserByName(userDB.getName()); // 获取当前新身份用户的完整信息 User secondUser = userListDB.get(0); // 更新当前token对应的当前用户 redisTemplate.opsForValue().set("TOKEN_" + token, secondUser, 30, TimeUnit.MINUTES); // 设置当前用户的zSet redisTemplate.opsForZSet().add("userRank", secondUser, secondUser.getScore().doubleValue()); }
当前用户积分更新过程如下:
// 更新当前用户的积分 double tempScore = Double.valueOf(orderDetail.getPrice()) * 0.3; String key1 = "TOKEN_" + token; // 由当前token获取当前用户 User user = (User)redisTemplate.opsForValue().get(key1); // 更新当前用户的zSet分数 redisTemplate.opsForZSet().incrementScore("userRank", user, tempScore); // 获取当前用户的zSet分数 double newScore = redisTemplate.opsForZSet().score("userRank", user); // 删除当前用户的zSet(因为要更新当前用户的信息,将当前用户在数据库中的分数进行同步) redisTemplate.opsForZSet().remove("userRank", new Object[] { user }); user.setScore(newScore); userDAO.updateUserScore(user); // 更新token对应的当前用户的信息 redisTemplate.opsForValue().set(key1, user); // 新增当前用户的zSet redisTemplate.opsForZSet().add("userRank", user, newScore);
위 내용은 Redis+SpringBoot 사례 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











Redis Cluster Mode는 Sharding을 통해 Redis 인스턴스를 여러 서버에 배포하여 확장 성 및 가용성을 향상시킵니다. 시공 단계는 다음과 같습니다. 포트가 다른 홀수 redis 인스턴스를 만듭니다. 3 개의 센티넬 인스턴스를 만들고, Redis 인스턴스 및 장애 조치를 모니터링합니다. Sentinel 구성 파일 구성, Redis 인스턴스 정보 및 장애 조치 설정 모니터링 추가; Redis 인스턴스 구성 파일 구성, 클러스터 모드 활성화 및 클러스터 정보 파일 경로를 지정합니다. 각 redis 인스턴스의 정보를 포함하는 Nodes.conf 파일을 작성합니다. 클러스터를 시작하고 Create 명령을 실행하여 클러스터를 작성하고 복제본 수를 지정하십시오. 클러스터에 로그인하여 클러스터 정보 명령을 실행하여 클러스터 상태를 확인하십시오. 만들다

Redis 지시 사항을 사용하려면 다음 단계가 필요합니다. Redis 클라이언트를 엽니 다. 명령 (동사 키 값)을 입력하십시오. 필요한 매개 변수를 제공합니다 (명령어마다 다름). 명령을 실행하려면 Enter를 누르십시오. Redis는 작업 결과를 나타내는 응답을 반환합니다 (일반적으로 OK 또는 -err).

Redis 데이터를 지우는 방법 : Flushall 명령을 사용하여 모든 키 값을 지우십시오. FlushDB 명령을 사용하여 현재 선택한 데이터베이스의 키 값을 지우십시오. 선택을 사용하여 데이터베이스를 전환 한 다음 FlushDB를 사용하여 여러 데이터베이스를 지우십시오. del 명령을 사용하여 특정 키를 삭제하십시오. Redis-Cli 도구를 사용하여 데이터를 지우십시오.

Redis는 단일 스레드 아키텍처를 사용하여 고성능, 단순성 및 일관성을 제공합니다. 동시성을 향상시키기 위해 I/O 멀티플렉싱, 이벤트 루프, 비 블로킹 I/O 및 공유 메모리를 사용하지만 동시성 제한 제한, 단일 고장 지점 및 쓰기 집약적 인 워크로드에 부적합한 제한이 있습니다.

Redis 소스 코드를 이해하는 가장 좋은 방법은 단계별로 이동하는 것입니다. Redis의 기본 사항에 익숙해집니다. 특정 모듈을 선택하거나 시작점으로 기능합니다. 모듈 또는 함수의 진입 점으로 시작하여 코드를 한 줄씩 봅니다. 함수 호출 체인을 통해 코드를 봅니다. Redis가 사용하는 기본 데이터 구조에 익숙해 지십시오. Redis가 사용하는 알고리즘을 식별하십시오.

Redis에서 모든 키를 보려면 세 가지 방법이 있습니다. 키 명령을 사용하여 지정된 패턴과 일치하는 모든 키를 반환하십시오. 스캔 명령을 사용하여 키를 반복하고 키 세트를 반환하십시오. 정보 명령을 사용하여 총 키 수를 얻으십시오.

Redis는 해시 테이블을 사용하여 데이터를 저장하고 문자열, 목록, 해시 테이블, 컬렉션 및 주문한 컬렉션과 같은 데이터 구조를 지원합니다. Redis는 Snapshots (RDB)를 통해 데이터를 유지하고 WRITE 전용 (AOF) 메커니즘을 추가합니다. Redis는 마스터 슬레이브 복제를 사용하여 데이터 가용성을 향상시킵니다. Redis는 단일 스레드 이벤트 루프를 사용하여 연결 및 명령을 처리하여 데이터 원자력과 일관성을 보장합니다. Redis는 키의 만료 시간을 설정하고 게으른 삭제 메커니즘을 사용하여 만료 키를 삭제합니다.

Redis의 대기열을 읽으려면 대기열 이름을 얻고 LPOP 명령을 사용하여 요소를 읽고 빈 큐를 처리해야합니다. 특정 단계는 다음과 같습니다. 대기열 이름 가져 오기 : "큐 :"와 같은 "대기열 : my-queue"의 접두사로 이름을 지정하십시오. LPOP 명령을 사용하십시오. 빈 대기열 처리 : 대기열이 비어 있으면 LPOP이 NIL을 반환하고 요소를 읽기 전에 대기열이 존재하는지 확인할 수 있습니다.
