提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 1. 退出登录
    • 1.1 后端
    • 1.2 前端
  • 2. 获取当前用户信息
  • 3. C端用户竞赛列表功能
    • 3.1 后端
    • 3.2 Jmeter-基本操作
    • 3.3 数据版本性能测试-压力测试
    • 3.4 redis版本-缓存结构设计
    • 3.5 redis版本代码开发
    • 3.6 redis版本性能测试
  • 总结


前言

1. 退出登录

1.1 后端

后端直接拷贝代码就可以了

但是我们点击了退出登录,用户还是可以查看竞赛和题目列表的,但是不能答题,怎么实现这种可以一些操作的功能呢–》网关—》配置白名单之类的就可以了

1.2 前端

先是home.vue

<template><div class="oj-main-layout"><div class="oj-main-layout-header"><div class="oj-main-layout-nav"><Navbar></Navbar></div></div><div ><img src="@/assets/images/log-banner.png" class="banner-img"></div></div><RouterView />
</template><script setup>
import Navbar from '@/components/Navbar.vue'
</script><style lang="scss" scoped>
.el-main {padding: 0;
}.oj-main-layout {// background-color: #f7f7f7;padding-top: 20px;.banner-img {max-width: 1520px;margin: 0 auto;border-radius: 16px;width: "100%"}.oj-main-layout-header {height: 60px;position: absolute;width: 100%;background: #fff;left: 0;top: 0;z-index: 3;overflow: hidden;}.oj-main-layout-nav {max-width: 1520px;min-width: 100%;margin: 0 auto;height: 60px;background: #fff;}// banner 图.oj-ship-banner {max-width: 1520px;min-width: 1520;margin: 0 auto;width: 100%;height: 100%;height: 350px;// width: 1677px;color: #ffffff;background: url("@/assets/index_bg.png") left top no-repeat;background-size: cover;overflow: hidden;}
}
</style>

然后是我们自定义的组件Navbar
放在components

<template><div class="oj-navbar"><div class="oj-navbar-menus"><img class="oj-navbar-logo" src="@/assets/logo.png" /><el-menu router class="oj-navbar-menu" mode="horizontal"><el-menu-item index="/c-oj/home/question">题库</el-menu-item><el-menu-item index="/c-oj/home/exam">竞赛</el-menu-item></el-menu></div><div class="oj-navbar-users"><img v-if="isLogin" class="oj-message" @click="goMessage" src="@/assets/message/message.png" /><el-dropdown v-if="isLogin"><div class="oj-navbar-name"><img class="oj-head-image" v-if="isLogin" :src="userInfo.headImage" /><span>{{ userInfo.nickName }}</span></div><template #dropdown><el-dropdown-menu><el-dropdown-item @click="goUserDetail"><div class="oj-navabar-item"><span>个人中心</span></div></el-dropdown-item><el-dropdown-item @click="goMyExam"><div class="oj-navabar-item"><span>我的竞赛</span></div></el-dropdown-item><el-dropdown-item><div class="oj-navabar-item"><span @click="handleLogout">退出登录</span></div></el-dropdown-item></el-dropdown-menu></template></el-dropdown><span class="oj-navbar-login-btn" v-if="!isLogin" @click="goLogin">登录</span></div></div>
</template><script setup>
import { reactive, ref } from 'vue';
import router from '@/router';
import { getToken, removeToken } from '@/utils/cookie';const isLogin = ref(false)
const userInfo = reactive({nickName: '',headImage: ''
})</script><style lang="scss" scoped>
.oj-navbar {display: flex;justify-content: space-between;align-items: center;padding: 0 20px;box-sizing: border-box;max-width: 1520px;margin: 0 auto;.oj-navbar-menus {display: flex;align-items: center;height: 50px;.el-menu-item {font-family: PingFangSC, PingFang SC;font-weight: 400;font-size: 20px;color: #222222;line-height: 28px;text-align: center;width: 42px;text-align: left;margin-right: 25px;}}.oj-navbar-logo {width: 38px;height: 38px;background: #32C5FF;border-radius: 8px;cursor: pointer;object-fit: contain;margin-right: 59px;}.oj-navbar-menu {// margin-left: 18px;width: 600px;border: none;.el-menu-item {font-size: 16px;font-weight: 500;background-color: transparent !important;transition: none;border: none;line-height: 60px;}}.oj-navbar-users {display: flex;align-items: center;}.oj-navbar-login-btn {line-height: 60px;display: inline-block;font-family: PingFangSC, PingFang SC;font-weight: 400;font-size: 18px;color: #222222;text-align: center;cursor: pointer;.line {display: inline-block;width: 25px;}}.oj-message {cursor: pointer;margin-top: 15px;}.oj-head-image {width: 30px;height: 30px;border-radius: 30px;margin-right: 10px;}.oj-navbar-name {cursor: pointer;margin-top: 15px;font-weight: 400;color: #000;margin-left: 15px;font-size: 20px;width: 100px;display: flex;align-items: center;}.oj-navabar-item {display: flex;align-items: center;justify-content: center;padding: 0 32px;}
}
</style>

在这里插入图片描述
然后是配置登录按钮

function goLogin(){router.push("/c-oj/login")
}

登录成功以后还要修改登录状态了isLogin
我们先要判断用户状态,第一token存不存在,存在了也不能说明登录了,因为可能过期了,后端过期了,不会删除token,所以还要请求后端,判断token是否过期

        <div class="oj-navbar-name"><img class="oj-head-image" v-if="isLogin" src="@/assets/images/headImage.jpg" /><span>CK</span></div>

我们先把这个写死
这个也要请求后端,顺便就可以判断是否过期了
所以这个请求用户头像和昵称的接口就可以判断是否过期了

function checkToken(){if(getToken()){//判断是否过期,获取用户信息isLogin.value = true;}
}checkToken()

在这里插入图片描述
这样就成功了,获取头像和昵称的接口还没实现

最后是退出登录的逻辑实现了

export function logoutService() {return service({url: "/user/logout",method: "delete",});
}

header已经在请求拦截器中添加了

async function handleLogout(){await ElMessageBox.confirm('退出登录','温馨提示',{confirmButtonText: '确认',cancelButtonText: '取消',type: 'warning',})await logoutService();removeToken();isLogin.value=false;
}

2. 获取当前用户信息

这个还可以判断是否过期
在这里插入图片描述

在request.js里面,这个方法中的router.push(‘/c-oj/login’)不要了,没有登录就不要跳转到登录页面了

如果已经过期了–》网关就过不去

@Data
public class LoginUserVO {private String nickName;private String headImage;
}

这个是返回的用户数据,core中,那么这样的话,就要在登录的时候就把用户昵称存入redis

@Data
public class LoginUser {//存储在redis中的用户信息private Integer identity;private String nickName;private String headImage;
}
    public String createToken(Long userId, String secret,Integer identity,String nickName,String headImage){Map<String, Object> claims = new HashMap<>();String userKey = UUID.fastUUID().toString();claims.put(JwtConstants.LOGIN_USER_ID, userId);claims.put(JwtConstants.LOGIN_USER_KEY, userKey);String token = JwtUtils.createToken(claims, secret);LoginUser loginUser = new LoginUser();loginUser.setIdentity(identity);//2表示管理员,1表示普通用户loginUser.setNickName(nickName);loginUser.setHeadImage(headImage);redisService.setCacheObject(getTokenKey(userKey), loginUser, CacheConstants.EXPIRED, TimeUnit.MINUTES);return token;}

在这里插入图片描述

然后就是在登录的时候存入图片
在这里插入图片描述
这样就成功了

管理员那里的createToken传入null就可以了

然后就是前端了

export function infoService() {return service({url: "/user/info",method: "get",});
}
async function checkToken(){if(getToken()){//判断是否过期,获取用户信息const ret = await infoService()Object.assign(userInfo,ret.data)isLogin.value = true;}
}
        <div class="oj-navbar-name"><img class="oj-head-image" v-if="isLogin" :src="userInfo.headImage" /><span>{{ userInfo.nickName }}</span></div>

注意加了冒号的src是针对响应式数据的,没有加冒号的就是真对字符串进行处理了

这样以后就可以了

3. C端用户竞赛列表功能

3.1 后端

C端列表只展示已经发布的竞赛
C端列表功能可以不登录就使用了—》网关配置,m还有题目列表也不需要登录,配一个统一前缀表示不用登录就可以使用,比如semiLogin

在这里插入图片描述
在这里插入图片描述
未完赛指的是比赛还没有开始和已经开始的,历史竞赛指的是比赛已结束了
用一个type字段来区分,这也是一个过滤条件,type为0表示没有结束的竞赛,type为1表示已经结束的竞赛

开始写代码
先直接复制管理员的列表接口,然后在做一些修改

@Data
public class ExamQueryDTO extends PageQueryDTO {private String title;private String startTime;private String endTime;private Integer type;//0表示未结束的竞赛,1表示已经结束的竞赛
}
@Data
public class ExamVO {@JsonSerialize(using = ToStringSerializer.class)private Long examId;private String title;@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")private LocalDateTime startTime;@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")private LocalDateTime endTime;}
<select id="selectExamList" resultType="com.ck.friend.domain.exam.vo.ExamVO">SELECTte.exam_id,te.title,te.start_time,te.end_timeFROMtb_exam te<where>status = 1<if test="title !=null and title !='' ">AND te.title LIKE CONCAT('%',#{title},'%')</if><if test="startTime != null and startTime != '' ">AND te.start_time >= #{startTime}</if><if test="endTime != null and endTime != ''">AND te.end_time &lt;= #{endTime}</if><if test="type  ==  0">AND te.end_time > Now()</if><if test="type  ==  1">AND te.end_time &lt;= Now()</if></where>ORDER BYte.create_time DESC</select>
    @GetMapping("/semiLogin/list")public TableDataInfo list(ExamQueryDTO examQueryDTO){log.info("获取竞赛列表信息,examQueryDTO:{}", examQueryDTO);return getTableDataInfo(examService.list(examQueryDTO));}
security:ignore:whites: - /**/login- /friend/user/sendCode- /friend/user/loginOrRegister- /**/semiLogin/**

在这里插入图片描述
这样就成功了

在这里插入图片描述
记得还有拦截器也要配置一下

3.2 Jmeter-基本操作

Apache JMeter是Apache组织开发的基于Java的压⼒测试⼯具。⽤于对软件做压⼒测试,它最初被设计⽤于Web应⽤测试,但后来扩展到其他测试领域。
下载官网

一个linux来服务部署
一个linux服务器来组件部署,比如redis,mysql,nacos
一个linux服务器来部署jemeter

压缩之后,在bin目录下找到jmeter.bat文件,双击就可以运行了。就可以启动了
还有一个方法是打开cmd,输入jmeter就可以打开的
----》注意这样的话,就要添加环境变量的,就是添加bin目录就可以了
但是我们的jmeter是英文的,可以变为中文的
在bin目录下找到jmeter.properties
找到language配置,默认是en
改为language=zh_CN就可以了
在这里插入图片描述
在这里插入图片描述

点击f12,中的network中的XHR可以找到发送的后端请求
先添加一个线程组
在这里插入图片描述
然后在添加http请求
在这里插入图片描述
点击左上角的运行之后要保存才可以
但是我们直接运行看不到响应的数据,所以还要添加一个监听器
在这里插入图片描述
在这里插入图片描述
这样运行以后就可以看到结果那些了
在这里插入图片描述
这个就是10个请求,相当于10个用户同时发送
在这里插入图片描述
点击这个可以清楚结果树
在这里插入图片描述
这样就有10个了

3.3 数据版本性能测试-压力测试

在这里插入图片描述
这样写的意思就是有1000个用户在120s内不断地发送请求

在linux执行jmeter
在这里插入图片描述
4.7/s
表示每s处理4.7个请求JPS

第一行表示42s内处理了201个请求,然后每s4.7个
=就是上面累加的和,是请求数和时间的累加
+表示是不同的数据
最终表示每s只能处理5.9个请求
所以性能不好
Err:表示错误数,和错误数占总数的比例
怎么提高性能呢----》redis
redis可以提高很多的性能
这样也说明了mysql性能太低了
而C端肯定人很多的,所以我们一定要用redis提高速度
因为B端管理员太少了,所以不用管是否用redis提高速度
MySQL可以保证数据的准确性,redis保证速度
所以我们先从redis中查询,如果没有再去数据库,在同步到redis

3.4 redis版本-缓存结构设计

发布竞赛的时候可以存入redis,因为只有发布的竞赛才会展示给用户
取消发布的时候就从redis中移除

然后就是已经发布的竞赛不允许修改(在前端的按钮实现了),所以不用担心redis数据不准确

然后就是选择什么数据结构存储到redis中
—》结构是有序的,可以按照开赛时间来排序,而且支持分页

redis中有list这个数据结构–》支持分页查询

然后就未完赛和历史竞赛可以分开存储到不同redis中,要用两个list
key是什么—》exam:history:list和exam:time:list(未完赛)
然后就是还有一个list就是我报名的竞赛列表,然后就是我报名的竞赛列表一定是会和未完赛和历史竞赛重复一些的–》不好,重复次数太多了就不好了,所以就不要用list来存基本信息了,会重复的
—》一个竞赛基本信息存一份,这样就不会浪费了
value为String,json格式,key为exam:detail:examId

所以exam:history:list和exam:time:list(未完赛)存储examId
exam:detail:examId存储详细信息,这样就不会很浪费了

list存储examId的,一份基本信息存一份String

3.5 redis版本代码开发

注意发布的竞赛肯定是一个还没有开始,还没结束的竞赛

—》e:t:l,和e:d:exmaId

取消发布的竞赛也是一个还没有开始,还没结束的竞赛

查询redis数据的时候,第一次(发布上线的时候自己调用)从数据库中查询,然后存入redis,后面才是从redis中查询

现在system下面写代码
就是发布的时候加入redis,取消发布的时候从redis中删除
创建一个manager的包

@Component
public class ExamCacheManager {@Autowiredprivate RedisService redisService;public void addCache(Exam exam) {redisService.leftPushForList(getExamListKey(), exam.getExamId());redisService.setCacheObject(getDetailKey(exam.getExamId()), exam);}public void deleteCache(Long examId) {redisService.removeForList(getExamListKey(), examId);redisService.deleteObject(getDetailKey(examId));}private String getExamListKey() {return CacheConstants.EXAM_UNFINISHED_LIST;}private String getDetailKey(Long examId) {return CacheConstants.EXAM_DETAIL + examId;}}

leftPushForList往左边插入,这个就相当于是把早创建的放到前面了

    public final static String EXAM_UNFINISHED_LIST = "exam:time:list"; // 未完赛竞赛列表public final static String EXAM_HISTORY_LIST = "exam:history:list";  // 历史竞赛列表public final static String EXAM_DETAIL = "exam:detail:";    //竞赛详情信息

然后就是修改cancelPublish和publish的代码
把这两个方法添加进去就可以了
这两个就是对redis的增加和删除了
然后就是对redis的查询了

在这里插入图片描述
至于对redis的修改呢
我们规定
已经发布的,或者已经开赛的,已经存入redis的,不能修改exam相关数据
撤销发布的,没有存入redis的才可以修改exam,这样就没有问题了

在这里插入图片描述
然后就是redis的查询了

在friend中

@AllArgsConstructor
@Getter
public enum ExamListType {EXAM_UN_FINISH_LIST(0),EXAM_HISTORY_LIST(1);private final Integer value;}
    public <T> List<T> multiGet(final List<String> keyList, Class<T> clazz) {List list = redisTemplate.opsForValue().multiGet(keyList);if (list == null || list.size() <= 0) {return null;}List<T> result = new ArrayList<>();for (Object o : list) {result.add(JSON.parseObject(String.valueOf(o), clazz));}return result;}public <K, V> void multiSet(Map<? extends K, ? extends V> map) {redisTemplate.opsForValue().multiSet(map);}

在RedisService中插入这两个方法。这两个方法是批量处理的方法,就是对examId的list批量从redis中获取详细数据,或者批量设置数据,不然一个一个设置,效率还是太低了

还是创建一个manager的包

@Component
public class ExamCacheManager {@Autowiredprivate ExamMapper examMapper;@Autowiredprivate RedisService redisService;public Long getListSize(Integer examListType) {String examListKey = getExamListKey(examListType);return redisService.getListSize(examListKey);}public List<ExamVO> getExamVOList(ExamQueryDTO examQueryDTO) {int start = (examQueryDTO.getPageNum() - 1) * examQueryDTO.getPageSize();int end = start + examQueryDTO.getPageSize() - 1; //下标需要 -1String examListKey = getExamListKey(examQueryDTO.getType());List<Long> examIdList = redisService.getCacheListByRange(examListKey, start, end, Long.class);List<ExamVO> examVOList = assembleExamVOList(examIdList);if (CollectionUtil.isEmpty(examVOList)) {//说明redis中数据可能有问题 从数据库中查数据并且重新刷新缓存examVOList = getExamListByDB(examQueryDTO); //从数据库中获取数据refreshCache(examQueryDTO.getType());}return examVOList;}//刷新缓存逻辑public void refreshCache(Integer examListType) {List<Exam> examList = new ArrayList<>();if (ExamListType.EXAM_UN_FINISH_LIST.getValue().equals(examListType)) {//查询未完赛的竞赛列表examList = examMapper.selectList(new LambdaQueryWrapper<Exam>().select(Exam::getExamId, Exam::getTitle, Exam::getStartTime, Exam::getEndTime).gt(Exam::getEndTime, LocalDateTime.now()).eq(Exam::getStatus, Constants.TRUE).orderByDesc(Exam::getCreateTime));} else if (ExamListType.EXAM_HISTORY_LIST.getValue().equals(examListType)) {//查询历史竞赛examList = examMapper.selectList(new LambdaQueryWrapper<Exam>().select(Exam::getExamId, Exam::getTitle, Exam::getStartTime, Exam::getEndTime).le(Exam::getEndTime, LocalDateTime.now()).eq(Exam::getStatus, Constants.TRUE).orderByDesc(Exam::getCreateTime));}if (CollectionUtil.isEmpty(examList)) {return;}Map<String, Exam> examMap = new HashMap<>();List<Long> examIdList = new ArrayList<>();for (Exam exam : examList) {examMap.put(getDetailKey(exam.getExamId()), exam);examIdList.add(exam.getExamId());}redisService.multiSet(examMap);  //刷新详情缓存redisService.deleteObject(getExamListKey(examListType));redisService.rightPushAll(getExamListKey(examListType), examIdList);      //刷新列表缓存}private List<ExamVO> getExamListByDB(ExamQueryDTO examQueryDTO) {PageHelper.startPage(examQueryDTO.getPageNum(), examQueryDTO.getPageSize());return examMapper.selectExamList(examQueryDTO);}private List<ExamVO> assembleExamVOList(List<Long> examIdList) {if (CollectionUtil.isEmpty(examIdList)) {//说明redis当中没数据 从数据库中查数据并且重新刷新缓存return null;}//拼接redis当中key的方法 并且将拼接好的key存储到一个list中List<String> detailKeyList = new ArrayList<>();for (Long examId : examIdList) {detailKeyList.add(getDetailKey(examId));}List<ExamVO> examVOList = redisService.multiGet(detailKeyList, ExamVO.class);CollUtil.removeNull(examVOList);if (CollectionUtil.isEmpty(examVOList) || examVOList.size() != examIdList.size()) {//说明redis中数据有问题 从数据库中查数据并且重新刷新缓存return null;}return examVOList;}private String getExamListKey(Integer examListType) {if (ExamListType.EXAM_UN_FINISH_LIST.getValue().equals(examListType)) {return CacheConstants.EXAM_UNFINISHED_LIST;} else if (ExamListType.EXAM_HISTORY_LIST.getValue().equals(examListType)) {return CacheConstants.EXAM_HISTORY_LIST;}return null;}private String getDetailKey(Long examId) {return CacheConstants.EXAM_DETAIL + examId;}}

现在开始挨个讲一下这些方法的使用
getListSize是根据key查出对应的list的元素数量

getExamVOList是根据service传入的参数进行分页查询
其实就是根据页数和每页数量进行下标的查询罢了
中的assembleExamVOList是根据从redis查出的examId再从redis查出对应的exam详细数据
getExamListByDB中的CollUtil.removeNull(examVOList);就是移除数组中null的元素

refreshCache是刷新缓存,从数据库中查询exam列表数据,和详细数据
将详细数据直接multiSet,将列表数据,先deleteObject,在rightPushAll
是尾插
然后就可以写正式的代码了

    @GetMapping("/semiLogin/redis/list")public TableDataInfo redisList(ExamQueryDTO examQueryDTO){log.info("获取竞赛列表信息,examQueryDTO:{}", examQueryDTO);return examService.redisList(examQueryDTO);}
    @Overridepublic TableDataInfo redisList(ExamQueryDTO examQueryDTO) {Long listSize = examCacheManager.getListSize(examQueryDTO.getType());List<ExamVO> list;TableDataInfo tableDataInfo =new TableDataInfo();if(listSize==null||listSize==0){//说明缓存中没有数据,所以要先从数据库中获取数据,然后存入redislist = list(examQueryDTO);examCacheManager.refreshCache(examQueryDTO.getType());long total = new PageInfo<>(list).getTotal();return TableDataInfo.success(list, total);}else{//直接从redis中获取数据list = examCacheManager.getExamVOList(examQueryDTO);listSize = examCacheManager.getListSize(examQueryDTO.getType());return TableDataInfo.success(list, listSize);}}

这样就可以了
在这里插入图片描述
这样就可以了
然后就是竞赛慢慢就变为结束了的,怎么转移redis数据呢–》后面再说

3.6 redis版本性能测试

再次进行压力测试
我们在Windows下用jmeter进行测试,发现快得多有了redis之后,相比以前
然后在linux下的jmeter进行测试
在这里插入图片描述
发现速度直接提升了几十倍

总结

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

海滨浴场应急广播:守护碧海蓝天的安全防线

海滨浴场应急广播&#xff1a;守护碧海蓝天的安全防线&#xff01;海滨浴场&#xff0c;是人们休闲娱乐、亲近自然的理想场所。然而&#xff0c;变幻莫测的海洋环境也潜藏着诸多安全隐患&#xff0c;如溺水、离岸流、海蜇蜇伤、极端天气等。为了有效应对突发事件&#xff0c;保…

华曦达港股IPO观察丨以创新研发为笔,构建AI Home智慧生活新蓝图

深圳市华曦达科技股份有限公司自创立伊始&#xff0c;便将敏锐的市场洞察与前沿技术追踪视为生命线。通过构建一支卓越的研发团队&#xff0c;公司专注于自主核心技术的深耕与积累&#xff0c;以精密的硬件与创新的软件筑起坚实的技术壁垒。其精心打造的“技术创新&#xff0d;…

构建现代化的Web UI自动化测试框架:从图片上传测试实践说起

构建现代化的Web UI自动化测试框架&#xff1a;从图片上传测试实践说起如何设计一个可维护、可扩展的Web UI自动化测试框架&#xff1f;本文通过一个图片上传测试实例&#xff0c;详细介绍专业测试框架的搭建与实践。当前测试框架结构 首先&#xff0c;让我们了解一下当前的测试…

Apache IoTDB:大数据时代时序数据库选型的技术突围与实践指南

摘要&#xff1a;时序数据库在大数据时代迎来爆发式增长&#xff0c;IoTDB作为Apache顶级开源项目展现出显著优势&#xff1a;1. 性能卓越&#xff1a;支持千万级数据点/秒写入&#xff0c;18:1高压缩比&#xff0c;查询延迟低至500ms&#xff1b;2. 创新架构&#xff1a;采用树…

2025年8月16日(星期六):雨骑古莲村游记

清晨&#xff0c;当第一缕微光还未完全驱散夜幕的静谧&#xff0c;我们这群由校长领衔的骑行爱好者已整装待发。咖啡节早市尚未开摊&#xff0c;空气中弥漫着一种期待与宁静交织的氛围&#xff0c;仿佛连时间都在为我们即将开启的旅程而放慢脚步。今天的目标是古莲村&#xff0…

Pandas数据预处理中缺失值处理

一、缺失值的概念表现形式1.数据库中常用null表示2.部分编程语言中用NA表示3.可能表现为空字符串&#xff08;‘’&#xff09;或特定数值4.在Pandas中统一用NaN表示&#xff08;来自NumPy库&#xff0c;NaN、NAN、nan本质一致&#xff09;NaN的特性1.与任何值都不相等&#xf…

计算机网络:(十五)TCP拥塞控制与拥塞控制算法深度剖析

> 当网络变成"堵城",TCP如何化身智能交通指挥家?揭秘百万级并发背后的流量控制艺术! ### 一、生死攸关:为什么需要拥塞控制? **真实灾难案例**:1986年劳伦斯伯克利实验室网络大崩溃,因缺乏拥塞控制导致全网瘫痪36小时。TCP拥塞控制由此诞生,核心解决**资…

python中的单下划线“_”与双下划线“__”的使用场景及“左右双下划线”(魔术方法:`__xxx__`)

在Python中&#xff0c;单下划线“_”和双下划线“__”的使用场景和含义有显著区别&#xff0c;主要体现在命名约定和语法 一、单下划线“_”的使用场景 单下划线更多是编程约定&#xff08;而非强制语法&#xff09;&#xff0c;用于传递特定的“暗示”&#xff0c;不影响代码…

我们为什么需要时序数据库?

引言在当今数据驱动的世界中&#xff0c;时间序列数据正以前所未有的速度增长。从物联网设备传感器、金融交易记录到应用程序性能监控&#xff0c;时间序列数据无处不在。传统的关系型数据库在处理这类数据时往往力不从心&#xff0c;这时时序数据库(Time Series Database, TSD…

python-林粒粒的视频笔记1

python的方法和函数指什么 可变类型和不可变类型 不可变类型&#xff0c;比如字符串通过方法调用后&#xff0c;字符串本身的值不改变 要改变需要重新赋值才能进行改变 比如可变数据类型类型&#xff0c;调用方法后可以直接改变原列表 因此&#xff0c;可变数据类型需要再重新赋…

CentOS 7的下载与安装

一 、CentOS 7的下载与安装 注意&#xff1a; CentOS 7 已于2024年6月30日停止维护&#xff01; 1、下载 由于 centos 7 已经停止维护&#xff0c;部分镜像网站移除了对centos 7的支持&#xff0c;这里找到了部分现在还可以使用的镜像网站 阿里云开源镜像站&#xff1a;http…

矿物分类系统开发笔记(二):模型训练[删除空缺行]

目录 一、阶段衔接与开发目标 二、数据准备 三、模型选择与训练 1. 逻辑回归&#xff08;LR&#xff09; 2. 随机森林&#xff08;RF&#xff09; 3. 高斯朴素贝叶斯&#xff08;GNB&#xff09; 4. 支持向量机&#xff08;SVM&#xff09; 5. AdaBoost 6. XGBoost 四…

通信方式:命名管道

一、命名管道 1. 命名管道的原理 有了匿名管道&#xff0c;理解命名管道就非常简单了。 对于普通文件而言&#xff0c;两个进程打开同一个文件&#xff0c;OS是不会将文件加载两次的&#xff0c;这两个进程都会指向同一个文件&#xff0c;那么&#xff0c;也就享有同一份 in…

如何将数据库快速接入大模型实现智能问数,实现chatbi、dataagent,只需短短几步,不需要配置工作流!

智能问数系统初始化操作流程 一、系统初始化与管理员账号创建登录与初始化提示&#xff1a;首次访问系统登录页&#xff0c;若系统未初始化&#xff0c;会弹出 “系统未完成初始化&#xff0c;请初始化管理员账号” 提示&#xff0c;点击【去创建】。填写管理员信息&#xff1a…

告别手写文档!Spring Boot API 文档终极解决方案:SpringDoc OpenAPI

在前后端分离和微服务盛行的今天&#xff0c;API 文档是团队协作的“通用语言”。一份清晰、准确、实时同步的文档&#xff0c;能极大提升开发和联调效率。然而&#xff0c;手动编写和维护 API 文档&#xff08;如 Word、Markdown 或 Postman&#xff09;是一场永无止境的噩梦—…

N4200EX是一款全智能超声波检测仪产品简析

N4200EX是一款全智能超声波检测仪&#xff0c;适用于石油、石化、天然气、气体生产等行业的压力管路、阀门、设备的各种防爆场合气体泄漏、真空泄漏、阀门内漏检测。●本安防爆设计&#xff0c;防爆、防尘、防水、抗摔。●适应恶劣环境&#xff0c;可在-25℃超低温环境检测&…

NestJS @Inject 装饰器入门教程

一、核心概念解析 1.1 依赖注入&#xff08;DI&#xff09;的本质 依赖注入是一种设计模式&#xff0c;通过 IoC&#xff08;控制反转&#xff09;容器管理对象生命周期。在 NestJS 中&#xff0c;Injectable() 标记的类会被容器管理&#xff0c;而 Inject() 用于显式指定依赖项…

网络地址详解

子网划分详解&#xff1a;从 IP 地址结构到实际应用 在计算机网络中&#xff0c;子网划分是一项关键的技术&#xff0c;它能帮助我们更高效地管理 IP 地址资源&#xff0c;优化网络性能。要深入理解子网划分&#xff0c;首先需要从 IP 地址的基本结构说起。 一、IPv4 地址的基…

吾日三省吾身 | 周反思 8.19

上周一览总体来说&#xff0c;上个周是一个被项目驱使而险些丧失自主思考能力的危险阶段。相比任何有机械化工作经验的读者都有类似的体验&#xff0c;在手上打螺丝的无尽循环中&#xff0c;自己的脑子就会逐渐丧失对自身的感知以及自主思考的能力。而这个负循环一旦开始&#…

08.19总结

连通性 在无向图中&#xff0c;若任意两点间均存在路径相连&#xff0c;则该图称为连通图。 若删除图中任意一个顶点后&#xff0c;剩余图仍保持连通性&#xff0c;则该图为点双连通图。 若删除图中任意一条边后&#xff0c;图仍保持连通性&#xff0c;则该图为边双连通图。 在…