Redis 篇- 实战项目中使用 Redis 实现经典功能(异步秒杀商品、点赞功能、共同关注的好友、投喂功能)

news2024/9/24 11:25:31

🔥博客主页: 【小扳_-CSDN博客】
❤感谢大家点赞👍收藏⭐评论✍

文章目录

        1.0 使用 Redis 实现异步秒杀

        1.1 基于 Lua 脚本判断是否符合条件:库存是否充足、一人一单

        1.2 基于 Redis 中的 Stream 实现消息队列

        1.3 使用 Java 操作 Redis 实现消息队列

        2.0 使用 Redis 实现点赞功能

        2.1 使用 Redis 实现点赞排行榜功能

        3.0 使用 Redis 实现好友之间的共同关注(共同好友)

        4.0 使用 Redis 实现投喂(发布文章)

        4.1 使用 Redis 实现收件箱(收邮件)

        4.1.1 实现滚动分页查询(查询朋友圈信息)


        1.0 使用 Redis 实现异步秒杀

        异步秒杀,顾名思义使用不同线程执行不同的任务,在 Redis 实现异步秒杀时,主线程执行操作 Redis 来判断是否符合条件下单,对于操作数据库的任务则交给线程池中的线程来执行。

        而且将下单的 id 等信息放入到消息队列中,再由执行数据库操作的时候再来获取 id 消息,这就不用一直等待数据库完成之后,才能迎接下一个线程进行下单操作了。在下单的时候就不用再考虑是否安全问题了,从而实现一人一单的时候就不需要进行上锁处理了,还有解决超卖问题也不需要乐观锁了。

        这样数据库就可以在适合的时间段根据消息队列中的消息,来将数据存放到数据库中,而从减轻了数据库的压力,效率还很高。

        为了保证在秒杀过程中不会出现线程安全情况,使用 Lua 脚本操作 Redis 命令。

        1.1 基于 Lua 脚本判断是否符合条件:库存是否充足、一人一单

实现思路:

Lua 脚本如下:

local shopId = ARGV[1]
local userId = ARGV[2]

-- 商品key
local shopKey = "shop:"+shopId

-- 用户Key
local orderKey = "order:"+shopId

-- 判断库存是否充足
if(tonumber(redis.call('get',shopKey)) <= 0) then
    -- 库存不足,返回1
    return 1

end

-- 判断是否已经下单过了
if(redis.call('sismember', orderKey,userId) == 1) then
    -- 已经下过单了,返回2
    return 2
end
-- 扣库存
redis.call('incrby',shopKey,-1)
-- 下单保存用户
redis.call('sadd', orderKey,userId)
return 0

        1.2 基于 Redis 中的 Stream 实现消息队列

        使用 Redis 中的 Stream 数据结构,来实现消息队列。

常见的 Redis 命令:

        1)发送消息到消息队列中:

XADD key * field string [field string]

        将指定的键值对发送到具体的队列中

具体代码如下:

命令执行结果:

        2)读取消息队列中的消息:

创建消费者组:

XGROUP CREATE key groupname id|$ MKSTREAM

        ID 为 0 的话,从队列中从 0 开始重新读取任务,而 ID 为 $ 的话,从队列最后一个开始读取任务,抛弃之前队列中的任务。 

具体代码如下:

消费者组读取消息队列:

XREADGROUP GROUP group consumer COUNT count BLOCK milliseconds STREAMS key ID

        消费组从队列中读取消息。若 ID 为 ">" ,则从消息队列最后一个开始,也就是最新的消息开始读取消息;若 ID 为 "0",则从 pending 中读取消息,也就是读取消息了,但是没有进行确认的消息。

确认消息:

XACK key group id consumer

        从消息队列读取出来的消息之后,进行确认,则该消息就不会进入到 pending 状态,否则该消息再没有进行确认下,会来到 pending 状态。

        1.3 使用 Java 操作 Redis 实现消息队列

        1)发布消息:

        可以用 Lua 脚本,在确认完可以运行下单的用户,进行下单,也就是将相关信息放到消息队列中,交由其他线程池来完成读取消息后进行操作数据库。

代码实现:

local shopId = ARGV[1]
local userId = ARGV[2]

-- 商品key
local shopKey = "shop:"+shopId

-- 用户Key
local orderKey = "order:"+shopId
local streamKey = "stream"

-- 判断库存是否充足
if(tonumber(redis.call('get',shopKey)) <= 0) then
    -- 库存不足,返回1
    return 1

end

-- 判断是否已经下单过了
if(redis.call('sismember', orderKey,userId) == 1) then
    -- 已经下过单了,返回2
    return 2
end
-- 扣库存
redis.call('incrby',shopKey,-1)
-- 下单保存用户
redis.call('sadd', orderKey,userId)
-- 将数据保存在消息队列中
redis.call('xadd',streamKey,"*","shopId",shopId,"userId",userId)

return 0

        在之前判断是否符合下单的 Lua 脚本中加上往队列中添加数据。

        2)从消息队列中获取数据

        持续的从消息队列中尝试获取数据。当然,该方法在实战中交给线程池处理。

import cn.hutool.core.bean.BeanUtil;
import com.project.volunteermanagementproject.pojo.StreamObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.stream.*;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.util.List;
import java.util.Map;
@Component
@Slf4j
public class StreamUtil {

    private final StringRedisTemplate stringRedisTemplate;
    public StreamUtil(StringRedisTemplate stringRedisTemplate){
        this.stringRedisTemplate = stringRedisTemplate;
    }


    //实现发送消息
    public RecordId pubStream(StreamObject streamObject){
        Map<String, Object> map = BeanUtil.beanToMap(streamObject);
        return stringRedisTemplate.opsForStream().add("s1", map);
    }

    //实现从消息队列中获取消息
    public void getStream(){
        while (true){
            try {
                List<MapRecord<String, Object, Object>> read = stringRedisTemplate.opsForStream().read(
                        Consumer.from("g1", "c1"),
                        StreamReadOptions.empty().count(1).block(Duration.ofSeconds(2)),
                        StreamOffset.create("s1", ReadOffset.lastConsumed())
                );

                if (read == null || read.isEmpty()){
                    //如果获取失败,说明没有消息,继续下一次循环
                    continue;
                }
                //解析消息中的消息
                MapRecord<String, Object, Object> entries = read.get(0);
                Map<Object, Object> value = entries.getValue();
                StreamObject streamObject = BeanUtil.fillBeanWithMap(value, new StreamObject(), true);
                //这就拿到了消息队列中的数据了,就可以去使用该对象了
                log.info("成功从消息队列中获取到数据: "+streamObject);
                //这就需要确认消息队列
                stringRedisTemplate.opsForStream().acknowledge("s1", "g1", entries.getId());
            } catch (Exception e) {
                //如果在获取消息过程中出现异常,则需要再次执行该消息任务
                while (true){
                    try {
                        List<MapRecord<String, Object, Object>> read = stringRedisTemplate.opsForStream().read(
                                Consumer.from("g1", "c1"),
                                StreamReadOptions.empty().count(1),
                                StreamOffset.create("s1", ReadOffset.from("0"))
                        );
                        if (read == null || read.size() == 0){
                            break;
                        }
                        MapRecord<String, Object, Object> entries = read.get(0);
                        Map<Object, Object> value = entries.getValue();
                        StreamObject streamObject = BeanUtil.fillBeanWithMap(value, new StreamObject(), true);
                        //重新拿到未确认的数据
                        log.info("再次成功拿到数据: "+streamObject);
                        //再次进行消息确认
                        Long acknowledge = stringRedisTemplate.opsForStream().acknowledge("s1", "g1", entries.getId());
                    } catch (Exception ex) {
                        throw new RuntimeException(ex);
                    }

                }
            }

        }
    }
}

 

        3)手动模拟往消息队列中添加消息进行下单,通过读取消息队列中的方法进行监听:

    @Test
    void text2(){
        //持续接收消息
        StreamUtil streamUtil = new StreamUtil(stringRedisTemplate);
        streamUtil.getStream();
        
    }

发送的消息:

接收的消息:

        2.0 使用 Redis 实现点赞功能

        实现点赞思路:每一次点赞,都往数据库中修改一次数据+1,但是在日常的社交软件中,都不会有无限点赞的效果,比如说朋友圈,第一次点赞成功,再点一次则取消点赞。因此,需要解决的是一个人只能点赞一次,或者取消一次点赞。

        如何判断当前用户是否已经点赞呢?

        当然方法有非常多种,这里介绍的是使用 Redis 解决该方法。

        具体思路:使用 Redis 中的 set 集合数据结构,利用 set 的不可重复特性。在点击点赞之前,通过判断集合中之前是否存在该用户 id ,如果存在,则再点击一下为取消点赞;如果之前不存在,则点击一下为点赞成功,之后将该用户 id 放入集合中,方便下一次判断是否点赞。

代码实现:

import cn.hutool.core.util.BooleanUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

@Component
public class ThumbsUpUtil {

    @Autowired
    StringRedisTemplate stringRedisTemplate;
    //当前用户id
    private final Integer currentUserId = 1;
    public Boolean thumbs(Integer userId){
        String key = "userId:"+userId;
        //判断当前用户是否已经点赞了
        Boolean member = stringRedisTemplate.opsForSet().isMember(key, currentUserId.toString());
        if (BooleanUtil.isTrue(member)) {
            //接着之后就可以从数据库中进行更新了

            //当数据库更新成功之后,才判断:如果已经点赞了,则再点一下的结果是取消点赞,将该 currentUserId 从集合中移除
            stringRedisTemplate.opsForSet().remove(key,currentUserId.toString());

            return false;
        }

        //同理,接着之后就可以从数据库中进行更新了

        //当数据库更新成功之后,才判断:如果之前没有点赞,则点一下的结果是点赞成功,将该 currentUserId 添加到集合中
        stringRedisTemplate.opsForSet().add(key,currentUserId.toString());

        return true;
    }
}

        2.1 使用 Redis 实现点赞排行榜功能

        在点赞完之后,按照点赞时间先后排序,返回 Top5 的用户,如果单单只靠 Redis 中的 set 数据结构可以实现吗?

        很显然是不能实现的,因为 set 不具备排序功能。而 SortedSet 可以根据 score 值进行排序。因此,我们将 score 值设置为时间戳,根据时间戳来进行排序,选取前 5 名点赞用户。

        那么就需要将之前点赞的代码进行修改,将 set 更换成 SortedSet 。

代码实现:

        1)重新使用 SortedSet 实现点赞功能:

    @Autowired
    StringRedisTemplate stringRedisTemplate;
    //当前用户id

    public Boolean thumbs(Integer userId,Integer currentUserId){
        String key = "userId:"+userId;
        //判断当前用户是否已经点赞了
        Double score = stringRedisTemplate.opsForZSet().score(key, currentUserId.toString());
        if (score != null) {
            //接着之后就可以从数据库中进行更新了

            //当数据库更新成功之后,才判断:如果已经点赞了,则再点一下的结果是取消点赞,将该 currentUserId 从集合中移除
            stringRedisTemplate.opsForZSet().remove(key,currentUserId.toString());

            return false;
        }

        //同理,接着之后就可以从数据库中进行更新了

        //当数据库更新成功之后,才判断:如果之前没有点赞,则点一下的结果是点赞成功,将该 currentUserId 添加到集合中
        stringRedisTemplate.opsForZSet().add(key,currentUserId.toString(),System.currentTimeMillis());

        return true;
    }

        2)实现点赞排行榜:

    //根据时间来排序集合中的用户 id 前 5 名
    public void userRange(Integer userId){
        String key =  "userId:"+userId;
        Set<String> range = stringRedisTemplate.opsForZSet().range(key, 0, 4);
        //这就获取到点赞前5名的用户id,最后根据用户id来获取用户的其他消息
        log.info("从用户: "+userId+" 中获取到前5名粉丝ID:"+range);
    }

        3)代码测试:

        添加不同用户 ID 到集合中:

    @Autowired
    ThumbsUpUtil thumbsUpUtil;
    @Test
    void text3(){
        //模拟添加数据
        thumbsUpUtil.thumbs(1,2);
        thumbsUpUtil.thumbs(1,3);
        thumbsUpUtil.thumbs(1,4);
        thumbsUpUtil.thumbs(1,5);
        thumbsUpUtil.thumbs(1,6);
        thumbsUpUtil.thumbs(1,7);
    }

        Top5 排行榜:

    @Test
    void text4(){
        //获取用户ID为1的集合中的前五名用户
        thumbsUpUtil.userRange(1);
    }

运行结果:

        前 5 名的用户 ID 是根据时间戳来进行排序的,所以输出结果是没有问题的。

测试相同用户点赞多次:

    @Test
    void text5(){
        //现在用户2想继续对用户1点赞
        thumbsUpUtil.thumbs(1,2);
    }

运行结果:

        此时用户 ID 为 2 已经被移除了,这就是说明取消点赞了。

现在用户 1 中粉丝前 5 名的 ID 为:

        需要注意的是:当将根据用户 ID 从数据库中获取数据的时候,需要按照传进来的 ID 顺序来得到最终的结果。因为在根据用户 ID 进行批量查询的是用 in(用户 ID ) ,这样返回的结果会按照用户 ID 从大到小进行返回,因此,通过 ORDER BY FIELD(用户 ID 顺序) 命令,才会按照指定的用户 ID 顺序返回数据。

        3.0 使用 Redis 实现好友之间的共同关注(共同好友)

        用户之间的关注,可以直接用一张数据库表来进行关联,而对于用户与用户之间的共同用户该用什么的方法实现呢?

        可以使用 Redis 中的 set 数据结构来实现,利用 set 的不可重复和通过两个集合求得的交集,从而来获取共同的好友。因为不在乎用户顺序,所以不需要用到 SortedSet 数据结构。

代码如下:

准备了两个集合:

求该两个集合的交集:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Component
public class CommonUser {

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    public void getCommonUser(String key1,String key2){
        Set<String> common = stringRedisTemplate.opsForSet().intersect(key1, key2);
        if (common != null){
            List<Object> collect = common.stream().map(Long::valueOf).collect(Collectors.toList());
            System.out.println(collect);
        }
    }
}
    @Autowired
    CommonUser commonUser;
    @Test
    void text6(){
        String k1 = "userId:1";
        String k2 = "userId:2";
        commonUser.getCommonUser(k1,k2);
    }

运行结果:

        4.0 使用 Redis 实现投喂(发布文章)

        关注推送也叫做 Feed 流,直译为投喂。为用户持续的提供"沉浸式"的体验,通过无限下拉刷新获取新的信息。

        Feed 流有两种常见的模式:

        1)Timeline:不做内容筛选,简单的按照内容发布时间排序,常用与好友或关注。例如朋友圈。

        优点:信息全面,不会缺失。并且实现也相对简单。

        缺点:信息噪音较多,用户不一定感兴趣,内容获取效率低。

        该模式实现的方案有三种:拉模式、推模式、推拉结合。

        2)智能排序:利用智能算法屏蔽违规的、用户不感兴趣的内容。推送用户感兴趣信息来吸引用户。

        优点:投喂用户感兴趣,用户黏度很高,容易沉迷。

        缺点:如果算法不精确,可能起到反作用。

        4.1 使用 Redis 实现收件箱(收邮件)

        用户将文章推送给自己的粉丝,首先可以根据用户之间的关系表来查询用户的粉丝。获取到粉丝 ID 之后,循环将文章逐个推送给粉丝收件箱中,当前不是将整个文章推送到收件箱中,而是推送的是文章 ID ,文章则保存在数据库中,粉丝可以根据文章 ID 来查询文章内容。

        对于粉丝的收件箱可以用 Redis 来实现,由于根据发送的时间来将文章进行排列,所以使用 SortedSet 实现收件箱。key 设置为粉丝 ID ,每一个粉丝都会有一个收件箱;value 设置为文章 ID;score 设置为当前时间戳。

        只要是博主发送文章的时候,将文章保存到数据库中,且将文章 ID 发送出去即可。

代码实现:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class InboxUtil {

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    //实现发布文章的时候,将文章推送给好友
    //前缀默认已经实现了:获取到了当前用户的全部好友、将文章已经保存在数据库中
    public void send(Integer articleId, List<Integer> listId,Integer userId){
        String key = "feed:";
        //将文章ID推送到好友收件箱中
        for (Integer id : listId) {
            stringRedisTemplate.opsForZSet().add(key+id, articleId.toString(),System.currentTimeMillis());
        }
    }
}

 

模拟发布文章,且将文章 ID 推送给好友的收件箱:

代码如下:

    @Autowired
    InboxUtil inboxUtil;
    //实现将文章ID推送给好友
    @Test
    void text7(){
        List<Integer> list = new ArrayList<>(10);
        for (int i = 1; i <= 10; i++) {
            list.add(i);
        }
        Integer articleId = 100;
        inboxUtil.send(articleId,list);

    }

运行结果:

        现在用户 1 - 10 收件箱中都有一篇文件的 ID 。

        4.1.1 实现滚动分页查询(查询朋友圈信息)

        用户查询自己的收件箱,比如像朋友圈,按照时间来排序好友发布的信息,而查看好友的朋友圈的时候,从上至下,来查看好友的消息。

实现思路:

        从用户集合中查询信息也不难,在实现的时候不就是直接去查看当前用户的收件箱,其实不然,需要考虑的细节还挺多的。

        查看消息的时候,按照时间来查看,从最新的时间到最旧的时间的顺序来进行查看,比如说,朋友圈,打开朋友圈最先看到的是最新的好友信息,

        因此使用一下命令来进行查看:

ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]

        1)max:最大值,要区分第一次查询和其他查询:第一次查询的时候 max 最大值为当前时间戳;其他几次查询的时候 max 最大值为上一次的时间戳最小值。

        2)min:最小值,不需要区分第一次查询和其他查询,可以将其设置为 0 ,将 min 固定为 0 即可。

        3)offset:偏移量,需要区分第一次查询和其他查询:第一次查询 offset 设置为 0,偏移量为 0 时,表示当前时间戳可以大于等于此次要查询的时间戳,所以就可以拿到文章 ID ;其他查询 offset 设置为 1 ,一般来说没有问题,但是考虑到特殊情况,万一查询到的在相同的时间戳内发布了多篇文章呢?因此,在其他查询 offset 设置为最小时间戳有多少个。

        4)count:每次查询多少个 ID ,一般根据业务制定。

代码实现:

import com.project.volunteermanagementproject.pojo.ScrollQueryDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;

import java.util.*;

@Component
@Slf4j
public class ScrollQuery {

    @Autowired
    StringRedisTemplate stringRedisTemplate;
    //读取收件箱
    public ScrollQueryDTO read(int id,long max,long offset){
        //用户ID的收件箱
        String key = "feed:"+ id;
        Set<ZSetOperations.TypedTuple<String>> tuples = stringRedisTemplate.opsForZSet().reverseRangeByScoreWithScores(key, 0, max, offset, 3);

        if (tuples == null || tuples.isEmpty()){
            return null;
        }
        List<Integer> articleIds = new ArrayList<>(tuples.size());
        long time = 0;
        long count = 1;
        for (ZSetOperations.TypedTuple<String> tuple : tuples) {
            //获取文章id

            articleIds.add(Integer.valueOf(tuple.getValue()));
            //获取时间戳
            long l = tuple.getScore().longValue();

            if (time == l){
                count++;
            }else {
                time = l;
                count = 1;
            }
        }
        log.info("当前用户查看的文章ID为:"+articleIds);
        log.info("下次的最大时间戳为:"+time+","+"下次的偏移量为:"+count);
        return new ScrollQueryDTO(time, count);
    }
}

测试代码:

    @Autowired
    ScrollQuery scrollQuery;
    @Test
    void text8() throws InterruptedException {
        //当第一次查询的时候,max设置为当前时间戳,而偏移量为0
        int id = 1;
        long max= System.currentTimeMillis();
        long offset = 0;
        while (true){
            ScrollQueryDTO read = scrollQuery.read(id, max, offset);
            if (read == null){
                return;
            }
            max = read.getMax();
            offset = read.getCount();
            Thread.sleep(1000);
        }
    }

运行结果:

        看到文章 ID 的顺序是正确的,所以功能实现成功了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2126554.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Unity Hub自动安装指定版本Unity的Android开发环境

Unity开发Android环境要求SDK、DNK、JDK、Gradle版本都要对才能发布APK&#xff0c;自己去配置很容易出错。Unity Hub可以自动安装指定版本Unity的Android开发环境。 1.安装国内用的UnityHub&#xff08;我这里用的3.3.2-c6&#xff09; 2.找到对应的Unity版本 3.点击【从Unit…

docker管理redis集群

1.拉取redis镜像 docker pull redis拉取完成 [rootlocalhost ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE redis latest a617c1c92774 3 years ago 105MB2.运行redis容器 docker run -itd --name redis-test01 -p 6379:6379…

谈谈PCIe VID、DID、SSID、SSVID背后的智慧

PCIe Vendor ID 想了半天还是觉得从“ID是什么”这个问题开始比较好。那么ID是什么&#xff1f;ID就是身份。那身份又是什么&#xff1f;身份就是一个合理存在&#xff0c;用于区分不同个体。为什么叫“合理存在”呢&#xff1f;如果国家不给你发身份证&#xff0c;你就是黑户…

记一次导入dbf文件后数据为空问题的解决方法

前言 省流&#xff1a;这篇文章最终采用的是更换导出文件格式的方法&#xff0c;看到这里觉得方法不适用的小伙伴可以不用浪费几秒钟看完这篇文章哦。 问题描述 作者使用的是Navicat数据库管理工具&#xff0c;然后在将源数据库的数据表导出为dbf格式文件后&#xff0c;再将…

F110批量付款如何Debug BTE增强(后台JOB的调试方法)

F110批量付款如何Debug BTE增强&#xff08;后台JOB的调试方法&#xff09; SAP系统中的F110&#xff08;Automatic Payment&#xff09;是一个常用的付款程序&#xff0c;在实施过程中&#xff0c;也经常会遇到一些运行的错误&#xff0c;而对于此类的错误&#xff0c;通常的…

基于SpringBoot的租房网站系统

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot框架 工具&#xff1a;ECLIPSE 系统展示 首页 管理员功能界面 用户信息界面 预约看房界…

动手学深度学习(pytorch)学习记录28-使用块的网络(VGG)[学习记录]

目录 VGG块VGG网络训练模型 VGG块 定义了一个名为vgg_block的函数来实现一个VGG块 import torch from torch import nn from d2l import torch as d2ldef vgg_block(num_convs, in_channels, out_channels):layers []for _ in range(num_convs):layers.append(nn.Conv2d(in_…

线程池原理及改造

目录 一 线程池执行原理 二 线程池改造(一) 三 线程池改造(二) 一 线程池执行原理 首先我们先了解一下线程池里面几个参数&#xff1a; 第一个是核心线程数&#xff0c;第二个是线程池最大线程数。&#xff08;线程池里面的线程分为核心线程和非核心线程&#xff0c;既然核心…

人物化身持有者每月奖励:九月版

世界急需英雄。你准备好响应号召了吗&#xff1f; 穿上你的斗篷&#xff0c;戴上你的面具&#xff0c;用你的风格保卫 The Sandbox 的街道吧&#xff01;本月为人物化身持有者准备的独家奖励是 The Sandbox 超级套装&#xff01; 本月我们将首次向我们生态系统中的所有人物化…

Vulnhub-RickdiculouslyEasy靶机攻略

御剑扫描到ip 一.第一个flag 主机扫描 目录扫描 二.网页信息收集-第二个flag 9090也开放了web服务所以我们在IP地址后面加端口试试&#xff0c;如下图&#xff0c;加上了端口&#xff0c;并且发现了第二个flag&#xff0c;也对第二个flag进行了简单的探索也没有发现什么可以…

Kubernetes上安装Metallb和Ingress并部署应用程序

视频和代码仓库 视频教程地址&#xff1a;https://www.bilibili.com/video/BV1QV4rebEb8 代码仓库地址&#xff1a;https://github.com/xiaohh-me/kubernetes-yaml 网络规划 之前已经写了几篇安装Kubernetes文章&#xff0c;这次来讲讲在Kubernetes上安装Ingress&#xff0c…

【Linux】Linux介绍及CentOS虚拟机环境搭建

内容大纲介绍 文章目录 内容大纲介绍1.计算机简介2.Linux系统介绍3.虚拟化软件介绍4.Linux环境搭建5.扩展_虚拟机的快照6.Linux的目录介绍 1.计算机简介 概述 全称叫电子计算机, 英文名叫Computer, 俗称叫: 电脑, 简称叫: PC, 就是有硬件和软件组成的电子设备. 组成 计算机硬件…

IO复用-epoll基础

文章目录 IO复用认识epollepoll原理重要结构体 epoll的ET、LTepoll高效的原因epoll需要解决的问题对于使用epoll的建议 IO复用 IO等待拷贝 IO复用是一种通过减少等待时间&#xff0c;来提高IO效率的方式。 其原理是通过同时管理多个IO接口&#xff08;文件描述符&#xff09;…

VS2022搭建Linux开发环境

一、VS2022按钮Linux开发组件 双击启动后 选择Linux开发组件 点击修改&#xff0c;开始按钮Linux组件 二、创建新项目 三、远程连接Linux系统 选择工具&#xff0c;点击选项 选择跨平台中的连接管理器&#xff0c;点击添加 配置信息&#xff0c;输入你需要连接到的Linux…

《论面向服务架构设计及其应用》写作框架,软考高级系统架构设计师

论文真题 面向服务架构(Service-Oriented Architecture, SOA) 是一种应用框架,将日常的业务应用划分为单独的业务功能服务和流程,通过采用良好定义的接口和标准协议将这些服务关联起来。通过实施基于SOA的系统架构,用户可以构建、部署和整合服务,无需依赖应用程序及其运…

PyCharm的安装步骤

如何在本机上下载和安装PyCharm&#xff0c;请看以下讲解&#xff1b; 目录 一、下载 二、安装 第 1 步&#xff1a; 第 2 步&#xff1a; 第 3 步&#xff1a; 第 4 步&#xff1a; 第 5 步&#xff1a; 三、配置 第 1 步&#xff1a; 第 2 步&#xff1a; 第 3 步…

基于SpringBoot+Vue+MySQL的实训管理系统

系统展示 用户前台界面 管理员后台界面 系统背景 在当今信息化高速发展的时代&#xff0c;实训管理系统的建设对于提升教育机构的教学效率、优化资源配置、增强师生互动具有重要意义。本系统基于SpringBoot框架构建后端服务&#xff0c;利用其高效、简洁的特点&#xff0c;快速…

U盘文件及文件夹带锁修复

磁盘管理修复工具Disks磁盘管理–针对U盘文件及文件夹带锁修复 本文章只针对统信系统 文章目录 功能概述一、安装工具二、数据备份三、检查文件系统1. 通过启动栏中的“磁盘”或者桌面的“磁盘”启动文件来启动应用:2. 选择U盘设备3. 点击“检查文件系统”按钮(如果无此按钮…

【计算机毕设-大数据方向】基于大数据的健康美食推荐系统设计与实现

&#x1f497;博主介绍&#xff1a;✌全平台粉丝5W,高级大厂开发程序员&#x1f603;&#xff0c;博客之星、掘金/知乎/华为云/阿里云等平台优质作者。 【源码获取】关注并且私信我 【联系方式】&#x1f447;&#x1f447;&#x1f447;最下边&#x1f447;&#x1f447;&…

存储卡照片丢失:高效恢复策略与实战指南

一、引言&#xff1a;存储卡照片丢失的烦恼 在数字化时代&#xff0c;存储卡作为便携的数据存储介质&#xff0c;广泛应用于数码相机、智能手机、无人机等各类电子设备中&#xff0c;成为我们记录生活点滴、捕捉美好瞬间的重要工具。然而&#xff0c;随着使用频率的增加&#…