欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

基于Redis實(shí)現(xiàn)短信驗(yàn)證碼登錄項(xiàng)目示例(附源碼)

 更新時(shí)間:2022年05月19日 15:38:00   作者:扎哇太棗糕  
手機(jī)登錄驗(yàn)證在很多網(wǎng)頁上都得到使用,本文主要介紹了基于Redis實(shí)現(xiàn)短信驗(yàn)證碼登錄項(xiàng)目示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

Redis短信登錄流程描述

短信驗(yàn)證碼的發(fā)送

用戶提交手機(jī)號(hào),系統(tǒng)驗(yàn)證手機(jī)號(hào)是否有效,畢竟無效手機(jī)號(hào)會(huì)消耗你的短信驗(yàn)證次數(shù)還會(huì)導(dǎo)致系統(tǒng)的性能下降。如果手機(jī)號(hào)為無效的話就讓用戶重新提交手機(jī)號(hào),如果有效就生成驗(yàn)證碼并將該驗(yàn)證碼作為value保存到redis中對(duì)應(yīng)的key是手機(jī)號(hào),之所以這么做的原因是保證key的唯一性,如果使用固定字符串作為可以的話會(huì)被后面的數(shù)據(jù)所覆蓋。然后在控制臺(tái)輸出驗(yàn)證碼模擬發(fā)送驗(yàn)證碼的過程

短信驗(yàn)證碼的驗(yàn)證

用戶的手機(jī)號(hào)接收到驗(yàn)證碼后在平臺(tái)上提交驗(yàn)證碼,系統(tǒng)從redis中根據(jù)手機(jī)號(hào)讀取驗(yàn)證碼并進(jìn)行校驗(yàn),如果驗(yàn)證通過的話就根據(jù)用戶驗(yàn)證使用的手機(jī)號(hào)去數(shù)據(jù)庫中進(jìn)行查詢用戶信息。如果存在就將查詢到的用戶信息保存到redis中,完成登錄;如果不存在的話就創(chuàng)建一個(gè)新用戶,并將該用戶的信息分別保存到sql數(shù)據(jù)庫和redis中,生成隨機(jī)token作為key、使用hash結(jié)構(gòu)存儲(chǔ)user數(shù)據(jù)作為value,并將這個(gè)token返回給客戶端,至此完成登錄注冊(cè)

是否登錄的驗(yàn)證

用戶訪問系統(tǒng)業(yè)務(wù)邏輯的時(shí)候需要校驗(yàn)他是否已經(jīng)登錄,如果登錄可以訪問否則就去登錄,那么該如何完成是否登錄的校驗(yàn)?zāi)??這就要了解session的相關(guān)知識(shí)了,每一個(gè)session都有一個(gè)sessionId信息保存在瀏覽器的cookie中,當(dāng)用戶使用瀏覽器發(fā)送請(qǐng)求的時(shí)候會(huì)攜帶上cookie信息,此時(shí)系統(tǒng)就可以使用cookie中的sessionId獲取到session信息,并通過session獲取到登錄時(shí)存儲(chǔ)的用戶信息。如果此時(shí)用戶在數(shù)據(jù)庫中存在的話就將該用戶的信息緩存在ThreadLocal(方便后續(xù)驗(yàn)證)中,并放行該訪問;否則就說明發(fā)送請(qǐng)求的用戶未登錄或不合法,就要攔截到他的請(qǐng)求前往登錄

源碼分析

模擬發(fā)送短信驗(yàn)證碼

UserController定義與前端交互

@Resource
private IUserService userService;

/**
?* 發(fā)送手機(jī)驗(yàn)證碼
?*/
@PostMapping("code")
public Result sendCode(@RequestParam("phone") String phone, HttpSession session) {
? ? // 發(fā)送短信驗(yàn)證碼并保存驗(yàn)證碼
? ? return userService.sendCode(phone, session);
}

上面使用到了sendCode方法,在userService里定義一下接口,然后在對(duì)應(yīng)實(shí)現(xiàn)類中按照上面的流程重寫該方法的業(yè)務(wù)邏輯代碼

@Override
public Result sendCode(String phone, HttpSession session) {
? ? // 校驗(yàn)手機(jī)號(hào)
? ? if (RegexUtils.isPhoneInvalid(phone)) {
? ? ? ? // 無效手機(jī)號(hào),返回錯(cuò)誤信息
? ? ? ? return Result.fail("手機(jī)號(hào)格式有誤!");
? ? }
? ? // 有效生成驗(yàn)證碼
? ? String code = RandomUtil.randomNumbers(6);
? ? // 保存 (固定前綴+手機(jī)號(hào)) 和驗(yàn)證碼到Redis中,設(shè)置驗(yàn)證碼的有效期為2分鐘
? ? // RedisConstants.LOGIN_CODE_KEY = “l(fā)ogin:code:”
? ? // RedisConstants.LOGIN_CODE_TTL = 2L
? ? stringRedisTemplate.opsForValue().set(RedisConstants.LOGIN_CODE_KEY + phone, code, RedisConstants.LOGIN_CODE_TTL, TimeUnit.MINUTES);
? ? // 模擬發(fā)送驗(yàn)證碼
? ? log.debug("驗(yàn)證碼:{}", code);
? ? // 返回
? ? return Result.ok();
}

手機(jī)號(hào)格式校驗(yàn)使用到的RegexUtils類中的工具方法

/**
?* 手機(jī)號(hào)正則
?*/
public static final String PHONE_REGEX = "^1([38][0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|9[89])\\d{8}$";

/**
* 是否是無效手機(jī)格式
?* @param phone 要校驗(yàn)的手機(jī)號(hào)
?* @return true:符合,false:不符合
?*/
public static boolean isPhoneInvalid(String phone){
? ? return mismatch(phone, RegexPatterns.PHONE_REGEX);
}

// 校驗(yàn)是否不符合正則格式
private static boolean mismatch(String str, String regex){
? ? if (StrUtil.isBlank(str)) {
? ? ? ? return true;
? ? }
? ? return !str.matches(regex);
}

短信驗(yàn)證碼的驗(yàn)證

UserController定義與前端交互,其中參數(shù)LoginFormDTO 是前端使用手機(jī)號(hào)+驗(yàn)證碼登錄或者手機(jī)號(hào)+密碼登錄是傳遞過來的JSON數(shù)據(jù)

/**
 * 登錄功能
 * @param loginForm 登錄參數(shù),包含手機(jī)號(hào)、驗(yàn)證碼;或者手機(jī)號(hào)、密碼
 */
@PostMapping("/login")
public Result login(@RequestBody LoginFormDTO loginForm, HttpSession session){
    // 實(shí)現(xiàn)登錄功能
    return userService.login(loginForm, session);
}

上面使用到了login方法,在userService里定義一下接口,然后在對(duì)應(yīng)實(shí)現(xiàn)類中按照上賣弄的流程描述重寫該方法的業(yè)務(wù)邏輯代碼

@Override
public Result login(LoginFormDTO loginForm, HttpSession session) {
? ? String phone = loginForm.getPhone();

? ? // 驗(yàn)證碼校驗(yàn)
? ? String code = loginForm.getCode();
? ? String cacheCode = stringRedisTemplate.opsForValue().get(RedisConstants.LOGIN_CODE_KEY + phone);
? ? if (cacheCode == null || !code.equals(cacheCode)) {
? ? ? ? return Result.fail("驗(yàn)證碼錯(cuò)誤!");
? ? }

? ? // 根據(jù)手機(jī)號(hào)查詢用戶信息
? ? User user = query().eq("phone", phone).one();
? ? if (user == null) {
? ? ? ? // 不存在就創(chuàng)建一個(gè)新用戶
? ? ? ? user = createUserWithPhone(phone);
? ? }

? ? // 保存用戶信息到redis中
? ? // 生成隨機(jī)token
? ? String token = UUID.randomUUID().toString(true);
? ? // user先轉(zhuǎn)userDTO再轉(zhuǎn)hashMap存儲(chǔ) ?轉(zhuǎn)HashMap時(shí)的第三個(gè)參數(shù)的意思是忽略null值將值都轉(zhuǎn)換成String類型
? ? UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
? ? Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),
? ? ? ? ? ? CopyOptions.create()
? ? ? ? ? ? ? ? ? ? .setIgnoreNullValue(true)
? ? ? ? ? ? ? ? ? ? .setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));
? ? // RedisConstants.LOGIN_USER_KEY = "login:token:"
? ? stringRedisTemplate.opsForHash().putAll(RedisConstants.LOGIN_USER_KEY + token, userMap);
? ? // 設(shè)置失效時(shí)間為30分鐘
? ? // RedisConstants.LOGIN_USER_TTL = 30L
? ? stringRedisTemplate.expire(RedisConstants.LOGIN_USER_KEY + token, RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);
? ? // 返回前端token
? ? return Result.ok(token);
}

private User createUserWithPhone(String phone) {
? ? User user = new User();
? ? user.setPhone(phone);
? ? // SystemConstants.USER_NICK_NAME_PREFIX = "user_"
? ? user.setNickName(SystemConstants.USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));
? ? save(user);
? ? return user;
}

保存的時(shí)候使用BeanUtil將User轉(zhuǎn)換成UserDTO進(jìn)行存儲(chǔ),UserDTO的結(jié)構(gòu)如下,只保存一部分的數(shù)據(jù),一方面可以不用來回傳遞用戶有關(guān)的隱私數(shù)據(jù),一方面也節(jié)省內(nèi)存提高性能。由于這里的id是數(shù)值類型,但是stringRedisTemplate存儲(chǔ)時(shí)需要hash的鍵值都是String型,所以說應(yīng)該在存儲(chǔ)之前將id的值轉(zhuǎn)換成String類型,就在上面代碼塊的24~27行完成了這個(gè)操作

@Data
public class UserDTO {
? ? private Long id;
? ? private String nickName;
? ? private String icon;
}

校驗(yàn)是否登錄

用戶發(fā)送請(qǐng)求不止一次,所以說登錄驗(yàn)證也不止進(jìn)行一次,于是可以使用攔截器完成驗(yàn)證,攔截器的使用可分為兩步:

創(chuàng)建攔截器

/**
?* @author : mereign
?* @date : 2022/5/5 - 10:31
?* @desc : 攔截器,實(shí)現(xiàn)請(qǐng)求攔截,判斷登錄信息
?*/
@Component
public class LoginInterceptor implements HandlerInterceptor {

? ? @Autowired
? ? private StringRedisTemplate stringRedisTemplate;

? ? @Override
? ? public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
? ? ? ? // 獲取請(qǐng)求頭中的token信息
? ? ? ? String token = request.getHeader("authorization");
? ? ? ? if (StrUtil.isBlank(token)) {
? ? ? ? ? ? // token為空,返回401未授權(quán)狀態(tài)碼,攔截
? ? ? ? ? ? response.setStatus(401);
? ? ? ? ? ? return false;
? ? ? ? }
? ? ? ? // 根據(jù)token獲取redis中的用戶value
? ? ? ? Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(RedisConstants.LOGIN_USER_KEY + token);
? ? ? ? HttpSession session = request.getSession();

? ? ? ? // 判斷用戶是否存在
? ? ? ? if (userMap.isEmpty()) {
? ? ? ? ? ? // 用戶不存在,返回401未授權(quán)狀態(tài)碼,攔截
? ? ? ? ? ? response.setStatus(401);
? ? ? ? ? ? return false;
? ? ? ? }

? ? ? ? // 用戶存在,將hash數(shù)據(jù)轉(zhuǎn)換為userDTO,存信息到ThreadLocal
? ? ? ? UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
? ? ? ? UserHolder.saveUser(userDTO);

? ? ? ? // 刷新token有效期,放行
? ? ? ? stringRedisTemplate.expire(RedisConstants.LOGIN_USER_KEY + token, RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);
? ? ? ? return true;
? ? }

? ? @Override
? ? public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

? ? }

? ? @Override
? ? public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
? ? ? ? UserHolder.removeUser();
? ? }
}

注冊(cè)攔截器

/**
?* @author : mereign
?* @date : 2022/5/5 - 10:43
?* @desc :
?*/
@Configuration
public class MvcConfig implements WebMvcConfigurer {
? ? @Autowired
? ? private LoginInterceptor loginInterceptor;

? ? @Override
? ? public void addInterceptors(InterceptorRegistry registry) {
? ? ? ? registry.addInterceptor(loginInterceptor)
? ? ? ? ? ? ? ? .excludePathPatterns(
? ? ? ? ? ? ? ? ? ? ? ? "/shop/**",
? ? ? ? ? ? ? ? ? ? ? ? "/shop-type/**",
? ? ? ? ? ? ? ? ? ? ? ? "/voucher/**",
? ? ? ? ? ? ? ? ? ? ? ? "/upload/**",
? ? ? ? ? ? ? ? ? ? ? ? "/blog/hot",
? ? ? ? ? ? ? ? ? ? ? ? "/user/code",
? ? ? ? ? ? ? ? ? ? ? ? "/user/login"
? ? ? ? ? ? ? ? );
? ? }
}

緩存用戶的信息到ThreadLocal中的工具方法

public class UserHolder {
? ? private static final ThreadLocal<UserDTO> tl = new ThreadLocal<>();

? ? public static void saveUser(UserDTO user){
? ? ? ? tl.set(user);
? ? }

? ? public static UserDTO getUser(){
? ? ? ? return tl.get();
? ? }

? ? public static void removeUser(){
? ? ? ? tl.remove();
? ? }
}

UserController定義與前端交互

@GetMapping("/me")
public Result me(){
    // 獲取當(dāng)前登錄的用戶并返回
    UserDTO user = UserHolder.getUser();
    return Result.ok(user);
}

登錄驗(yàn)證優(yōu)化

由上面的登錄驗(yàn)證可知,我們對(duì)一些需要用戶登錄驗(yàn)證的功能設(shè)置了攔截器,如果驗(yàn)證通過會(huì)刷新token的有效期,這樣的話只要用戶一直訪問我們攔截的功能就可以一直保持token是有效的。但是,如果用戶登陸之后的操作一直是不需要驗(yàn)證的,那也就意味著token的有效期一直不會(huì)刷新,這樣的話30分鐘之后token就會(huì)失效用戶驗(yàn)證就會(huì)失敗,這樣顯然是不合理的
于是我們可以使用兩個(gè)攔截器完成,最前面的負(fù)責(zé)攔截所有的請(qǐng)求,獲取token、從redis中查詢用戶,將查詢結(jié)果放到ThreadLocal(可能存null)、刷新token有效期,最后直接放行;后面的攔截器只負(fù)責(zé)判斷有沒有從redis中查詢到用戶,他從ThreadLocal獲取查詢結(jié)果,判斷有則放行無則攔截

創(chuàng)建兩個(gè)攔截器

/**
?* @author : mereign
?* @date : 2022/5/5 - 10:31
?* @desc : 前置攔截器,攔截所有請(qǐng)求,前置工作
?*/
@Component
public class RefreshTokenInterceptor implements HandlerInterceptor {

? ? @Autowired
? ? private StringRedisTemplate stringRedisTemplate;

? ? @Override
? ? public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
? ? ? ? // 獲取請(qǐng)求頭中的token信息
? ? ? ? String token = request.getHeader("authorization");
? ? ? ? if (StrUtil.isBlank(token)) {
? ? ? ? ? ? // token為空 直接放行
? ? ? ? ? ? return true;
? ? ? ? }

? ? ? ? // 根據(jù)token獲取redis中的用戶value
? ? ? ? Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(RedisConstants.LOGIN_USER_KEY + token);
? ? ? ? HttpSession session = request.getSession();
? ? ? ? // 判斷用戶是否存在
? ? ? ? if (userMap.isEmpty()) {
? ? ? ? ? ? // 用戶不存在 直接放行
? ? ? ? ? ? return true;
? ? ? ? }

? ? ? ? // 用戶存在,將hash數(shù)據(jù)轉(zhuǎn)換為userDTO,存信息到ThreadLocal
? ? ? ? UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
? ? ? ? UserHolder.saveUser(userDTO);

? ? ? ? // 刷新token有效期,放行
? ? ? ? stringRedisTemplate.expire(RedisConstants.LOGIN_USER_KEY + token, RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);
? ? ? ? return true;
? ? }

? ? @Override
? ? public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

? ? }

? ? @Override
? ? public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
? ? ? ? UserHolder.removeUser();
? ? }
}
/**
?* @author : mereign
?* @date : 2022/5/5 - 10:31
?* @desc : 登錄攔截器,攔截需要攔截的請(qǐng)求,判斷登錄信息
?*/
@Component
public class LoginInterceptor implements HandlerInterceptor {

? ? @Override
? ? public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
? ? ? ? // 判斷登錄
? ? ? ? if (UserHolder.getUser() == null) {
? ? ? ? ? ? response.setStatus(401);
? ? ? ? ? ? return false;
? ? ? ? }
? ? ? ? return true;
? ? }

? ? @Override
? ? public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

? ? }

? ? @Override
? ? public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

? ? }
}

創(chuàng)建完攔截器之后要將兩個(gè)攔截器通過配置類配置到容器中生效,多個(gè)攔截器的優(yōu)先級(jí),默認(rèn)按照添加順序執(zhí)行優(yōu)先級(jí),但是也可以使用order方法指定優(yōu)先級(jí),按參數(shù)的大小排序優(yōu)先級(jí),參數(shù)越小優(yōu)先級(jí)越高

/**
?* @author : mereign
?* @date : 2022/5/5 - 10:43
?* @desc : 配置類注冊(cè)攔截器
?*/
@Configuration
public class MvcConfig implements WebMvcConfigurer {
? ? @Autowired
? ? private RefreshTokenInterceptor refreshTokenInterceptor;
? ? @Autowired
? ? private LoginInterceptor loginInterceptor;

? ? @Override
? ? public void addInterceptors(InterceptorRegistry registry) {
? ? ? ? // 前置攔截器
? ? ? ? registry.addInterceptor(refreshTokenInterceptor)
? ? ? ? ? ? ? ? .addPathPatterns("/**")
? ? ? ? ? ? ? ? .order(0);
? ? ? ? // 后置攔截器
? ? ? ? registry.addInterceptor(loginInterceptor)
? ? ? ? ? ? ? ? .excludePathPatterns(
? ? ? ? ? ? ? ? ? ? ? ? "/shop/**",
? ? ? ? ? ? ? ? ? ? ? ? "/voucher/**",
? ? ? ? ? ? ? ? ? ? ? ? "/shop-type/**",
? ? ? ? ? ? ? ? ? ? ? ? "/upload/**",
? ? ? ? ? ? ? ? ? ? ? ? "/blog/hot",
? ? ? ? ? ? ? ? ? ? ? ? "/user/code",
? ? ? ? ? ? ? ? ? ? ? ? "/user/login"
? ? ? ? ? ? ? ? )
? ? ? ? ? ? ? ? .order(1);
? ? }
}

到此這篇關(guān)于基于Redis實(shí)現(xiàn)短信驗(yàn)證碼登錄項(xiàng)目示例(附源碼)的文章就介紹到這了,更多相關(guān)Redis 短信驗(yàn)證碼登錄內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Redis 分片集群的實(shí)現(xiàn)

    Redis 分片集群的實(shí)現(xiàn)

    本文主要介紹了Redis 分片集群的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • Redis數(shù)據(jù)庫分布式設(shè)計(jì)方案介紹

    Redis數(shù)據(jù)庫分布式設(shè)計(jì)方案介紹

    大家好,本篇文章主要講的是Redis數(shù)據(jù)庫分布式設(shè)計(jì)方案介紹,感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下
    2022-01-01
  • Redis的數(shù)據(jù)復(fù)制過程詳解

    Redis的數(shù)據(jù)復(fù)制過程詳解

    Redis 的復(fù)制功能分為同步(sync)和命令傳播(command propagate)這兩個(gè)操作,這篇文章主要介紹了Redis的數(shù)據(jù)復(fù)制,需要的朋友可以參考下
    2022-12-12
  • 深入了解Redis連接數(shù)問題的現(xiàn)象和解法

    深入了解Redis連接數(shù)問題的現(xiàn)象和解法

    一般情況?Redis?連接數(shù)問題并不常見,但是當(dāng)你業(yè)務(wù)服務(wù)增加、對(duì)?Redis?的依賴持續(xù)增強(qiáng)的過程中,可能會(huì)遇到很多?Redis?的問題,這個(gè)時(shí)候,Redis?連接數(shù)可能就成了一個(gè)常見的問題,在本章節(jié),希望能夠帶大家了解Redis連接數(shù)問題的現(xiàn)象和解法,需要的朋友可以參考下
    2023-12-12
  • redis連接報(bào)錯(cuò)error:NOAUTH Authentication required

    redis連接報(bào)錯(cuò)error:NOAUTH Authentication required

    本文主要介紹了redis連接報(bào)錯(cuò)error:NOAUTH Authentication required,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-05-05
  • 淺析Redis底層數(shù)據(jù)結(jié)構(gòu)Dict

    淺析Redis底層數(shù)據(jù)結(jié)構(gòu)Dict

    Redis是一個(gè)鍵值型的數(shù)據(jù)庫,我們可以根據(jù)鍵實(shí)現(xiàn)快速的增刪改查,而鍵與值的映射關(guān)系正是通過Dict來實(shí)現(xiàn)的,當(dāng)然?Dict?也是?Set?Hash?的實(shí)現(xiàn)方式,本文就詳細(xì)帶大家介紹一下Redis底層數(shù)據(jù)結(jié)構(gòu)?Dict,,需要的朋友可以參考下
    2023-05-05
  • 談?wù)凴edis分布式鎖的正確實(shí)現(xiàn)方法

    談?wù)凴edis分布式鎖的正確實(shí)現(xiàn)方法

    這篇文章主要給大家介紹了關(guān)于Redis分布式鎖的正確實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Redis具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • Redis 單機(jī)安裝和哨兵模式集群安裝的實(shí)現(xiàn)

    Redis 單機(jī)安裝和哨兵模式集群安裝的實(shí)現(xiàn)

    本文主要介紹了Redis 單機(jī)安裝和哨兵模式集群安裝的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • 將音頻文件轉(zhuǎn)二進(jìn)制分包存儲(chǔ)到Redis的實(shí)現(xiàn)方法(奇淫技巧操作)

    將音頻文件轉(zhuǎn)二進(jìn)制分包存儲(chǔ)到Redis的實(shí)現(xiàn)方法(奇淫技巧操作)

    這篇文章主要介紹了將音頻文件轉(zhuǎn)二進(jìn)制分包存儲(chǔ)到Redis的實(shí)現(xiàn)方法(奇淫技巧操作),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-07-07
  • Redis Cluster的圖文講解

    Redis Cluster的圖文講解

    今天小編就為大家分享一篇關(guān)于Redis Cluster的圖文講解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2019-01-01

最新評(píng)論